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

  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("finding tests in:~n\t{eunit_tests, ~p}.", [T]),
  71. ?DEBUG("with options:~n\t{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".