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.

551 lines
21 KiB

преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 10 години
преди 9 години
преди 6 години
преди 6 години
преди 6 години
преди 6 години
преди 6 години
преди 6 години
преди 6 години
преди 6 години
преди 6 години
преди 9 години
преди 9 години
преди 9 години
преди 6 години
преди 6 години
  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. %% exported solely for tests
  9. -export([prepare_tests/1, eunit_opts/1, validate_tests/2]).
  10. -include("rebar.hrl").
  11. -include_lib("providers/include/providers.hrl").
  12. -define(PROVIDER, eunit).
  13. %% we need to modify app_info state before compile
  14. -define(DEPS, [lock]).
  15. -define(DEFAULT_TEST_REGEX, "^(?!\\._).*\\.erl\$").
  16. %% ===================================================================
  17. %% Public API
  18. %% ===================================================================
  19. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  20. init(State) ->
  21. Provider = providers:create([{name, ?PROVIDER},
  22. {module, ?MODULE},
  23. {deps, ?DEPS},
  24. {bare, true},
  25. {example, "rebar3 eunit"},
  26. {short_desc, "Run EUnit Tests."},
  27. {desc, "Run EUnit Tests."},
  28. {opts, eunit_opts(State)},
  29. {profiles, [test]}]),
  30. State1 = rebar_state:add_provider(State, Provider),
  31. {ok, State1}.
  32. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  33. do(State) ->
  34. Tests = prepare_tests(State),
  35. %% inject `eunit_first_files`, `eunit_compile_opts` and any
  36. %% directories required by tests into the applications
  37. NewState = inject_eunit_state(State, Tests),
  38. case compile(NewState) of
  39. %% successfully compiled apps
  40. {ok, S} -> do(S, Tests);
  41. Error -> Error
  42. end.
  43. do(State, Tests) ->
  44. ?INFO("Performing EUnit tests...", []),
  45. setup_name(State),
  46. rebar_paths:set_paths([deps, plugins], State),
  47. %% Run eunit provider prehooks
  48. Providers = rebar_state:providers(State),
  49. Cwd = rebar_dir:get_cwd(),
  50. rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State),
  51. case validate_tests(State, Tests) of
  52. {ok, T} ->
  53. case run_tests(State, T) of
  54. {ok, State1} ->
  55. %% Run eunit provider posthooks
  56. rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State1),
  57. rebar_paths:set_paths([plugins, deps], State),
  58. {ok, State1};
  59. Error ->
  60. rebar_paths:set_paths([plugins, deps], State),
  61. Error
  62. end;
  63. Error ->
  64. rebar_paths:set_paths([plugins, deps], State),
  65. Error
  66. end.
  67. run_tests(State, Tests) ->
  68. T = translate_paths(State, Tests),
  69. EUnitOpts = resolve_eunit_opts(State),
  70. ?DEBUG("eunit_tests ~p", [T]),
  71. ?DEBUG("eunit_opts ~p", [EUnitOpts]),
  72. try eunit:test(T, EUnitOpts) of
  73. Result ->
  74. ok = maybe_write_coverdata(State),
  75. case handle_results(Result) of
  76. {error, Reason} ->
  77. ?PRV_ERROR(Reason);
  78. ok ->
  79. {ok, State}
  80. end
  81. catch error:badarg -> ?PRV_ERROR({error, badarg})
  82. end.
  83. -spec format_error(any()) -> iolist().
  84. format_error(unknown_error) ->
  85. io_lib:format("Error running tests", []);
  86. format_error({error_running_tests, Reason}) ->
  87. io_lib:format("Error running tests: ~p", [Reason]);
  88. format_error({eunit_test_errors, Errors}) ->
  89. io_lib:format(lists:concat(["Error Running EUnit Tests:"] ++
  90. lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []);
  91. format_error({badconfig, {Msg, {Value, Key}}}) ->
  92. io_lib:format(Msg, [Value, Key]);
  93. format_error({generator, Value}) ->
  94. io_lib:format("Generator ~p has an invalid format", [Value]);
  95. format_error({error, Error}) ->
  96. format_error({error_running_tests, Error}).
  97. %% ===================================================================
  98. %% Internal functions
  99. %% ===================================================================
  100. setup_name(State) ->
  101. {Long, Short, Opts} = rebar_dist_utils:find_options(State),
  102. rebar_dist_utils:either(Long, Short, Opts).
  103. prepare_tests(State) ->
  104. %% parse and translate command line tests
  105. CmdTests = resolve_tests(State),
  106. CfgTests = cfg_tests(State),
  107. ProjectApps = rebar_state:project_apps(State),
  108. %% prioritize tests to run first trying any command line specified
  109. %% tests falling back to tests specified in the config file finally
  110. %% running a default set if no other tests are present
  111. select_tests(State, ProjectApps, CmdTests, CfgTests).
  112. resolve_tests(State) ->
  113. {RawOpts, _} = rebar_state:command_parsed_args(State),
  114. Apps = resolve(app, application, RawOpts),
  115. Applications = resolve(application, RawOpts),
  116. Dirs = resolve(dir, RawOpts),
  117. Files = resolve(file, RawOpts),
  118. Modules = resolve(module, RawOpts),
  119. Suites = resolve(suite, module, RawOpts),
  120. Generator = resolve(generator, RawOpts),
  121. Apps ++ Applications ++ Dirs ++ Files ++ Modules ++ Suites ++ Generator.
  122. resolve(Flag, RawOpts) -> resolve(Flag, Flag, RawOpts).
  123. resolve(Flag, EUnitKey, RawOpts) ->
  124. case proplists:get_value(Flag, RawOpts) of
  125. undefined -> [];
  126. Args -> normalize(EUnitKey,
  127. rebar_string:lexemes(Args, [$,]))
  128. end.
  129. normalize(generator, Args) ->
  130. lists:flatmap(fun(Value) -> normalize_(generator, Value) end, Args);
  131. normalize(EUnitKey, Args) ->
  132. lists:map(fun(Arg) -> normalize_(EUnitKey, Arg) end, Args).
  133. normalize_(generator, Value) ->
  134. case string:tokens(Value, [$:]) of
  135. [Module0, Functions] ->
  136. Module = list_to_atom(Module0),
  137. lists:map(fun(F) -> {generator, Module, list_to_atom(F)} end,
  138. string:tokens(Functions, [$;]));
  139. _ ->
  140. ?PRV_ERROR({generator, Value})
  141. end;
  142. normalize_(Key, Value) when Key == dir; Key == file -> {Key, Value};
  143. normalize_(Key, Value) -> {Key, list_to_atom(Value)}.
  144. cfg_tests(State) ->
  145. case rebar_state:get(State, eunit_tests, []) of
  146. Tests when is_list(Tests) ->
  147. lists:map(fun({app, App}) -> {application, App}; (T) -> T end, Tests);
  148. Wrong ->
  149. %% probably a single non list term
  150. ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, eunit_tests}}})
  151. end.
  152. select_tests(_State, _ProjectApps, _, {error, _} = Error) -> Error;
  153. select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)};
  154. select_tests(_State, _ProjectApps, [], Tests) -> {ok, Tests};
  155. select_tests(_State, _ProjectApps, Tests, _) -> {ok, Tests}.
  156. default_tests(State, Apps) ->
  157. %% use `{application, App}` for each app in project
  158. AppTests = set_apps(Apps),
  159. %% additional test modules in `test` dir of each app
  160. ModTests = set_modules(Apps, State),
  161. AppTests ++ ModTests.
  162. set_apps(Apps) -> set_apps(Apps, []).
  163. set_apps([], Acc) -> Acc;
  164. set_apps([App|Rest], Acc) ->
  165. AppName = list_to_atom(binary_to_list(rebar_app_info:name(App))),
  166. set_apps(Rest, [{application, AppName}|Acc]).
  167. set_modules(Apps, State) -> set_modules(Apps, State, {[], []}).
  168. set_modules([], State, {AppAcc, TestAcc}) ->
  169. Regex = rebar_state:get(State, eunit_test_regex, ?DEFAULT_TEST_REGEX),
  170. BareTestDir = [filename:join([rebar_state:dir(State), "test"])],
  171. TestSrc = gather_src(BareTestDir, Regex),
  172. dedupe_tests({AppAcc, TestAcc ++ TestSrc});
  173. set_modules([App|Rest], State, {AppAcc, TestAcc}) ->
  174. F = fun(Dir) -> filename:join([rebar_app_info:dir(App), Dir]) end,
  175. AppDirs = lists:map(F, rebar_dir:src_dirs(rebar_app_info:opts(App), ["src"])),
  176. Regex = rebar_state:get(State, eunit_test_regex, ?DEFAULT_TEST_REGEX),
  177. AppSrc = gather_src(AppDirs, Regex),
  178. TestDirs = [filename:join([rebar_app_info:dir(App), "test"])],
  179. TestSrc = gather_src(TestDirs, Regex),
  180. set_modules(Rest, State, {AppSrc ++ AppAcc, TestSrc ++ TestAcc}).
  181. gather_src(Dirs, Regex) -> gather_src(Dirs, Regex, []).
  182. gather_src([], _Regex, Srcs) -> Srcs;
  183. gather_src([Dir|Rest], Regex, Srcs) ->
  184. gather_src(Rest, Regex, Srcs ++ rebar_utils:find_files(Dir, Regex, true)).
  185. dedupe_tests({AppMods, TestMods}) ->
  186. UniqueTestMods = lists:usort(TestMods) -- AppMods,
  187. %% for each modules in TestMods create a test if there is not a module
  188. %% in AppMods that will trigger it
  189. F = fun(TestMod) ->
  190. M = filename:rootname(filename:basename(TestMod)),
  191. MatchesTest = fun(AppMod) -> filename:rootname(filename:basename(AppMod)) ++ "_tests" == M end,
  192. case lists:any(MatchesTest, AppMods) of
  193. false -> {true, {module, list_to_atom(M)}};
  194. true -> false
  195. end
  196. end,
  197. rebar_utils:filtermap(F, UniqueTestMods).
  198. inject_eunit_state(State, {ok, Tests}) ->
  199. Apps = rebar_state:project_apps(State),
  200. case inject_eunit_state(State, Apps, []) of
  201. {ok, {NewState, ModdedApps}} ->
  202. test_dirs(NewState, ModdedApps, Tests);
  203. {error, _} = Error -> Error
  204. end;
  205. inject_eunit_state(_State, Error) -> Error.
  206. inject_eunit_state(State, [App|Rest], Acc) ->
  207. case inject(rebar_app_info:opts(App)) of
  208. {error, _} = Error -> Error;
  209. NewOpts ->
  210. NewApp = rebar_app_info:opts(App, NewOpts),
  211. inject_eunit_state(State, Rest, [NewApp|Acc])
  212. end;
  213. inject_eunit_state(State, [], Acc) ->
  214. case inject(rebar_state:opts(State)) of
  215. {error, _} = Error -> Error;
  216. NewOpts -> {ok, {rebar_state:opts(State, NewOpts), lists:reverse(Acc)}}
  217. end.
  218. opts(Opts, Key, Default) ->
  219. case rebar_opts:get(Opts, Key, Default) of
  220. Vs when is_list(Vs) -> Vs;
  221. Wrong ->
  222. ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, Key}}})
  223. end.
  224. inject(Opts) -> erl_opts(Opts).
  225. erl_opts(Opts) ->
  226. %% append `eunit_compile_opts` to app defined `erl_opts`
  227. ErlOpts = opts(Opts, erl_opts, []),
  228. EUnitOpts = opts(Opts, eunit_compile_opts, []),
  229. case append(EUnitOpts, ErlOpts) of
  230. {error, _} = Error -> Error;
  231. NewErlOpts -> first_files(rebar_opts:set(Opts, erl_opts, NewErlOpts))
  232. end.
  233. first_files(Opts) ->
  234. %% append `eunit_first_files` to app defined `erl_first_files`
  235. FirstFiles = opts(Opts, erl_first_files, []),
  236. EUnitFirstFiles = opts(Opts, eunit_first_files, []),
  237. case append(EUnitFirstFiles, FirstFiles) of
  238. {error, _} = Error -> Error;
  239. NewFirstFiles -> eunit_macro(rebar_opts:set(Opts, erl_first_files, NewFirstFiles))
  240. end.
  241. eunit_macro(Opts) ->
  242. ErlOpts = opts(Opts, erl_opts, []),
  243. NewOpts = safe_define_eunit_macro(ErlOpts),
  244. rebar_opts:set(Opts, erl_opts, NewOpts).
  245. safe_define_eunit_macro(Opts) ->
  246. %% defining a compile macro twice results in an exception so
  247. %% make sure 'EUNIT' is only defined once
  248. case test_defined(Opts) of
  249. true -> Opts;
  250. false -> [{d, 'EUNIT'}|Opts]
  251. end.
  252. test_defined([{d, 'EUNIT'}|_]) -> true;
  253. test_defined([{d, 'EUNIT', true}|_]) -> true;
  254. test_defined([_|Rest]) -> test_defined(Rest);
  255. test_defined([]) -> false.
  256. append({error, _} = Error, _) -> Error;
  257. append(_, {error, _} = Error) -> Error;
  258. append(A, B) -> A ++ B.
  259. test_dirs(State, Apps, []) -> rebar_state:project_apps(State, Apps);
  260. test_dirs(State, Apps, [{dir, Dir}|Rest]) ->
  261. %% insert `Dir` into an app if relative, or the base state if not
  262. %% app relative but relative to the root or not at all if outside
  263. %% project scope
  264. {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir),
  265. test_dirs(NewState, NewApps, Rest);
  266. test_dirs(State, Apps, [{file, File}|Rest]) ->
  267. Dir = filename:dirname(File),
  268. {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir),
  269. test_dirs(NewState, NewApps, Rest);
  270. test_dirs(State, Apps, [_|Rest]) -> test_dirs(State, Apps, Rest).
  271. maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) ->
  272. case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of
  273. {ok, Path} ->
  274. Opts = inject_test_dir(rebar_app_info:opts(App), Path),
  275. {State, AppAcc ++ [rebar_app_info:opts(App, Opts)] ++ Rest};
  276. {error, badparent} ->
  277. maybe_inject_test_dir(State, AppAcc ++ [App], Rest, Dir)
  278. end;
  279. maybe_inject_test_dir(State, AppAcc, [], Dir) ->
  280. case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of
  281. {ok, Path} ->
  282. Opts = inject_test_dir(rebar_state:opts(State), Path),
  283. {rebar_state:opts(State, Opts), AppAcc};
  284. {error, badparent} ->
  285. {State, AppAcc}
  286. end.
  287. inject_test_dir(Opts, Dir) ->
  288. %% append specified test targets to app defined `extra_src_dirs`
  289. ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []),
  290. rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]).
  291. compile({error, _} = Error) -> Error;
  292. compile(State) ->
  293. {ok, S} = rebar_prv_compile:do(State),
  294. ok = maybe_cover_compile(S),
  295. {ok, S}.
  296. validate_tests(State, {ok, Tests}) ->
  297. gather_tests(fun(Elem) -> validate(State, Elem) end, Tests, []);
  298. validate_tests(_State, Error) -> Error.
  299. gather_tests(_F, [], Acc) -> {ok, lists:reverse(Acc)};
  300. gather_tests(F, [Test|Rest], Acc) ->
  301. case F(Test) of
  302. ok -> gather_tests(F, Rest, [Test|Acc]);
  303. %% failure mode, switch to gathering errors
  304. {error, Error} -> gather_errors(F, Rest, [Error])
  305. end.
  306. gather_errors(_F, [], Acc) -> ?PRV_ERROR({eunit_test_errors, lists:reverse(Acc)});
  307. gather_errors(F, [Test|Rest], Acc) ->
  308. case F(Test) of
  309. ok -> gather_errors(F, Rest, Acc);
  310. {error, Error} -> gather_errors(F, Rest, [Error|Acc])
  311. end.
  312. validate(State, {application, App}) ->
  313. validate_app(State, App);
  314. validate(State, {dir, Dir}) ->
  315. validate_dir(State, Dir);
  316. validate(State, {file, File}) ->
  317. validate_file(State, File);
  318. validate(State, {module, Module}) ->
  319. validate_module(State, Module);
  320. validate(State, {suite, Module}) ->
  321. validate_module(State, Module);
  322. validate(State, {generator, Module, Function}) ->
  323. validate_generator(State, Module, Function);
  324. validate(State, Module) when is_atom(Module) ->
  325. validate_module(State, Module);
  326. validate(State, Path) when is_list(Path) ->
  327. case ec_file:is_dir(Path) of
  328. true -> validate(State, {dir, Path});
  329. false -> validate(State, {file, Path})
  330. end;
  331. %% unrecognized tests should be included. if they're invalid eunit will error
  332. %% and rebar.config may contain arbitrarily complex tests that are effectively
  333. %% unvalidatable
  334. validate(_State, _Test) -> ok.
  335. validate_app(State, AppName) ->
  336. ProjectApps = rebar_state:project_apps(State),
  337. validate_app(State, ProjectApps, AppName).
  338. validate_app(_State, [], AppName) ->
  339. {error, lists:concat(["Application `", AppName, "' not found in project."])};
  340. validate_app(State, [App|Rest], AppName) ->
  341. case AppName == binary_to_atom(rebar_app_info:name(App), unicode) of
  342. true -> ok;
  343. false -> validate_app(State, Rest, AppName)
  344. end.
  345. validate_dir(State, Dir) ->
  346. case ec_file:is_dir(filename:join([rebar_state:dir(State), Dir])) of
  347. true -> ok;
  348. false -> {error, lists:concat(["Directory `", Dir, "' not found."])}
  349. end.
  350. validate_file(State, File) ->
  351. case ec_file:exists(filename:join([rebar_state:dir(State), File])) of
  352. true -> ok;
  353. false -> {error, lists:concat(["File `", File, "' not found."])}
  354. end.
  355. validate_module(_State, Module) ->
  356. case code:which(Module) of
  357. non_existing -> {error, lists:concat(["Module `", Module, "' not found in project."])};
  358. _ -> ok
  359. end.
  360. validate_generator(State, Module, _Function) ->
  361. validate_module(State, Module).
  362. resolve_eunit_opts(State) ->
  363. {Opts, _} = rebar_state:command_parsed_args(State),
  364. EUnitOpts = rebar_state:get(State, eunit_opts, []),
  365. EUnitOpts1 = case proplists:get_value(verbose, Opts, false) of
  366. true -> set_verbose(EUnitOpts);
  367. false -> EUnitOpts
  368. end,
  369. EUnitOpts2 = case proplists:get_value(profile, Opts, false) of
  370. true -> set_profile(EUnitOpts1);
  371. false -> EUnitOpts1
  372. end,
  373. IsVerbose = lists:member(verbose, EUnitOpts2),
  374. case proplists:get_value(eunit_formatters, Opts, not IsVerbose) of
  375. true -> custom_eunit_formatters(EUnitOpts2);
  376. false -> EUnitOpts2
  377. end.
  378. custom_eunit_formatters(Opts) ->
  379. ReportOpts = custom_eunit_report_options(Opts),
  380. %% If `report` is already set then treat that like `eunit_formatters` is false
  381. case lists:keymember(report, 1, Opts) of
  382. true -> Opts;
  383. false -> [no_tty, {report, {eunit_progress, ReportOpts}} | Opts]
  384. end.
  385. custom_eunit_report_options(Opts) ->
  386. case lists:member(profile, Opts) of
  387. true -> [colored, profile];
  388. false -> [colored]
  389. end.
  390. set_profile(Opts) ->
  391. %% if `profile` is already set don't set it again
  392. case lists:member(profile, Opts) of
  393. true -> Opts;
  394. false -> [profile] ++ Opts
  395. end.
  396. set_verbose(Opts) ->
  397. %% if `verbose` is already set don't set it again
  398. case lists:member(verbose, Opts) of
  399. true -> Opts;
  400. false -> [verbose] ++ Opts
  401. end.
  402. translate_paths(State, Tests) -> translate_paths(State, Tests, []).
  403. translate_paths(_State, [], Acc) -> lists:reverse(Acc);
  404. translate_paths(State, [{K, _} = Path|Rest], Acc) when K == file; K == dir ->
  405. Apps = rebar_state:project_apps(State),
  406. translate_paths(State, Rest, [translate(State, Apps, Path)|Acc]);
  407. translate_paths(State, [Test|Rest], Acc) ->
  408. translate_paths(State, Rest, [Test|Acc]).
  409. translate(State, [App|Rest], {dir, Dir}) ->
  410. case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of
  411. {ok, Path} -> {dir, filename:join([rebar_app_info:out_dir(App), Path])};
  412. {error, badparent} -> translate(State, Rest, {dir, Dir})
  413. end;
  414. translate(State, [App|Rest], {file, FilePath}) ->
  415. Dir = filename:dirname(FilePath),
  416. File = filename:basename(FilePath),
  417. case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of
  418. {ok, Path} -> {file, filename:join([rebar_app_info:out_dir(App), Path, File])};
  419. {error, badparent} -> translate(State, Rest, {file, FilePath})
  420. end;
  421. translate(State, [], {dir, Dir}) ->
  422. case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of
  423. {ok, Path} -> {dir, filename:join([rebar_dir:base_dir(State), "extras", Path])};
  424. %% not relative, leave as is
  425. {error, badparent} -> {dir, Dir}
  426. end;
  427. translate(State, [], {file, FilePath}) ->
  428. Dir = filename:dirname(FilePath),
  429. File = filename:basename(FilePath),
  430. case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of
  431. {ok, Path} -> {file, filename:join([rebar_dir:base_dir(State), "extras", Path, File])};
  432. %% not relative, leave as is
  433. {error, badparent} -> {file, FilePath}
  434. end.
  435. maybe_cover_compile(State) ->
  436. {RawOpts, _} = rebar_state:command_parsed_args(State),
  437. State1 = case proplists:get_value(cover, RawOpts, false) of
  438. true -> rebar_state:set(State, cover_enabled, true);
  439. false -> State
  440. end,
  441. rebar_prv_cover:maybe_cover_compile(State1).
  442. maybe_write_coverdata(State) ->
  443. {RawOpts, _} = rebar_state:command_parsed_args(State),
  444. State1 = case proplists:get_value(cover, RawOpts, false) of
  445. true -> rebar_state:set(State, cover_enabled, true);
  446. false -> State
  447. end,
  448. Name = proplists:get_value(cover_export_name, RawOpts, ?PROVIDER),
  449. rebar_prv_cover:maybe_write_coverdata(State1, Name).
  450. handle_results(ok) -> ok;
  451. handle_results(error) ->
  452. {error, unknown_error};
  453. handle_results({error, Reason}) ->
  454. {error, {error_running_tests, Reason}}.
  455. eunit_opts(_State) ->
  456. [{app, undefined, "app", string, help(app)},
  457. {application, undefined, "application", string, help(app)},
  458. {cover, $c, "cover", boolean, help(cover)},
  459. {cover_export_name, undefined, "cover_export_name", string, help(cover_export_name)},
  460. {profile, $p, "profile", boolean, help(profile)},
  461. {dir, $d, "dir", string, help(dir)},
  462. {file, $f, "file", string, help(file)},
  463. {module, $m, "module", string, help(module)},
  464. {suite, $s, "suite", string, help(module)},
  465. {generator, $g, "generator", string, help(generator)},
  466. {verbose, $v, "verbose", boolean, help(verbose)},
  467. {name, undefined, "name", atom, help(name)},
  468. {sname, undefined, "sname", atom, help(sname)},
  469. {setcookie, undefined, "setcookie", atom, help(setcookie)}].
  470. help(app) -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`.";
  471. help(cover) -> "Generate cover data. Defaults to false.";
  472. help(cover_export_name) -> "Base name of the coverdata file to write";
  473. help(profile) -> "Show the slowest tests. Defaults to false.";
  474. help(dir) -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`.";
  475. help(file) -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`.";
  476. help(module) -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.";
  477. help(generator) -> "Comma separated list of generators (the format is `module:function`) to load tests from. Equivalent to `[{generator, Module, Function}]`.";
  478. help(verbose) -> "Verbose output. Defaults to false.";
  479. help(name) -> "Gives a long name to the node";
  480. help(sname) -> "Gives a short name to the node";
  481. help(setcookie) -> "Sets the cookie if the node is distributed".