mirror of
https://github.com/correl/rebar.git
synced 2024-11-23 19:19:54 +00:00
Add experimental tests= filter for eunit suites
This commit is contained in:
parent
563607bc02
commit
69dc9ec933
3 changed files with 263 additions and 27 deletions
|
@ -297,7 +297,9 @@ generate-upgrade previous_release=path Build an upgrade package
|
||||||
|
|
||||||
generate-appups previous_release=path Generate appup files
|
generate-appups previous_release=path Generate appup files
|
||||||
|
|
||||||
eunit [suites=foo] Run eunit [test/foo_tests.erl] tests
|
eunit [suites=foo] Run eunit tests [foo.erl and test/foo_tests.erl]
|
||||||
|
[suites=foo] [tests=bar] Run specific eunit tests [first test name starting
|
||||||
|
with 'bar' in foo.erl and test/foo_tests.erl]
|
||||||
ct [suites=] [case=] Run common_test suites
|
ct [suites=] [case=] Run common_test suites
|
||||||
|
|
||||||
qc Test QuickCheck properties
|
qc Test QuickCheck properties
|
||||||
|
|
|
@ -43,7 +43,14 @@
|
||||||
%% The following Global options are supported:
|
%% The following Global options are supported:
|
||||||
%% <ul>
|
%% <ul>
|
||||||
%% <li>verbose=1 - show extra output from the eunit test</li>
|
%% <li>verbose=1 - show extra output from the eunit test</li>
|
||||||
%% <li>suites="foo,bar" - runs test/foo_tests.erl and test/bar_tests.erl</li>
|
%% <li>
|
||||||
|
%% suites="foo,bar" - runs tests in foo.erl, test/foo_tests.erl and
|
||||||
|
%% tests in bar.erl, test/bar_tests.erl
|
||||||
|
%% </li>
|
||||||
|
%% <li>
|
||||||
|
%% suites="foo,bar" tests="baz"- runs first test with name starting
|
||||||
|
%% with 'baz' in foo.erl, test/foo_tests.erl and tests in bar.erl,
|
||||||
|
%% test/bar_tests.erl
|
||||||
%% </ul>
|
%% </ul>
|
||||||
%% Additionally, for projects that have separate folders for the core
|
%% Additionally, for projects that have separate folders for the core
|
||||||
%% implementation, and for the unit tests, then the following
|
%% implementation, and for the unit tests, then the following
|
||||||
|
@ -92,21 +99,7 @@ run_eunit(Config, CodePath, SrcErls) ->
|
||||||
%% cover and eunit testing. Normally you can just tell cover
|
%% cover and eunit testing. Normally you can just tell cover
|
||||||
%% and/or eunit to scan the directory for you, but eunit does a
|
%% and/or eunit to scan the directory for you, but eunit does a
|
||||||
%% code:purge in conjunction with that scan and causes any cover
|
%% code:purge in conjunction with that scan and causes any cover
|
||||||
%% compilation info to be lost. Filter out "*_tests" modules so
|
%% compilation info to be lost.
|
||||||
%% eunit won't doubly run them and so cover only calculates
|
|
||||||
%% coverage on production code. However, keep "*_tests" modules
|
|
||||||
%% that are not automatically included by eunit.
|
|
||||||
%%
|
|
||||||
%% From 'Primitives' in the EUnit User's Guide
|
|
||||||
%% http://www.erlang.org/doc/apps/eunit/chapter.html
|
|
||||||
%% "In addition, EUnit will also look for another module whose
|
|
||||||
%% name is ModuleName plus the suffix _tests, and if it exists,
|
|
||||||
%% all the tests from that module will also be added. (If
|
|
||||||
%% ModuleName already contains the suffix _tests, this is not
|
|
||||||
%% done.) E.g., the specification {module, mymodule} will run all
|
|
||||||
%% tests in the modules mymodule and mymodule_tests. Typically,
|
|
||||||
%% the _tests module should only contain test cases that use the
|
|
||||||
%% public interface of the main module (and no other code)."
|
|
||||||
|
|
||||||
AllBeamFiles = rebar_utils:beams(?EUNIT_DIR),
|
AllBeamFiles = rebar_utils:beams(?EUNIT_DIR),
|
||||||
{BeamFiles, TestBeamFiles} =
|
{BeamFiles, TestBeamFiles} =
|
||||||
|
@ -115,16 +108,20 @@ run_eunit(Config, CodePath, SrcErls) ->
|
||||||
OtherBeamFiles = TestBeamFiles --
|
OtherBeamFiles = TestBeamFiles --
|
||||||
[filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles],
|
[filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles],
|
||||||
ModuleBeamFiles = BeamFiles ++ OtherBeamFiles,
|
ModuleBeamFiles = BeamFiles ++ OtherBeamFiles,
|
||||||
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- ModuleBeamFiles],
|
|
||||||
|
%% Get modules to be run in eunit
|
||||||
|
AllModules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- AllBeamFiles],
|
||||||
|
{SuitesProvided, FilteredModules} = filter_suites(Config, AllModules),
|
||||||
|
Tests = get_tests(Config, SuitesProvided, ModuleBeamFiles, FilteredModules),
|
||||||
|
|
||||||
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
||||||
FilteredModules = filter_modules(Config, Modules),
|
|
||||||
|
|
||||||
{ok, CoverLog} = cover_init(Config, ModuleBeamFiles),
|
{ok, CoverLog} = cover_init(Config, ModuleBeamFiles),
|
||||||
|
|
||||||
StatusBefore = status_before_eunit(),
|
StatusBefore = status_before_eunit(),
|
||||||
EunitResult = perform_eunit(Config, FilteredModules),
|
EunitResult = perform_eunit(Config, Tests),
|
||||||
perform_cover(Config, FilteredModules, SrcModules),
|
|
||||||
|
|
||||||
|
perform_cover(Config, FilteredModules, SrcModules),
|
||||||
cover_close(CoverLog),
|
cover_close(CoverLog),
|
||||||
|
|
||||||
case proplists:get_value(reset_after_eunit, get_eunit_opts(Config),
|
case proplists:get_value(reset_after_eunit, get_eunit_opts(Config),
|
||||||
|
@ -164,17 +161,136 @@ setup_code_path() ->
|
||||||
true = code:add_pathz(rebar_utils:ebin_dir()),
|
true = code:add_pathz(rebar_utils:ebin_dir()),
|
||||||
CodePath.
|
CodePath.
|
||||||
|
|
||||||
filter_modules(Config, Modules) ->
|
filter_suites(Config, Modules) ->
|
||||||
RawSuites = rebar_config:get_global(Config, suites, ""),
|
RawSuites = rebar_config:get_global(Config, suites, ""),
|
||||||
|
SuitesProvided = RawSuites =/= "",
|
||||||
Suites = [list_to_atom(Suite) || Suite <- string:tokens(RawSuites, ",")],
|
Suites = [list_to_atom(Suite) || Suite <- string:tokens(RawSuites, ",")],
|
||||||
filter_modules1(Modules, Suites).
|
{SuitesProvided, filter_suites1(Modules, Suites)}.
|
||||||
|
|
||||||
filter_modules1(Modules, []) ->
|
filter_suites1(Modules, []) ->
|
||||||
Modules;
|
Modules;
|
||||||
filter_modules1(Modules, Suites) ->
|
filter_suites1(Modules, Suites) ->
|
||||||
[M || M <- Modules, lists:member(M, Suites)].
|
[M || M <- Modules, lists:member(M, Suites)].
|
||||||
|
|
||||||
perform_eunit(Config, FilteredModules) ->
|
get_tests(Config, SuitesProvided, ModuleBeamFiles, FilteredModules) ->
|
||||||
|
case SuitesProvided of
|
||||||
|
false ->
|
||||||
|
%% No specific suites have been provided, use ModuleBeamFiles
|
||||||
|
%% which filters out "*_tests" modules so eunit won't doubly run
|
||||||
|
%% them and cover only calculates coverage on production code.
|
||||||
|
%% However, keep "*_tests" modules that are not automatically
|
||||||
|
%% included by eunit.
|
||||||
|
%%
|
||||||
|
%% From 'Primitives' in the EUnit User's Guide
|
||||||
|
%% http://www.erlang.org/doc/apps/eunit/chapter.html
|
||||||
|
%% "In addition, EUnit will also look for another module whose
|
||||||
|
%% name is ModuleName plus the suffix _tests, and if it exists,
|
||||||
|
%% all the tests from that module will also be added. (If
|
||||||
|
%% ModuleName already contains the suffix _tests, this is not
|
||||||
|
%% done.) E.g., the specification {module, mymodule} will run all
|
||||||
|
%% tests in the modules mymodule and mymodule_tests. Typically,
|
||||||
|
%% the _tests module should only contain test cases that use the
|
||||||
|
%% public interface of the main module (and no other code)."
|
||||||
|
[rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- ModuleBeamFiles];
|
||||||
|
true ->
|
||||||
|
%% Specific suites have been provided, return the existing modules
|
||||||
|
build_tests(Config, FilteredModules)
|
||||||
|
end.
|
||||||
|
|
||||||
|
build_tests(Config, SuitesModules) ->
|
||||||
|
RawFunctions = rebar_utils:get_experimental_global(Config, tests, ""),
|
||||||
|
Tests = [list_to_atom(F1) || F1 <- string:tokens(RawFunctions, ",")],
|
||||||
|
case Tests of
|
||||||
|
[] ->
|
||||||
|
SuitesModules;
|
||||||
|
Functions ->
|
||||||
|
case build_tests1(SuitesModules, Functions, []) of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
RawTests ->
|
||||||
|
?CONSOLE(" Running test function(s):~n", []),
|
||||||
|
F = fun({M, F2}, Acc) ->
|
||||||
|
?CONSOLE(" ~p:~p/0~n", [M, F2]),
|
||||||
|
[eunit_test:function_wrapper(M, F2)|Acc]
|
||||||
|
end,
|
||||||
|
lists:foldl(F, [], RawTests)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
build_tests1([], _Functions, TestFunctions) ->
|
||||||
|
TestFunctions;
|
||||||
|
|
||||||
|
build_tests1([Module|TModules], Functions, TestFunctions) ->
|
||||||
|
%% Get module exports
|
||||||
|
ModuleStr = atom_to_list(Module),
|
||||||
|
ModuleExports = get_beam_test_exports(ModuleStr),
|
||||||
|
%% Get module _tests exports
|
||||||
|
TestModuleStr = string:concat(ModuleStr, "_tests"),
|
||||||
|
TestModuleExports = get_beam_test_exports(TestModuleStr),
|
||||||
|
%% Build tests {M, F} list
|
||||||
|
Tests = build_tests2(Functions, {Module, ModuleExports},
|
||||||
|
{list_to_atom(TestModuleStr), TestModuleExports}),
|
||||||
|
build_tests1(TModules, Functions, lists:merge([TestFunctions, Tests])).
|
||||||
|
|
||||||
|
build_tests2(Functions, {Mod, ModExports}, {TestMod, TestModExports}) ->
|
||||||
|
%% Look for matching functions into ModExports
|
||||||
|
ModExportsStr = [atom_to_list(E1) || E1 <- ModExports],
|
||||||
|
TestModExportsStr = [atom_to_list(E2) || E2 <- TestModExports],
|
||||||
|
get_matching_exports(Functions, {Mod, ModExportsStr},
|
||||||
|
{TestMod, TestModExportsStr}, []).
|
||||||
|
|
||||||
|
get_matching_exports([], _, _, Matched) ->
|
||||||
|
Matched;
|
||||||
|
get_matching_exports([Function|TFunctions], {Mod, ModExportsStr},
|
||||||
|
{TestMod, TestModExportsStr}, Matched) ->
|
||||||
|
|
||||||
|
FunctionStr = atom_to_list(Function),
|
||||||
|
%% Get matching Function in module, otherwise look in _tests module
|
||||||
|
NewMatch = case get_matching_export(FunctionStr, ModExportsStr) of
|
||||||
|
[] ->
|
||||||
|
{TestMod, get_matching_export(FunctionStr,
|
||||||
|
TestModExportsStr)};
|
||||||
|
MatchingExport ->
|
||||||
|
{Mod, MatchingExport}
|
||||||
|
end,
|
||||||
|
case NewMatch of
|
||||||
|
{_, []} ->
|
||||||
|
get_matching_exports(TFunctions, {Mod, ModExportsStr},
|
||||||
|
{TestMod, TestModExportsStr}, Matched);
|
||||||
|
_ ->
|
||||||
|
get_matching_exports(TFunctions, {Mod, ModExportsStr},
|
||||||
|
{TestMod, TestModExportsStr},
|
||||||
|
[NewMatch|Matched])
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_matching_export(_FunctionStr, []) ->
|
||||||
|
[];
|
||||||
|
get_matching_export(FunctionStr, [ExportStr|TExportsStr]) ->
|
||||||
|
case string:str(ExportStr, FunctionStr) of
|
||||||
|
1 ->
|
||||||
|
list_to_atom(ExportStr);
|
||||||
|
_ ->
|
||||||
|
get_matching_export(FunctionStr, TExportsStr)
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_beam_test_exports(ModuleStr) ->
|
||||||
|
FilePath = filename:join(eunit_dir(),
|
||||||
|
string:concat(ModuleStr, ".beam")),
|
||||||
|
case filelib:is_regular(FilePath) of
|
||||||
|
true ->
|
||||||
|
{beam_file, _, Exports0, _, _, _} = beam_disasm:file(FilePath),
|
||||||
|
Exports1 = [FunName || {FunName, FunArity, _} <- Exports0,
|
||||||
|
FunArity =:= 0],
|
||||||
|
F = fun(FName) ->
|
||||||
|
FNameStr = atom_to_list(FName),
|
||||||
|
re:run(FNameStr, "_test(_)?") =/= nomatch
|
||||||
|
end,
|
||||||
|
lists:filter(F, Exports1);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
perform_eunit(Config, Tests) ->
|
||||||
EunitOpts = get_eunit_opts(Config),
|
EunitOpts = get_eunit_opts(Config),
|
||||||
|
|
||||||
%% Move down into ?EUNIT_DIR while we run tests so any generated files
|
%% Move down into ?EUNIT_DIR while we run tests so any generated files
|
||||||
|
@ -182,7 +298,7 @@ perform_eunit(Config, FilteredModules) ->
|
||||||
Cwd = rebar_utils:get_cwd(),
|
Cwd = rebar_utils:get_cwd(),
|
||||||
ok = file:set_cwd(?EUNIT_DIR),
|
ok = file:set_cwd(?EUNIT_DIR),
|
||||||
|
|
||||||
EunitResult = (catch eunit:test(FilteredModules, EunitOpts)),
|
EunitResult = (catch eunit:test(Tests, EunitOpts)),
|
||||||
|
|
||||||
%% Return to original working dir
|
%% Return to original working dir
|
||||||
ok = file:set_cwd(Cwd),
|
ok = file:set_cwd(Cwd),
|
||||||
|
|
|
@ -59,6 +59,104 @@ eunit_test_() ->
|
||||||
?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}]
|
?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}]
|
||||||
end}.
|
end}.
|
||||||
|
|
||||||
|
eunit_with_suites_and_tests_test_() ->
|
||||||
|
[{"Ensure EUnit runs selected suites",
|
||||||
|
setup, fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit suites=myapp_mymod2")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Selected suite tests in 'test' directory are found and run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod2_tests:") =/= 0)},
|
||||||
|
|
||||||
|
{"Selected suite tests in 'src' directory are found and run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod2:") =/= 0)},
|
||||||
|
|
||||||
|
{"Unselected suite tests in 'test' directory are not run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod_tests:") =:= 0)},
|
||||||
|
|
||||||
|
{"Unselected suite tests in 'src' directory are not run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod:") =:= 0)},
|
||||||
|
|
||||||
|
{"Selected suite tests are only run once",
|
||||||
|
?_assert(string:str(RebarOut, "All 4 tests passed") =/= 0)}]
|
||||||
|
end},
|
||||||
|
{"Ensure EUnit runs selected _tests suites",
|
||||||
|
setup, fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit suites=myapp_mymod2_tests")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Selected suite tests in 'test' directory are found and run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod2_tests:") =/= 0)},
|
||||||
|
|
||||||
|
{"Selected suite tests in 'src' directory are not run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod2:") =:= 0)},
|
||||||
|
|
||||||
|
{"Unselected suite tests in 'test' directory are not run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod_tests:") =:= 0)},
|
||||||
|
|
||||||
|
{"Unselected suite tests in 'src' directory are not run",
|
||||||
|
?_assert(string:str(RebarOut, "myapp_mymod:") =:= 0)},
|
||||||
|
|
||||||
|
{"Selected suite tests are only run once",
|
||||||
|
?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}]
|
||||||
|
end},
|
||||||
|
{"Ensure EUnit runs a specific test defined in a selected suite",
|
||||||
|
setup, fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit suites=myapp_mymod2 tests=myprivate2")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Selected suite tests are found and run",
|
||||||
|
?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod2:myprivate2_test/0") =/= 0)},
|
||||||
|
|
||||||
|
{"Selected suite tests is run once",
|
||||||
|
?_assert(string:str(RebarOut, "Test passed") =/= 0)}]
|
||||||
|
end},
|
||||||
|
{"Ensure EUnit runs specific tests defined in selected suites",
|
||||||
|
setup, fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit suites=myapp_mymod,myapp_mymod2"
|
||||||
|
" tests=myprivate,myfunc2")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Selected suite tests are found and run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod:myprivate_test/0") =/= 0),
|
||||||
|
?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod2:myprivate2_test/0") =/= 0),
|
||||||
|
?_assert(
|
||||||
|
string:str(RebarOut,
|
||||||
|
"myapp_mymod2_tests:myfunc2_test/0") =/= 0)]},
|
||||||
|
|
||||||
|
{"Selected suite tests are run once",
|
||||||
|
?_assert(string:str(RebarOut, "All 3 tests passed") =/= 0)}]
|
||||||
|
end},
|
||||||
|
{"Ensure EUnit runs specific test in _tests suites",
|
||||||
|
setup,
|
||||||
|
fun() ->
|
||||||
|
setup_project_with_multiple_modules(),
|
||||||
|
rebar("-v eunit suites=myapp_mymod2_tests tests=common_name_test")
|
||||||
|
end,
|
||||||
|
fun teardown/1,
|
||||||
|
fun(RebarOut) ->
|
||||||
|
[{"Only selected suite tests are found and run",
|
||||||
|
[?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod2:common_name_test/0") =:= 0),
|
||||||
|
?_assert(string:str(RebarOut,
|
||||||
|
"myapp_mymod2_tests:common_name_test/0")
|
||||||
|
=/= 0)]},
|
||||||
|
|
||||||
|
{"Selected suite tests is run once",
|
||||||
|
?_assert(string:str(RebarOut, "Test passed") =/= 0)}]
|
||||||
|
end}].
|
||||||
|
|
||||||
cover_test_() ->
|
cover_test_() ->
|
||||||
{"Ensure Cover runs with tests in a test dir and no defined suite",
|
{"Ensure Cover runs with tests in a test dir and no defined suite",
|
||||||
setup, fun() -> setup_cover_project(), rebar("-v eunit") end,
|
setup, fun() -> setup_cover_project(), rebar("-v eunit") end,
|
||||||
|
@ -158,6 +256,21 @@ basic_setup_test_() ->
|
||||||
"-include_lib(\"eunit/include/eunit.hrl\").\n",
|
"-include_lib(\"eunit/include/eunit.hrl\").\n",
|
||||||
"myfunc_test() -> ?assertMatch(ok, myapp_mymod:myfunc()).\n"]).
|
"myfunc_test() -> ?assertMatch(ok, myapp_mymod:myfunc()).\n"]).
|
||||||
|
|
||||||
|
-define(myapp_mymod2,
|
||||||
|
["-module(myapp_mymod2).\n",
|
||||||
|
"-export([myfunc2/0]).\n",
|
||||||
|
"-include_lib(\"eunit/include/eunit.hrl\").\n",
|
||||||
|
"myfunc2() -> ok.\n",
|
||||||
|
"myprivate2_test() -> ?assert(true).\n",
|
||||||
|
"common_name_test() -> ?assert(true).\n"]).
|
||||||
|
|
||||||
|
-define(myapp_mymod2_tests,
|
||||||
|
["-module(myapp_mymod2_tests).\n",
|
||||||
|
"-compile([export_all]).\n",
|
||||||
|
"-include_lib(\"eunit/include/eunit.hrl\").\n",
|
||||||
|
"myfunc2_test() -> ?assertMatch(ok, myapp_mymod2:myfunc2()).\n",
|
||||||
|
"common_name_test() -> ?assert(true).\n"]).
|
||||||
|
|
||||||
-define(mysuite,
|
-define(mysuite,
|
||||||
["-module(mysuite).\n",
|
["-module(mysuite).\n",
|
||||||
"-export([all_test_/0]).\n",
|
"-export([all_test_/0]).\n",
|
||||||
|
@ -186,6 +299,11 @@ setup_basic_project() ->
|
||||||
ok = file:write_file("test/myapp_mymod_tests.erl", ?myapp_mymod_tests),
|
ok = file:write_file("test/myapp_mymod_tests.erl", ?myapp_mymod_tests),
|
||||||
ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod).
|
ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod).
|
||||||
|
|
||||||
|
setup_project_with_multiple_modules() ->
|
||||||
|
setup_basic_project(),
|
||||||
|
ok = file:write_file("test/myapp_mymod2_tests.erl", ?myapp_mymod2_tests),
|
||||||
|
ok = file:write_file("src/myapp_mymod2.erl", ?myapp_mymod2).
|
||||||
|
|
||||||
setup_cover_project() ->
|
setup_cover_project() ->
|
||||||
setup_basic_project(),
|
setup_basic_project(),
|
||||||
ok = file:write_file("rebar.config", "{cover_enabled, true}.\n").
|
ok = file:write_file("rebar.config", "{cover_enabled, true}.\n").
|
||||||
|
|
Loading…
Reference in a new issue