mirror of
https://github.com/correl/rebar.git
synced 2024-11-23 11:09:55 +00:00
Add 'qc' cmd and rename eunit-compile to test-compile
This commit is contained in:
parent
902dcdf3ff
commit
e75a97ad33
13 changed files with 309 additions and 151 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -5,8 +5,6 @@ rebar
|
|||
.*.swp
|
||||
rt.work
|
||||
.hgignore
|
||||
.eunit
|
||||
.test
|
||||
dialyzer_warnings
|
||||
xref_warnings
|
||||
rebar.cmd
|
||||
rebar.ps1
|
||||
|
|
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ all:
|
|||
./bootstrap
|
||||
|
||||
clean:
|
||||
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .eunit
|
||||
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .test
|
||||
|
||||
debug:
|
||||
@./bootstrap debug
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
rebar_utils.erl:161: Call to missing or unexported function escript:foldl/3
|
||||
rebar_utils.erl:162: Call to missing or unexported function escript:foldl/3
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
rebar_otp_app,
|
||||
rebar_port_compiler,
|
||||
rebar_protobuffs_compiler,
|
||||
rebar_qc,
|
||||
rebar_rel_utils,
|
||||
rebar_reltool,
|
||||
rebar_require_vsn,
|
||||
|
@ -73,6 +74,7 @@
|
|||
rebar_otp_app,
|
||||
rebar_ct,
|
||||
rebar_eunit,
|
||||
rebar_qc,
|
||||
rebar_escripter,
|
||||
rebar_edoc,
|
||||
rebar_shell,
|
||||
|
|
|
@ -10,3 +10,5 @@
|
|||
-define(ERROR(Str, Args), rebar_log:log(error, Str, Args)).
|
||||
|
||||
-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
|
||||
|
||||
-define(TEST_DIR, ".test").
|
||||
|
|
|
@ -9,8 +9,8 @@ _rebar()
|
|||
sopts="-h -c -v -V -f -j"
|
||||
lopts=" --help --commands --verbose --force --jobs= --version"
|
||||
cmdsnvars="check-deps clean compile create create-app create-node ct \
|
||||
doc delete-deps escriptize eunit eunit-compile get-deps generate \
|
||||
generate-upgrade help list-deps list-templates update-deps version \
|
||||
doc delete-deps escriptize eunit get-deps generate generate-upgrade \
|
||||
help list-deps list-templates qc test-compile update-deps version \
|
||||
xref overlay apps= case= force=1 jobs= suites= verbose=1 appid= \
|
||||
previous_release= nodeid= root_dir= skip_deps=true skip_apps= \
|
||||
template= template_dir="
|
||||
|
|
|
@ -88,6 +88,11 @@
|
|||
%% Option to use short names (i.e., -sname test) when starting ct
|
||||
{ct_use_short_names, true}.
|
||||
|
||||
%% == QuickCheck ==
|
||||
|
||||
%% If qc_mod is unspecified, rebar tries to detect Triq or EQC
|
||||
{qc_opts, [{qc_mod, module()}, Options]}.
|
||||
|
||||
%% == Cleanup ==
|
||||
|
||||
%% Which files to cleanup
|
||||
|
|
|
@ -297,10 +297,12 @@ generate-upgrade previous_release=path Build an upgrade package
|
|||
|
||||
generate-appups previous_release=path Generate appup files
|
||||
|
||||
test-compile Compile sources for eunit/qc run
|
||||
eunit [suites=foo] Run eunit [test/foo_tests.erl] tests
|
||||
eunit-compile Compile sources for EUnit run
|
||||
ct [suites=] [case=] Run common_test suites
|
||||
|
||||
qc Test QuichCheck properties
|
||||
|
||||
xref Run cross reference analysis
|
||||
|
||||
help Show the program options
|
||||
|
@ -362,9 +364,10 @@ filter_flags(Config, [Item | Rest], Commands) ->
|
|||
|
||||
command_names() ->
|
||||
["check-deps", "clean", "compile", "create", "create-app", "create-node",
|
||||
"ct", "delete-deps", "doc", "eunit", "eunit-compile", "generate",
|
||||
"generate-appups", "generate-upgrade", "get-deps", "help", "list-deps",
|
||||
"list-templates", "update-deps", "overlay", "shell", "version", "xref"].
|
||||
"ct", "delete-deps", "doc", "eunit", "generate", "generate-appups",
|
||||
"generate-upgrade", "get-deps", "help", "list-deps", "list-templates",
|
||||
"test-compile", "qc", "update-deps", "overlay", "shell", "version",
|
||||
"xref"].
|
||||
|
||||
unabbreviate_command_names([]) ->
|
||||
[];
|
||||
|
|
|
@ -29,9 +29,8 @@
|
|||
-export([compile/2,
|
||||
clean/2]).
|
||||
|
||||
%% for internal use by only eunit
|
||||
-export([doterl_compile/2,
|
||||
doterl_compile/3]).
|
||||
%% for internal use by only eunit and qc
|
||||
-export([test_compile/1]).
|
||||
|
||||
-include("rebar.hrl").
|
||||
|
||||
|
@ -111,11 +110,113 @@ clean(_Config, _AppFile) ->
|
|||
lists:foreach(fun(Dir) -> delete_dir(Dir, dirs(Dir)) end, dirs("ebin")),
|
||||
ok.
|
||||
|
||||
%% ===================================================================
|
||||
%% .erl Compilation API (externally used by only eunit and qc)
|
||||
%% ===================================================================
|
||||
|
||||
test_compile(Config) ->
|
||||
%% Obtain all the test modules for inclusion in the compile stage.
|
||||
%% Notice: this could also be achieved with the following
|
||||
%% rebar.config option: {eunit_compile_opts, [{src_dirs, ["test"]}]}
|
||||
TestErls = rebar_utils:find_files("test", ".*\\.erl\$"),
|
||||
|
||||
%% Copy source files to eunit dir for cover in case they are not directly
|
||||
%% in src but in a subdirectory of src. Cover only looks in cwd and ../src
|
||||
%% for source files. Also copy files from src_dirs.
|
||||
ErlOpts = rebar_utils:erl_opts(Config),
|
||||
|
||||
SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
|
||||
SrcErls = lists:foldl(
|
||||
fun(Dir, Acc) ->
|
||||
Files = rebar_utils:find_files(Dir, ".*\\.erl\$"),
|
||||
lists:append(Acc, Files)
|
||||
end, [], SrcDirs),
|
||||
|
||||
%% If it is not the first time rebar eunit is executed, there will be source
|
||||
%% files already present in ?TEST_DIR. Since some SCMs (like Perforce) set
|
||||
%% the source files as being read only (unless they are checked out), we
|
||||
%% need to be sure that the files already present in ?TEST_DIR are writable
|
||||
%% before doing the copy. This is done here by removing any file that was
|
||||
%% already present before calling rebar_file_utils:cp_r.
|
||||
|
||||
%% Get the full path to a file that was previously copied in ?TEST_DIR
|
||||
ToCleanUp = fun(F, Acc) ->
|
||||
F2 = filename:basename(F),
|
||||
F3 = filename:join([?TEST_DIR, F2]),
|
||||
case filelib:is_regular(F3) of
|
||||
true -> [F3|Acc];
|
||||
false -> Acc
|
||||
end
|
||||
end,
|
||||
|
||||
ok = rebar_file_utils:delete_each(lists:foldl(ToCleanUp, [], TestErls)),
|
||||
ok = rebar_file_utils:delete_each(lists:foldl(ToCleanUp, [], SrcErls)),
|
||||
|
||||
ok = rebar_file_utils:cp_r(SrcErls ++ TestErls, ?TEST_DIR),
|
||||
|
||||
%% Compile erlang code to ?TEST_DIR, using a tweaked config
|
||||
%% with appropriate defines for eunit, and include all the test modules
|
||||
%% as well.
|
||||
ok = doterl_compile(test_compile_config(Config), ?TEST_DIR, TestErls),
|
||||
|
||||
{ok, SrcErls}.
|
||||
|
||||
%% ===================================================================
|
||||
%% .erl Compilation API (externally used by only eunit)
|
||||
%% Internal functions
|
||||
%% ===================================================================
|
||||
|
||||
test_compile_config(Config) ->
|
||||
{Config1, TriqOpts} = triq_opts(Config),
|
||||
{Config2, PropErOpts} = proper_opts(Config1),
|
||||
{Config3, EqcOpts} = eqc_opts(Config2),
|
||||
|
||||
ErlOpts = rebar_config:get_list(Config3, erl_opts, []),
|
||||
EunitOpts = rebar_config:get_list(Config3, eunit_compile_opts, []),
|
||||
Opts0 = [{d, 'TEST'}] ++
|
||||
ErlOpts ++ EunitOpts ++ TriqOpts ++ PropErOpts ++ EqcOpts,
|
||||
Opts = [O || O <- Opts0, O =/= no_debug_info],
|
||||
Config4 = rebar_config:set(Config3, erl_opts, Opts),
|
||||
|
||||
FirstErls = rebar_config:get_list(Config4, eunit_first_files, []),
|
||||
rebar_config:set(Config4, erl_first_files, FirstErls).
|
||||
|
||||
triq_opts(Config) ->
|
||||
{NewConfig, IsAvail} = is_lib_avail(Config, is_triq_avail, triq,
|
||||
"triq.hrl", "Triq"),
|
||||
Opts = define_if('TRIQ', IsAvail),
|
||||
{NewConfig, Opts}.
|
||||
|
||||
proper_opts(Config) ->
|
||||
{NewConfig, IsAvail} = is_lib_avail(Config, is_proper_avail, proper,
|
||||
"proper.hrl", "PropEr"),
|
||||
Opts = define_if('PROPER', IsAvail),
|
||||
{NewConfig, Opts}.
|
||||
|
||||
eqc_opts(Config) ->
|
||||
{NewConfig, IsAvail} = is_lib_avail(Config, is_eqc_avail, eqc,
|
||||
"eqc.hrl", "QuickCheck"),
|
||||
Opts = define_if('EQC', IsAvail),
|
||||
{NewConfig, Opts}.
|
||||
|
||||
define_if(Def, true) -> [{d, Def}];
|
||||
define_if(_Def, false) -> [].
|
||||
|
||||
is_lib_avail(Config, DictKey, Mod, Hrl, Name) ->
|
||||
case rebar_config:get_xconf(Config, DictKey, undefined) of
|
||||
undefined ->
|
||||
IsAvail = case code:lib_dir(Mod, include) of
|
||||
{error, bad_name} ->
|
||||
false;
|
||||
Dir ->
|
||||
filelib:is_regular(filename:join(Dir, Hrl))
|
||||
end,
|
||||
NewConfig = rebar_config:set_xconf(Config, DictKey, IsAvail),
|
||||
?DEBUG("~s availability: ~p\n", [Name, IsAvail]),
|
||||
{NewConfig, IsAvail};
|
||||
IsAvail ->
|
||||
{Config, IsAvail}
|
||||
end.
|
||||
|
||||
-spec doterl_compile(Config::rebar_config:config(),
|
||||
OutDir::file:filename()) -> 'ok'.
|
||||
doterl_compile(Config, OutDir) ->
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
%% @doc rebar_eunit supports the following commands:
|
||||
%% <ul>
|
||||
%% <li>eunit - runs eunit tests</li>
|
||||
%% <li>clean - remove .eunit directory</li>
|
||||
%% <li>clean - remove ?TEST_DIR directory</li>
|
||||
%% <li>reset_after_eunit::boolean() - default = true.
|
||||
%% If true, try to "reset" VM state to approximate state prior to
|
||||
%% running the EUnit tests:
|
||||
|
@ -55,12 +55,10 @@
|
|||
|
||||
-export([eunit/2,
|
||||
clean/2,
|
||||
'eunit-compile'/2]).
|
||||
'test-compile'/2]).
|
||||
|
||||
-include("rebar.hrl").
|
||||
|
||||
-define(EUNIT_DIR, ".eunit").
|
||||
|
||||
%% ===================================================================
|
||||
%% Public API
|
||||
%% ===================================================================
|
||||
|
@ -70,9 +68,9 @@ eunit(Config, _AppFile) ->
|
|||
%% Save code path
|
||||
CodePath = setup_code_path(),
|
||||
|
||||
{ok, SrcErls} = eunit_compile(Config),
|
||||
{ok, SrcErls} = rebar_erlc_compiler:test_compile(Config),
|
||||
|
||||
%% Build a list of all the .beams in ?EUNIT_DIR -- use this for
|
||||
%% Build a list of all the .beams in ?TEST_DIR -- use this for
|
||||
%% cover and eunit testing. Normally you can just tell cover
|
||||
%% and/or eunit to scan the directory for you, but eunit does a
|
||||
%% code:purge in conjunction with that scan and causes any cover
|
||||
|
@ -80,14 +78,14 @@ eunit(Config, _AppFile) ->
|
|||
%% 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.
|
||||
AllBeamFiles = rebar_utils:beams(?EUNIT_DIR),
|
||||
AllBeamFiles = rebar_utils:beams(?TEST_DIR),
|
||||
{BeamFiles, TestBeamFiles} =
|
||||
lists:partition(fun(N) -> string:str(N, "_tests.beam") =:= 0 end,
|
||||
AllBeamFiles),
|
||||
OtherBeamFiles = TestBeamFiles --
|
||||
[filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles],
|
||||
ModuleBeamFiles = BeamFiles ++ OtherBeamFiles,
|
||||
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- ModuleBeamFiles],
|
||||
Modules = [rebar_utils:beam_to_mod(?TEST_DIR, N) || N <- ModuleBeamFiles],
|
||||
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
||||
FilteredModules = filter_modules(Config, Modules),
|
||||
|
||||
|
@ -119,13 +117,13 @@ eunit(Config, _AppFile) ->
|
|||
ok.
|
||||
|
||||
clean(_Config, _File) ->
|
||||
rebar_file_utils:rm_rf(?EUNIT_DIR).
|
||||
rebar_file_utils:rm_rf(?TEST_DIR).
|
||||
|
||||
'eunit-compile'(Config, _File) ->
|
||||
'test-compile'(Config, _File) ->
|
||||
ok = ensure_dirs(),
|
||||
%% Save code path
|
||||
CodePath = setup_code_path(),
|
||||
{ok, _SrcErls} = eunit_compile(Config),
|
||||
{ok, _SrcErls} = rebar_erlc_compiler:test_compile(Config),
|
||||
%% Restore code path
|
||||
true = code:set_path(CodePath),
|
||||
ok.
|
||||
|
@ -134,57 +132,10 @@ clean(_Config, _File) ->
|
|||
%% Internal functions
|
||||
%% ===================================================================
|
||||
|
||||
eunit_compile(Config) ->
|
||||
%% Obtain all the test modules for inclusion in the compile stage.
|
||||
%% Notice: this could also be achieved with the following
|
||||
%% rebar.config option: {eunit_compile_opts, [{src_dirs, ["test"]}]}
|
||||
TestErls = rebar_utils:find_files("test", ".*\\.erl\$"),
|
||||
|
||||
%% Copy source files to eunit dir for cover in case they are not directly
|
||||
%% in src but in a subdirectory of src. Cover only looks in cwd and ../src
|
||||
%% for source files. Also copy files from src_dirs.
|
||||
ErlOpts = rebar_utils:erl_opts(Config),
|
||||
|
||||
SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
|
||||
SrcErls = lists:foldl(
|
||||
fun(Dir, Acc) ->
|
||||
Files = rebar_utils:find_files(Dir, ".*\\.erl\$"),
|
||||
lists:append(Acc, Files)
|
||||
end, [], SrcDirs),
|
||||
|
||||
%% If it is not the first time rebar eunit is executed, there will be source
|
||||
%% files already present in ?EUNIT_DIR. Since some SCMs (like Perforce) set
|
||||
%% the source files as being read only (unless they are checked out), we
|
||||
%% need to be sure that the files already present in ?EUNIT_DIR are writable
|
||||
%% before doing the copy. This is done here by removing any file that was
|
||||
%% already present before calling rebar_file_utils:cp_r.
|
||||
|
||||
%% Get the full path to a file that was previously copied in ?EUNIT_DIR
|
||||
ToCleanUp = fun(F, Acc) ->
|
||||
F2 = filename:basename(F),
|
||||
F3 = filename:join([?EUNIT_DIR, F2]),
|
||||
case filelib:is_regular(F3) of
|
||||
true -> [F3|Acc];
|
||||
false -> Acc
|
||||
end
|
||||
end,
|
||||
|
||||
ok = rebar_file_utils:delete_each(lists:foldl(ToCleanUp, [], TestErls)),
|
||||
ok = rebar_file_utils:delete_each(lists:foldl(ToCleanUp, [], SrcErls)),
|
||||
|
||||
ok = rebar_file_utils:cp_r(SrcErls ++ TestErls, ?EUNIT_DIR),
|
||||
|
||||
%% Compile erlang code to ?EUNIT_DIR, using a tweaked config
|
||||
%% with appropriate defines for eunit, and include all the test modules
|
||||
%% as well.
|
||||
ok = rebar_erlc_compiler:doterl_compile(eunit_config(Config),
|
||||
?EUNIT_DIR, TestErls),
|
||||
{ok, SrcErls}.
|
||||
|
||||
ensure_dirs() ->
|
||||
%% Make sure ?EUNIT_DIR/ and ebin/ directory exists (append dummy module)
|
||||
ok = filelib:ensure_dir(filename:join(eunit_dir(), "dummy")),
|
||||
ok = filelib:ensure_dir(filename:join(ebin_dir(), "dummy")).
|
||||
%% Make sure ?TEST_DIR/ and ebin/ directory exists (append dummy module)
|
||||
ok = filelib:ensure_dir(filename:join(rebar_utils:test_dir(), "dummy")),
|
||||
ok = filelib:ensure_dir(filename:join(rebar_utils:ebin_dir(), "dummy")).
|
||||
|
||||
setup_code_path() ->
|
||||
%% Setup code path prior to compilation so that parse_transforms
|
||||
|
@ -192,16 +143,10 @@ setup_code_path() ->
|
|||
%% to the END of the code path so that we don't have to jump
|
||||
%% through hoops to access the .app file
|
||||
CodePath = code:get_path(),
|
||||
true = code:add_patha(eunit_dir()),
|
||||
true = code:add_pathz(ebin_dir()),
|
||||
true = code:add_patha(rebar_utils:test_dir()),
|
||||
true = code:add_pathz(rebar_utils:ebin_dir()),
|
||||
CodePath.
|
||||
|
||||
eunit_dir() ->
|
||||
filename:join(rebar_utils:get_cwd(), ?EUNIT_DIR).
|
||||
|
||||
ebin_dir() ->
|
||||
filename:join(rebar_utils:get_cwd(), "ebin").
|
||||
|
||||
filter_modules(Config, Modules) ->
|
||||
RawSuites = rebar_utils:get_deprecated_global(Config, suite, suites,
|
||||
[], "soon"),
|
||||
|
@ -216,10 +161,10 @@ filter_modules1(Modules, Suites) ->
|
|||
perform_eunit(Config, FilteredModules) ->
|
||||
EunitOpts = get_eunit_opts(Config),
|
||||
|
||||
%% Move down into ?EUNIT_DIR while we run tests so any generated files
|
||||
%% Move down into ?TEST_DIR while we run tests so any generated files
|
||||
%% are created there (versus in the source dir)
|
||||
Cwd = rebar_utils:get_cwd(),
|
||||
ok = file:set_cwd(?EUNIT_DIR),
|
||||
ok = file:set_cwd(?TEST_DIR),
|
||||
|
||||
EunitResult = (catch eunit:test(FilteredModules, EunitOpts)),
|
||||
|
||||
|
@ -239,51 +184,6 @@ get_eunit_opts(Config) ->
|
|||
|
||||
BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []).
|
||||
|
||||
eunit_config(Config) ->
|
||||
{Config1, EqcOpts} = eqc_opts(Config),
|
||||
{Config2, PropErOpts} = proper_opts(Config1),
|
||||
|
||||
ErlOpts = rebar_config:get_list(Config2, erl_opts, []),
|
||||
EunitOpts = rebar_config:get_list(Config2, eunit_compile_opts, []),
|
||||
Opts0 = [{d, 'TEST'}] ++
|
||||
ErlOpts ++ EunitOpts ++ EqcOpts ++ PropErOpts,
|
||||
Opts = [O || O <- Opts0, O =/= no_debug_info],
|
||||
Config3 = rebar_config:set(Config2, erl_opts, Opts),
|
||||
|
||||
FirstErls = rebar_config:get_list(Config3, eunit_first_files, []),
|
||||
rebar_config:set(Config3, erl_first_files, FirstErls).
|
||||
|
||||
eqc_opts(Config) ->
|
||||
{NewConfig, IsAvail} = is_lib_avail(Config, is_eqc_avail, eqc,
|
||||
"eqc.hrl", "QuickCheck"),
|
||||
Opts = define_if('EQC', IsAvail),
|
||||
{NewConfig, Opts}.
|
||||
|
||||
proper_opts(Config) ->
|
||||
{NewConfig, IsAvail} = is_lib_avail(Config, is_proper_avail, proper,
|
||||
"proper.hrl", "PropEr"),
|
||||
Opts = define_if('PROPER', IsAvail),
|
||||
{NewConfig, Opts}.
|
||||
|
||||
define_if(Def, true) -> [{d, Def}];
|
||||
define_if(_Def, false) -> [].
|
||||
|
||||
is_lib_avail(Config, DictKey, Mod, Hrl, Name) ->
|
||||
case rebar_config:get_xconf(Config, DictKey, undefined) of
|
||||
undefined ->
|
||||
IsAvail = case code:lib_dir(Mod, include) of
|
||||
{error, bad_name} ->
|
||||
false;
|
||||
Dir ->
|
||||
filelib:is_regular(filename:join(Dir, Hrl))
|
||||
end,
|
||||
NewConfig = rebar_config:set_xconf(Config, DictKey, IsAvail),
|
||||
?DEBUG("~s availability: ~p\n", [Name, IsAvail]),
|
||||
{NewConfig, IsAvail};
|
||||
IsAvail ->
|
||||
{Config, IsAvail}
|
||||
end.
|
||||
|
||||
perform_cover(Config, BeamFiles, SrcModules) ->
|
||||
perform_cover(rebar_config:get(Config, cover_enabled, false),
|
||||
Config, BeamFiles, SrcModules).
|
||||
|
@ -308,7 +208,7 @@ cover_analyze(Config, FilteredModules, SrcModules) ->
|
|||
[html])
|
||||
end, Coverage),
|
||||
|
||||
Index = filename:join([rebar_utils:get_cwd(), ?EUNIT_DIR, "index.html"]),
|
||||
Index = filename:join([rebar_utils:get_cwd(), ?TEST_DIR, "index.html"]),
|
||||
?CONSOLE("Cover analysis: ~s\n", [Index]),
|
||||
|
||||
%% Print coverage report, if configured
|
||||
|
@ -328,7 +228,7 @@ cover_init(false, _BeamFiles) ->
|
|||
{ok, not_enabled};
|
||||
cover_init(true, BeamFiles) ->
|
||||
%% Attempt to start the cover server, then set it's group leader to
|
||||
%% .eunit/cover.log, so all cover log messages will go there instead of
|
||||
%% ?TEST_DIR/cover.log, so all cover log messages will go there instead of
|
||||
%% to stdout. If the cover server is already started we'll reuse that
|
||||
%% pid.
|
||||
{ok, CoverPid} = case cover:start() of
|
||||
|
@ -341,7 +241,7 @@ cover_init(true, BeamFiles) ->
|
|||
end,
|
||||
|
||||
{ok, F} = OkOpen = file:open(
|
||||
filename:join([?EUNIT_DIR, "cover.log"]),
|
||||
filename:join([?TEST_DIR, "cover.log"]),
|
||||
[write]),
|
||||
|
||||
group_leader(F, CoverPid),
|
||||
|
@ -416,7 +316,7 @@ align_notcovered_count(Module, Covered, NotCovered, true) ->
|
|||
{Module, Covered, NotCovered - 1}.
|
||||
|
||||
cover_write_index(Coverage, SrcModules) ->
|
||||
{ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]),
|
||||
{ok, F} = file:open(filename:join([?TEST_DIR, "index.html"]), [write]),
|
||||
ok = file:write(F, "<html><head><title>Coverage Summary</title></head>\n"),
|
||||
IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
|
||||
{SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
|
||||
|
@ -474,7 +374,7 @@ cover_print_coverage(Coverage) ->
|
|||
?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]).
|
||||
|
||||
cover_file(Module) ->
|
||||
filename:join([?EUNIT_DIR, atom_to_list(Module) ++ ".COVER.html"]).
|
||||
filename:join([?TEST_DIR, atom_to_list(Module) ++ ".COVER.html"]).
|
||||
|
||||
percentage(0, 0) ->
|
||||
"not executed";
|
||||
|
|
140
src/rebar_qc.erl
Normal file
140
src/rebar_qc.erl
Normal file
|
@ -0,0 +1,140 @@
|
|||
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
|
||||
%% ex: ts=4 sw=4 et
|
||||
%% -------------------------------------------------------------------
|
||||
%%
|
||||
%% rebar: Erlang Build Tools
|
||||
%%
|
||||
%% Copyright (c) 2011-2012 Tuncer Ayaz
|
||||
%%
|
||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
%% of this software and associated documentation files (the "Software"), to deal
|
||||
%% in the Software without restriction, including without limitation the rights
|
||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
%% copies of the Software, and to permit persons to whom the Software is
|
||||
%% furnished to do so, subject to the following conditions:
|
||||
%%
|
||||
%% The above copyright notice and this permission notice shall be included in
|
||||
%% all copies or substantial portions of the Software.
|
||||
%%
|
||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
%% THE SOFTWARE.
|
||||
%% -------------------------------------------------------------------
|
||||
-module(rebar_qc).
|
||||
|
||||
-export([qc/2, triq/2, eqc/2]).
|
||||
|
||||
-include("rebar.hrl").
|
||||
|
||||
%% ===================================================================
|
||||
%% Public API
|
||||
%% ===================================================================
|
||||
|
||||
qc(Config, _AppFile) ->
|
||||
?CONSOLE("NOTICE: Using experimental 'qc' command~n", []),
|
||||
run_qc(Config, qc_opts(Config)).
|
||||
|
||||
triq(Config, _AppFile) ->
|
||||
?CONSOLE("NOTICE: Using experimental 'triq' command~n", []),
|
||||
ok = load_qc_mod(triq),
|
||||
run_qc(Config, qc_opts(Config), triq).
|
||||
|
||||
eqc(Config, _AppFile) ->
|
||||
?CONSOLE("NOTICE: Using experimental 'eqc' command~n", []),
|
||||
ok = load_qc_mod(eqc),
|
||||
run_qc(Config, qc_opts(Config), eqc).
|
||||
|
||||
%% ===================================================================
|
||||
%% Internal functions
|
||||
%% ===================================================================
|
||||
|
||||
-define(TRIQ_MOD, triq).
|
||||
-define(EQC_MOD, eqc).
|
||||
|
||||
qc_opts(Config) ->
|
||||
rebar_config:get(Config, qc_opts, []).
|
||||
|
||||
run_qc(Config, QCOpts) ->
|
||||
run_qc(Config, QCOpts, select_qc_mod(QCOpts)).
|
||||
|
||||
run_qc(Config, RawQCOpts, QC) ->
|
||||
?DEBUG("Selected QC module: ~p~n", [QC]),
|
||||
QCOpts = lists:filter(fun({qc_mod, _}) -> false;
|
||||
(_) -> true
|
||||
end, RawQCOpts),
|
||||
run(Config, QC, QCOpts).
|
||||
|
||||
select_qc_mod(QCOpts) ->
|
||||
case proplists:get_value(qc_mod, QCOpts) of
|
||||
undefined ->
|
||||
detect_qc_mod();
|
||||
QC ->
|
||||
case code:ensure_loaded(QC) of
|
||||
{module, QC} ->
|
||||
QC;
|
||||
{error, nofile} ->
|
||||
?ABORT("Configured QC library '~p' not available~n", [QC])
|
||||
end
|
||||
end.
|
||||
|
||||
detect_qc_mod() ->
|
||||
case code:ensure_loaded(?TRIQ_MOD) of
|
||||
{module, ?TRIQ_MOD} ->
|
||||
?TRIQ_MOD;
|
||||
{error, nofile} ->
|
||||
case code:ensure_loaded(?EQC_MOD) of
|
||||
{module, ?EQC_MOD} ->
|
||||
?EQC_MOD;
|
||||
{error, nofile} ->
|
||||
?ABORT("No QC library available~n", [])
|
||||
end
|
||||
end.
|
||||
|
||||
load_qc_mod(Mod) ->
|
||||
case code:ensure_loaded(Mod) of
|
||||
{module, Mod} ->
|
||||
ok;
|
||||
{error, nofile} ->
|
||||
?ABORT("Failed to load QC lib '~p'~n", [Mod])
|
||||
end.
|
||||
|
||||
setup_codepath() ->
|
||||
CodePath = code:get_path(),
|
||||
true = code:add_patha(rebar_utils:test_dir()),
|
||||
true = code:add_patha(rebar_utils:ebin_dir()),
|
||||
CodePath.
|
||||
|
||||
run(Config, QC, QCOpts) ->
|
||||
?DEBUG("qc_opts: ~p~n", [QCOpts]),
|
||||
|
||||
ok = filelib:ensure_dir(?TEST_DIR ++ "/foo"),
|
||||
CodePath = setup_codepath(),
|
||||
|
||||
%% Compile erlang code to ?TEST_DIR, using a tweaked config
|
||||
%% with appropriate defines, and include all the test modules
|
||||
%% as well.
|
||||
{ok, _SrcErls} = rebar_erlc_compiler:test_compile(Config),
|
||||
|
||||
case lists:flatten([qc_module(QC, QCOpts, M) || M <- find_prop_mods()]) of
|
||||
[] ->
|
||||
true = code:set_path(CodePath),
|
||||
ok;
|
||||
Errors ->
|
||||
?ABORT("One or more QC properties didn't hold true:~n~p~n",
|
||||
[Errors])
|
||||
end.
|
||||
|
||||
qc_module(QC=triq, _QCOpts, M) -> QC:module(M);
|
||||
qc_module(QC=eqc, QCOpts, M) -> QC:module(QCOpts, M).
|
||||
|
||||
find_prop_mods() ->
|
||||
Beams = rebar_utils:find_files(?TEST_DIR, ".*\\.beam\$"),
|
||||
[M || M <- [rebar_utils:erl_to_mod(Beam) || Beam <- Beams], has_prop(M)].
|
||||
|
||||
has_prop(Mod) ->
|
||||
lists:any(fun({F,_A}) -> lists:prefix("prop_", atom_to_list(F)) end,
|
||||
Mod:module_info(exports)).
|
|
@ -49,8 +49,9 @@
|
|||
get_deprecated_local/4, get_deprecated_local/5,
|
||||
delayed_halt/1,
|
||||
erl_opts/1,
|
||||
src_dirs/1
|
||||
]).
|
||||
src_dirs/1,
|
||||
test_dir/0,
|
||||
ebin_dir/0]).
|
||||
|
||||
-include("rebar.hrl").
|
||||
|
||||
|
@ -306,6 +307,12 @@ src_dirs([]) ->
|
|||
src_dirs(SrcDirs) ->
|
||||
SrcDirs.
|
||||
|
||||
test_dir() ->
|
||||
filename:join(rebar_utils:get_cwd(), ?TEST_DIR).
|
||||
|
||||
ebin_dir() ->
|
||||
filename:join(rebar_utils:get_cwd(), "ebin").
|
||||
|
||||
%% ====================================================================
|
||||
%% Internal functions
|
||||
%% ====================================================================
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
%% Assuming this test is run inside the rebar 'eunit'
|
||||
%% command, the current working directory will be '.eunit'
|
||||
%% command, the current working directory will be '.test'
|
||||
-define(REBAR_SCRIPT, "../rebar").
|
||||
|
||||
-define(TMP_DIR, "tmp_eunit/").
|
||||
|
@ -70,7 +70,7 @@ cover_test_() ->
|
|||
|
||||
{"Only production modules get coverage reports",
|
||||
assert_files_not_in("the temporary eunit directory",
|
||||
[".eunit/myapp_mymod_tests.COVER.html"])}]}.
|
||||
[".test/myapp_mymod_tests.COVER.html"])}]}.
|
||||
|
||||
cover_with_suite_test_() ->
|
||||
{"Ensure Cover runs with Tests in a test dir and a test suite",
|
||||
|
@ -83,21 +83,21 @@ cover_with_suite_test_() ->
|
|||
|
||||
[{"Cover reports are generated for module",
|
||||
assert_files_in("the temporary eunit directory",
|
||||
[".eunit/index.html",
|
||||
".eunit/mysuite.COVER.html"])},
|
||||
[".test/index.html",
|
||||
".test/mysuite.COVER.html"])},
|
||||
|
||||
{"Only production modules get coverage reports",
|
||||
assert_files_not_in("the temporary eunit directory",
|
||||
[".eunit/myapp_app.COVER.html",
|
||||
".eunit/myapp_mymod.COVER.html",
|
||||
".eunit/myapp_sup.COVER.html",
|
||||
".eunit/myapp_mymod_tests.COVER.html"])}]}.
|
||||
[".test/myapp_app.COVER.html",
|
||||
".test/myapp_mymod.COVER.html",
|
||||
".test/myapp_sup.COVER.html",
|
||||
".test/myapp_mymod_tests.COVER.html"])}]}.
|
||||
|
||||
expected_cover_generated_files() ->
|
||||
[".eunit/index.html",
|
||||
".eunit/myapp_app.COVER.html",
|
||||
".eunit/myapp_mymod.COVER.html",
|
||||
".eunit/myapp_sup.COVER.html"].
|
||||
[".test/index.html",
|
||||
".test/myapp_app.COVER.html",
|
||||
".test/myapp_mymod.COVER.html",
|
||||
".test/myapp_sup.COVER.html"].
|
||||
|
||||
cover_coverage_test_() ->
|
||||
{"Coverage is accurately calculated",
|
||||
|
@ -246,7 +246,7 @@ assert_files_not_in(_, []) -> [].
|
|||
|
||||
assert_full_coverage(Mod) ->
|
||||
fun() ->
|
||||
{ok, F} = file:read_file(".eunit/index.html"),
|
||||
{ok, F} = file:read_file(".test/index.html"),
|
||||
Result = [X || X <- string:tokens(binary_to_list(F), "\n"),
|
||||
string:str(X, Mod) =/= 0,
|
||||
string:str(X, "100%") =/= 0],
|
||||
|
|
Loading…
Reference in a new issue