You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
6.1 KiB

  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. -module(rebar_prv_eunit).
  4. -behaviour(provider).
  5. -export([init/1,
  6. do/1,
  7. format_error/1]).
  8. -include("rebar.hrl").
  9. -define(PROVIDER, eunit).
  10. -define(DEPS, [compile]).
  11. %% ===================================================================
  12. %% Public API
  13. %% ===================================================================
  14. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  15. init(State) ->
  16. Provider = providers:create([{name, ?PROVIDER},
  17. {module, ?MODULE},
  18. {deps, ?DEPS},
  19. {bare, false},
  20. {example, "rebar eunit"},
  21. {short_desc, "Run EUnit Tests."},
  22. {desc, ""},
  23. {opts, eunit_opts(State)},
  24. {profiles, [test]}]),
  25. State1 = rebar_state:add_provider(State, Provider),
  26. State2 = rebar_state:add_to_profile(State1, test, test_state(State1)),
  27. {ok, State2}.
  28. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  29. do(State) ->
  30. ?INFO("Performing EUnit tests...", []),
  31. {Opts, _} = rebar_state:command_parsed_args(State),
  32. EUnitOpts = resolve_eunit_opts(State, Opts),
  33. TestApps = filter_checkouts(rebar_state:project_apps(State)),
  34. ok = compile_tests(State, TestApps),
  35. ok = maybe_cover_compile(State, Opts),
  36. AppsToTest = test_dirs(State, TestApps),
  37. Result = eunit:test(AppsToTest, EUnitOpts),
  38. ok = rebar_prv_cover:maybe_write_coverdata(State, ?PROVIDER),
  39. case handle_results(Result) of
  40. {error, Reason} ->
  41. {error, {?MODULE, Reason}};
  42. ok ->
  43. {ok, State}
  44. end.
  45. -spec format_error(any()) -> iolist().
  46. format_error(unknown_error) ->
  47. io_lib:format("Error running tests", []);
  48. format_error({error_running_tests, Reason}) ->
  49. io_lib:format("Error running tests: ~p", [Reason]).
  50. eunit_opts(_State) ->
  51. [{cover, $c, "cover", boolean, help(cover)},
  52. {verbose, $v, "verbose", boolean, help(verbose)}].
  53. help(cover) -> "Generate cover data";
  54. help(verbose) -> "Verbose output".
  55. filter_checkouts(Apps) -> filter_checkouts(Apps, []).
  56. filter_checkouts([], Acc) -> lists:reverse(Acc);
  57. filter_checkouts([App|Rest], Acc) ->
  58. AppDir = filename:absname(rebar_app_info:dir(App)),
  59. CheckoutsDir = filename:absname("_checkouts"),
  60. case lists:prefix(CheckoutsDir, AppDir) of
  61. true -> filter_checkouts(Rest, Acc);
  62. false -> filter_checkouts(Rest, [App|Acc])
  63. end.
  64. resolve_eunit_opts(State, Opts) ->
  65. EUnitOpts = rebar_state:get(State, eunit_opts, []),
  66. case proplists:get_value(verbose, Opts, false) of
  67. true -> set_verbose(EUnitOpts);
  68. false -> EUnitOpts
  69. end.
  70. test_dirs(State, TestApps) ->
  71. %% we need to add "./ebin" if it exists but only if it's not already
  72. %% due to be added
  73. F = fun(App) -> rebar_app_info:dir(App) =/= rebar_dir:get_cwd() end,
  74. BareEbin = filename:join([rebar_dir:base_dir(State), "ebin"]),
  75. case lists:any(F, TestApps) andalso filelib:is_dir(BareEbin) of
  76. false -> application_dirs(TestApps, []);
  77. true -> [{dir, BareEbin}|application_dirs(TestApps, [])]
  78. end.
  79. application_dirs([], Acc) -> lists:reverse(Acc);
  80. application_dirs([App|Rest], Acc) ->
  81. AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))),
  82. application_dirs(Rest, [{application, AppName}|Acc]).
  83. test_state(State) ->
  84. ErlOpts = rebar_state:get(State, eunit_compile_opts, []),
  85. TestOpts = safe_define_test_macro(ErlOpts),
  86. first_files(State) ++ [{erl_opts, TestOpts}].
  87. safe_define_test_macro(Opts) ->
  88. %% defining a compile macro twice results in an exception so
  89. %% make sure 'TEST' is only defined once
  90. case test_defined(Opts) of
  91. true -> Opts;
  92. false -> [{d, 'TEST'}] ++ Opts
  93. end.
  94. test_defined([{d, 'TEST'}|_]) -> true;
  95. test_defined([{d, 'TEST', true}|_]) -> true;
  96. test_defined([_|Rest]) -> test_defined(Rest);
  97. test_defined([]) -> false.
  98. first_files(State) ->
  99. EUnitFirst = rebar_state:get(State, eunit_first_files, []),
  100. [{erl_first_files, EUnitFirst}].
  101. set_verbose(Opts) ->
  102. %% if `verbose` is already set don't set it again
  103. case lists:member(verbose, Opts) of
  104. true -> Opts;
  105. false -> [verbose] ++ Opts
  106. end.
  107. compile_tests(State, TestApps) ->
  108. State1 = replace_src_dirs(State),
  109. F = fun(AppInfo) ->
  110. AppDir = rebar_app_info:dir(AppInfo),
  111. S = case rebar_app_info:state(AppInfo) of
  112. undefined ->
  113. C = rebar_config:consult(AppDir),
  114. rebar_state:new(State1, C, AppDir);
  115. AppState ->
  116. AppState
  117. end,
  118. ok = rebar_erlc_compiler:compile(S,
  119. ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
  120. ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)))
  121. end,
  122. lists:foreach(F, TestApps),
  123. compile_bare_tests(State1, TestApps).
  124. compile_bare_tests(State, TestApps) ->
  125. F = fun(App) -> rebar_app_info:dir(App) == rebar_dir:get_cwd() end,
  126. case lists:filter(F, TestApps) of
  127. %% compile just the `test` directory of the base dir
  128. [] -> rebar_erlc_compiler:compile(State,
  129. rebar_dir:get_cwd(),
  130. rebar_dir:base_dir(State));
  131. %% already compiled `./test` so do nothing
  132. _ -> ok
  133. end.
  134. replace_src_dirs(State) ->
  135. %% replace any `src_dirs` with just the `test` dir
  136. ErlOpts = rebar_state:get(State, erl_opts, []),
  137. StrippedOpts = lists:keydelete(src_dirs, 1, ErlOpts),
  138. rebar_state:set(State, erl_opts, [{src_dirs, ["test"]}|StrippedOpts]).
  139. maybe_cover_compile(State, Opts) ->
  140. State1 = case proplists:get_value(cover, Opts, false) of
  141. true -> rebar_state:set(State, cover_enabled, true);
  142. false -> State
  143. end,
  144. rebar_prv_cover:maybe_cover_compile(State1).
  145. handle_results(ok) -> ok;
  146. handle_results(error) ->
  147. {error, unknown_error};
  148. handle_results({error, Reason}) ->
  149. {error, {error_running_tests, Reason}}.