您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

230 行
7.4 KiB

  1. %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. %% -------------------------------------------------------------------
  4. %%
  5. %% rebar: Erlang Build Tools
  6. %%
  7. %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
  8. %%
  9. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %% of this software and associated documentation files (the "Software"), to deal
  11. %% in the Software without restriction, including without limitation the rights
  12. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %% copies of the Software, and to permit persons to whom the Software is
  14. %% furnished to do so, subject to the following conditions:
  15. %%
  16. %% The above copyright notice and this permission notice shall be included in
  17. %% all copies or substantial portions of the Software.
  18. %%
  19. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %% THE SOFTWARE.
  26. %% -------------------------------------------------------------------
  27. %%
  28. %% Targets:
  29. %% test - runs common test suites in ./test
  30. %% int_test - runs suites in ./int_test
  31. %% perf_test - runs suites inm ./perf_test
  32. %%
  33. %% Global options:
  34. %% verbose=1 - show output from the common_test run as it goes
  35. %% suite="foo"" - runs <test>/foo_SUITE
  36. %% case="mycase" - runs individual test case foo_SUITE:mycase
  37. %% -------------------------------------------------------------------
  38. -module(rebar_ct).
  39. -export([ct/2]).
  40. -include("rebar.hrl").
  41. %% ===================================================================
  42. %% Public API
  43. %% ===================================================================
  44. ct(Config, File) ->
  45. run_test_if_present("test", Config, File).
  46. %% ===================================================================
  47. %% Internal functions
  48. %% ===================================================================
  49. run_test_if_present(TestDir, Config, File) ->
  50. case filelib:is_dir(TestDir) of
  51. false ->
  52. ?WARN("~s directory not present - skipping\n", [TestDir]),
  53. ok;
  54. true ->
  55. run_test(TestDir, Config, File)
  56. end.
  57. run_test(TestDir, Config, _File) ->
  58. {Cmd, RawLog} = make_cmd(TestDir, Config),
  59. clear_log(RawLog),
  60. case rebar_config:get_global(verbose, "0") of
  61. "0" ->
  62. Output = " >> " ++ RawLog ++ " 2>&1";
  63. _ ->
  64. Output = " 2>&1 | tee -a " ++ RawLog
  65. end,
  66. rebar_utils:sh(Cmd ++ Output, [{env,[{"TESTDIR", TestDir}]}]),
  67. check_log(RawLog).
  68. clear_log(RawLog) ->
  69. case filelib:ensure_dir("logs/index.html") of
  70. ok ->
  71. NowStr = rebar_utils:now_str(),
  72. LogHeader = "--- Test run on " ++ NowStr ++ " ---\n",
  73. ok = file:write_file(RawLog, LogHeader);
  74. {error, Reason} ->
  75. ?ERROR("Could not create log dir - ~p\n", [Reason]),
  76. ?FAIL
  77. end.
  78. %% calling ct with erl does not return non-zero on failure - have to check
  79. %% log results
  80. check_log(RawLog) ->
  81. {ok, Msg} =
  82. rebar_utils:sh("grep -e 'TEST COMPLETE' -e '{error,make_failed}' "
  83. ++ RawLog, [{use_stdout, false}]),
  84. MakeFailed = string:str(Msg, "{error,make_failed}") =/= 0,
  85. RunFailed = string:str(Msg, ", 0 failed") =:= 0,
  86. if
  87. MakeFailed ->
  88. show_log(RawLog),
  89. ?ERROR("Building tests failed\n",[]),
  90. ?FAIL;
  91. RunFailed ->
  92. show_log(RawLog),
  93. ?ERROR("One or more tests failed\n",[]),
  94. ?FAIL;
  95. true ->
  96. ?CONSOLE("DONE. ~s\n", [Msg])
  97. end.
  98. %% Show the log if it hasn't already been shown because verbose was on
  99. show_log(RawLog) ->
  100. ?CONSOLE("Showing log\n", []),
  101. case rebar_config:get_global(verbose, "0") of
  102. "0" ->
  103. {ok, Contents} = file:read_file(RawLog),
  104. ?CONSOLE("~s", [Contents]);
  105. _ ->
  106. ok
  107. end.
  108. make_cmd(TestDir, Config) ->
  109. Cwd = rebar_utils:get_cwd(),
  110. LogDir = filename:join(Cwd, "logs"),
  111. EbinDir = filename:absname(filename:join(Cwd, "ebin")),
  112. IncludeDir = filename:join(Cwd, "include"),
  113. case filelib:is_dir(IncludeDir) of
  114. true ->
  115. Include = " -I \"" ++ IncludeDir ++ "\"";
  116. false ->
  117. Include = ""
  118. end,
  119. %% Add the code path of the rebar process to the code path. This
  120. %% includes the dependencies in the code path. The directories
  121. %% that are part of the root Erlang install are filtered out to
  122. %% avoid duplication
  123. R = code:root_dir(),
  124. NonLibCodeDirs = [P || P <- code:get_path(), not lists:prefix(R, P)],
  125. CodeDirs = [io_lib:format("\"~s\"", [Dir]) ||
  126. Dir <- [EbinDir|NonLibCodeDirs]],
  127. CodePathString = string:join(CodeDirs, " "),
  128. Cmd = ?FMT("erl " % should we expand ERL_PATH?
  129. " -noshell -pa ~s ~s"
  130. " -s ct_run script_start -s erlang halt"
  131. " -name test@~s"
  132. " -logdir \"~s\""
  133. " -env TEST_DIR \"~s\"",
  134. [CodePathString,
  135. Include,
  136. net_adm:localhost(),
  137. LogDir,
  138. filename:join(Cwd, TestDir)]) ++
  139. get_cover_config(Config, Cwd) ++
  140. get_ct_config_file(TestDir) ++
  141. get_config_file(TestDir) ++
  142. get_suite(TestDir) ++
  143. get_case(),
  144. RawLog = filename:join(LogDir, "raw.log"),
  145. {Cmd, RawLog}.
  146. get_cover_config(Config, Cwd) ->
  147. case rebar_config:get_local(Config, cover_enabled, false) of
  148. false ->
  149. "";
  150. true ->
  151. case filelib:fold_files(Cwd, ".*cover\.spec\$", true, fun collect_ct_specs/2, []) of
  152. [] ->
  153. ?DEBUG("No cover spec found: ~s~n", [Cwd]),
  154. "";
  155. [Spec] ->
  156. ?DEBUG("Found cover file ~w~n", [Spec]),
  157. " -cover " ++ Spec;
  158. Specs ->
  159. ?ABORT("Multiple cover specs found: ~p~n", [Specs])
  160. end
  161. end.
  162. collect_ct_specs(F, Acc) ->
  163. %% Ignore any specs under the deps/ directory. Do this pulling the dirname off the
  164. %% the F and then splitting it into a list.
  165. Parts = filename:split(filename:dirname(F)),
  166. case lists:member("deps", Parts) of
  167. true ->
  168. Acc; % There is a directory named "deps" in path
  169. false ->
  170. [F | Acc] % No "deps" directory in path
  171. end.
  172. get_ct_config_file(TestDir) ->
  173. Config = filename:join(TestDir, "test.config"),
  174. case filelib:is_regular(Config) of
  175. false ->
  176. " ";
  177. true ->
  178. " -ct_config " ++ Config
  179. end.
  180. get_config_file(TestDir) ->
  181. Config = filename:join(TestDir, "app.config"),
  182. case filelib:is_regular(Config) of
  183. false ->
  184. " ";
  185. true ->
  186. " -config " ++ Config
  187. end.
  188. get_suite(TestDir) ->
  189. case rebar_config:get_global(suite, undefined) of
  190. undefined ->
  191. " -dir " ++ TestDir;
  192. Suite ->
  193. Filename = filename:join(TestDir, Suite ++ "_SUITE.erl"),
  194. case filelib:is_regular(Filename) of
  195. false ->
  196. ?ERROR("Suite ~s not found\n", [Suite]),
  197. ?FAIL;
  198. true ->
  199. " -suite " ++ Filename
  200. end
  201. end.
  202. get_case() ->
  203. case rebar_config:get_global('case', undefined) of
  204. undefined ->
  205. "";
  206. Case ->
  207. " -case " ++ Case
  208. end.