diff --git a/ebin/rebar.app b/ebin/rebar.app index bc7ecc8..7d98148 100644 --- a/ebin/rebar.app +++ b/ebin/rebar.app @@ -4,6 +4,7 @@ {modules, [ rebar_app_utils, rebar_config, rebar_core, + rebar_ct, rebar_erlc_compiler, rebar_file_utils, rebar_log, @@ -27,7 +28,8 @@ {app_modules, [ rebar_protobuffs_compiler, rebar_erlc_compiler, rebar_port_compiler, - rebar_otp_app ]}, + rebar_otp_app, + rebar_ct ]}, {rel_modules, [ rebar_reltool ]} ]} diff --git a/src/rebar_ct.erl b/src/rebar_ct.erl new file mode 100644 index 0000000..2cb2f8f --- /dev/null +++ b/src/rebar_ct.erl @@ -0,0 +1,196 @@ +%% ------------------------------------------------------------------- +%% +%% rebar: Erlang Build Tools +%% +%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) +%% +%% Permission is hereby granted, free of charge, to any person obtaining a copy +%% of this software and associated documentation files (the "Software"), to deal +%% in the Software without restriction, including without limitation the rights +%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +%% copies of the Software, and to permit persons to whom the Software is +%% furnished to do so, subject to the following conditions: +%% +%% The above copyright notice and this permission notice shall be included in +%% all copies or substantial portions of the Software. +%% +%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +%% THE SOFTWARE. +%% ------------------------------------------------------------------- +%% +%% Targets: +%% test - runs common test suites in ./test +%% int_test - runs suites in ./int_test +%% perf_test - runs suites inm ./perf_test +%% +%% Global options: +%% verbose=1 - show output from the common_test run as it goes +%% suite="foo"" - runs /foo_SUITE +%% case="mycase" - runs individual test case foo_SUITE:mycase +%% ------------------------------------------------------------------- +-module(rebar_ct). + +-export([test/2, + int_test/2, + perf_test/2]). + +-compile([export_all]). + +-include("rebar.hrl"). + +%% =================================================================== +%% Public API +%% =================================================================== + +test(Config, File) -> + run_test_if_present("test", Config, File). + +int_test(Config, File) -> + run_test_if_present("int_test", Config, File). + +perf_test(Config, File) -> + run_test_if_present("perf_test", Config, File). + +%% =================================================================== +%% Internal functions +%% =================================================================== +run_test_if_present(TestDir, Config, File) -> + case filelib:is_dir(TestDir) of + false -> + ?WARN("~s directory not present - skipping\n", [TestDir]), + ok; + true -> + run_test(TestDir, Config, File) + end. + +run_test(TestDir, Config, _File) -> + {Cmd, RawLog} = make_cmd(TestDir, Config), + clear_log(RawLog), + case rebar_config:get_global(verbose, "0") of + "0" -> + Output = " >> " ++ RawLog ++ " 2>&1"; + _ -> + Output = " 2>&1 | tee -a " ++ RawLog + end, + + case rebar_utils:sh(Cmd ++ Output, [{"TESTDIR", TestDir}]) of + ok -> + check_log(RawLog); + {error, _Rc} -> + show_log(RawLog), + ?ERROR("Executing tests failed.\n", []), + ?FAIL + end. + + +clear_log(RawLog) -> + case filelib:ensure_dir("logs/index.html") of + ok -> + NowStr = rebar_utils:now_str(), + LogHeader = "--- Test run on " ++ NowStr ++ " ---\n", + ok = file:write_file(RawLog, LogHeader); + {error, Reason} -> + ?ERROR("Could not create log dir - ~p\n", [Reason]), + ?FAIL + end. + +%% calling ct with erl does not return non-zero on failure - have to check +%% log results +check_log(RawLog) -> + Msg = os:cmd("grep 'TEST COMPLETE' " ++ RawLog), + case string:str(Msg, ", 0 failed") of + 0 -> + show_log(RawLog), + ?ERROR("One or more tests failed\n",[]), + ?FAIL; + _ -> + ?CONSOLE("DONE. ~s\n", [Msg]) + end. + +%% Show the log if it hasn't already been shown because verbose was on +show_log(RawLog) -> + ?CONSOLE("Showing log\n", []), + case rebar_config:get_global(verbose, "0") of + "0" -> + {ok, Contents} = file:read_file(RawLog), + ?CONSOLE("~s", [Contents]); + _ -> + ok + end. + +make_cmd(TestDir, Config) -> + {ok, Cwd} = file:get_cwd(), + LogDir = filename:join(Cwd, "logs"), + Ebin = filename:join(Cwd, "ebin"), + IncludeDir = filename:join(Cwd, "include"), + case filelib:is_dir(IncludeDir) of + true -> + Include = " -I \"" ++ IncludeDir ++ "\""; + false -> + Include = "" + end, + + Cmd = lists:flatten(io_lib:format("erl " % should we expand ERL_PATH? + " -noshell -pa \"~s\" ~s" + " -s ct_run script_start -s erlang halt" + " -name test@~s" + " -logdir \"~s\"" + " -env TEST_DIR \"~s\"", + [Ebin, + Include, + net_adm:localhost(), + LogDir, + filename:join(Cwd, TestDir)])) ++ + get_ct_config_file(TestDir) ++ + get_config_file(TestDir) ++ + get_suite(TestDir) ++ + get_case(), + RawLog = filename:join(LogDir, "raw.log"), + {Cmd, RawLog}. + + +get_ct_config_file(TestDir) -> + Config = filename:join(TestDir, "test.config"), + case filelib:is_regular(Config) of + false -> + " "; + true -> + " -ct_config " ++ Config + end. + +get_config_file(TestDir) -> + Config = filename:join(TestDir, "app.config"), + case filelib:is_regular(Config) of + false -> + " "; + true -> + " -config " ++ Config + end. + +get_suite(TestDir) -> + case rebar_config:get_global(suite, undefined) of + undefined -> + " -dir " ++ TestDir; + Suite -> + Filename = filename:join(TestDir, Suite ++ "_SUITE.erl"), + case filelib:is_regular(Filename) of + false -> + ?ERROR("Suite ~s not found\n", [Suite]), + ?FAIL; + true -> + " -suite " ++ Filename + end + end. + +get_case() -> + case rebar_config:get_global('case', undefined) of + undefined -> + ""; + Case -> + " -case " ++ Case + end.