mirror of
https://github.com/correl/rebar.git
synced 2024-11-23 19:19:54 +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
|
.*.swp
|
||||||
rt.work
|
rt.work
|
||||||
.hgignore
|
.hgignore
|
||||||
.eunit
|
.test
|
||||||
dialyzer_warnings
|
dialyzer_warnings
|
||||||
xref_warnings
|
|
||||||
rebar.cmd
|
rebar.cmd
|
||||||
rebar.ps1
|
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ all:
|
||||||
./bootstrap
|
./bootstrap
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .eunit
|
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .test
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
@./bootstrap 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_otp_app,
|
||||||
rebar_port_compiler,
|
rebar_port_compiler,
|
||||||
rebar_protobuffs_compiler,
|
rebar_protobuffs_compiler,
|
||||||
|
rebar_qc,
|
||||||
rebar_rel_utils,
|
rebar_rel_utils,
|
||||||
rebar_reltool,
|
rebar_reltool,
|
||||||
rebar_require_vsn,
|
rebar_require_vsn,
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
rebar_otp_app,
|
rebar_otp_app,
|
||||||
rebar_ct,
|
rebar_ct,
|
||||||
rebar_eunit,
|
rebar_eunit,
|
||||||
|
rebar_qc,
|
||||||
rebar_escripter,
|
rebar_escripter,
|
||||||
rebar_edoc,
|
rebar_edoc,
|
||||||
rebar_shell,
|
rebar_shell,
|
||||||
|
|
|
@ -10,3 +10,5 @@
|
||||||
-define(ERROR(Str, Args), rebar_log:log(error, Str, Args)).
|
-define(ERROR(Str, Args), rebar_log:log(error, Str, Args)).
|
||||||
|
|
||||||
-define(FMT(Str, Args), lists:flatten(io_lib:format(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"
|
sopts="-h -c -v -V -f -j"
|
||||||
lopts=" --help --commands --verbose --force --jobs= --version"
|
lopts=" --help --commands --verbose --force --jobs= --version"
|
||||||
cmdsnvars="check-deps clean compile create create-app create-node ct \
|
cmdsnvars="check-deps clean compile create create-app create-node ct \
|
||||||
doc delete-deps escriptize eunit eunit-compile get-deps generate \
|
doc delete-deps escriptize eunit get-deps generate generate-upgrade \
|
||||||
generate-upgrade help list-deps list-templates update-deps version \
|
help list-deps list-templates qc test-compile update-deps version \
|
||||||
xref overlay apps= case= force=1 jobs= suites= verbose=1 appid= \
|
xref overlay apps= case= force=1 jobs= suites= verbose=1 appid= \
|
||||||
previous_release= nodeid= root_dir= skip_deps=true skip_apps= \
|
previous_release= nodeid= root_dir= skip_deps=true skip_apps= \
|
||||||
template= template_dir="
|
template= template_dir="
|
||||||
|
|
|
@ -88,6 +88,11 @@
|
||||||
%% Option to use short names (i.e., -sname test) when starting ct
|
%% Option to use short names (i.e., -sname test) when starting ct
|
||||||
{ct_use_short_names, true}.
|
{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 ==
|
%% == Cleanup ==
|
||||||
|
|
||||||
%% Which files to 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
|
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 [suites=foo] Run eunit [test/foo_tests.erl] tests
|
||||||
eunit-compile Compile sources for EUnit run
|
|
||||||
ct [suites=] [case=] Run common_test suites
|
ct [suites=] [case=] Run common_test suites
|
||||||
|
|
||||||
|
qc Test QuichCheck properties
|
||||||
|
|
||||||
xref Run cross reference analysis
|
xref Run cross reference analysis
|
||||||
|
|
||||||
help Show the program options
|
help Show the program options
|
||||||
|
@ -362,9 +364,10 @@ filter_flags(Config, [Item | Rest], Commands) ->
|
||||||
|
|
||||||
command_names() ->
|
command_names() ->
|
||||||
["check-deps", "clean", "compile", "create", "create-app", "create-node",
|
["check-deps", "clean", "compile", "create", "create-app", "create-node",
|
||||||
"ct", "delete-deps", "doc", "eunit", "eunit-compile", "generate",
|
"ct", "delete-deps", "doc", "eunit", "generate", "generate-appups",
|
||||||
"generate-appups", "generate-upgrade", "get-deps", "help", "list-deps",
|
"generate-upgrade", "get-deps", "help", "list-deps", "list-templates",
|
||||||
"list-templates", "update-deps", "overlay", "shell", "version", "xref"].
|
"test-compile", "qc", "update-deps", "overlay", "shell", "version",
|
||||||
|
"xref"].
|
||||||
|
|
||||||
unabbreviate_command_names([]) ->
|
unabbreviate_command_names([]) ->
|
||||||
[];
|
[];
|
||||||
|
|
|
@ -29,9 +29,8 @@
|
||||||
-export([compile/2,
|
-export([compile/2,
|
||||||
clean/2]).
|
clean/2]).
|
||||||
|
|
||||||
%% for internal use by only eunit
|
%% for internal use by only eunit and qc
|
||||||
-export([doterl_compile/2,
|
-export([test_compile/1]).
|
||||||
doterl_compile/3]).
|
|
||||||
|
|
||||||
-include("rebar.hrl").
|
-include("rebar.hrl").
|
||||||
|
|
||||||
|
@ -111,11 +110,113 @@ clean(_Config, _AppFile) ->
|
||||||
lists:foreach(fun(Dir) -> delete_dir(Dir, dirs(Dir)) end, dirs("ebin")),
|
lists:foreach(fun(Dir) -> delete_dir(Dir, dirs(Dir)) end, dirs("ebin")),
|
||||||
ok.
|
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(),
|
-spec doterl_compile(Config::rebar_config:config(),
|
||||||
OutDir::file:filename()) -> 'ok'.
|
OutDir::file:filename()) -> 'ok'.
|
||||||
doterl_compile(Config, OutDir) ->
|
doterl_compile(Config, OutDir) ->
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
%% @doc rebar_eunit supports the following commands:
|
%% @doc rebar_eunit supports the following commands:
|
||||||
%% <ul>
|
%% <ul>
|
||||||
%% <li>eunit - runs eunit tests</li>
|
%% <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.
|
%% <li>reset_after_eunit::boolean() - default = true.
|
||||||
%% If true, try to "reset" VM state to approximate state prior to
|
%% If true, try to "reset" VM state to approximate state prior to
|
||||||
%% running the EUnit tests:
|
%% running the EUnit tests:
|
||||||
|
@ -55,12 +55,10 @@
|
||||||
|
|
||||||
-export([eunit/2,
|
-export([eunit/2,
|
||||||
clean/2,
|
clean/2,
|
||||||
'eunit-compile'/2]).
|
'test-compile'/2]).
|
||||||
|
|
||||||
-include("rebar.hrl").
|
-include("rebar.hrl").
|
||||||
|
|
||||||
-define(EUNIT_DIR, ".eunit").
|
|
||||||
|
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
%% Public API
|
%% Public API
|
||||||
%% ===================================================================
|
%% ===================================================================
|
||||||
|
@ -70,9 +68,9 @@ eunit(Config, _AppFile) ->
|
||||||
%% Save code path
|
%% Save code path
|
||||||
CodePath = setup_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
|
%% 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
|
||||||
|
@ -80,14 +78,14 @@ eunit(Config, _AppFile) ->
|
||||||
%% eunit won't doubly run them and so cover only calculates
|
%% eunit won't doubly run them and so cover only calculates
|
||||||
%% coverage on production code. However, keep "*_tests" modules
|
%% coverage on production code. However, keep "*_tests" modules
|
||||||
%% that are not automatically included by eunit.
|
%% that are not automatically included by eunit.
|
||||||
AllBeamFiles = rebar_utils:beams(?EUNIT_DIR),
|
AllBeamFiles = rebar_utils:beams(?TEST_DIR),
|
||||||
{BeamFiles, TestBeamFiles} =
|
{BeamFiles, TestBeamFiles} =
|
||||||
lists:partition(fun(N) -> string:str(N, "_tests.beam") =:= 0 end,
|
lists:partition(fun(N) -> string:str(N, "_tests.beam") =:= 0 end,
|
||||||
AllBeamFiles),
|
AllBeamFiles),
|
||||||
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],
|
Modules = [rebar_utils:beam_to_mod(?TEST_DIR, N) || N <- ModuleBeamFiles],
|
||||||
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
|
||||||
FilteredModules = filter_modules(Config, Modules),
|
FilteredModules = filter_modules(Config, Modules),
|
||||||
|
|
||||||
|
@ -119,13 +117,13 @@ eunit(Config, _AppFile) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
clean(_Config, _File) ->
|
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(),
|
ok = ensure_dirs(),
|
||||||
%% Save code path
|
%% Save code path
|
||||||
CodePath = setup_code_path(),
|
CodePath = setup_code_path(),
|
||||||
{ok, _SrcErls} = eunit_compile(Config),
|
{ok, _SrcErls} = rebar_erlc_compiler:test_compile(Config),
|
||||||
%% Restore code path
|
%% Restore code path
|
||||||
true = code:set_path(CodePath),
|
true = code:set_path(CodePath),
|
||||||
ok.
|
ok.
|
||||||
|
@ -134,57 +132,10 @@ clean(_Config, _File) ->
|
||||||
%% Internal functions
|
%% 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() ->
|
ensure_dirs() ->
|
||||||
%% Make sure ?EUNIT_DIR/ and ebin/ directory exists (append dummy module)
|
%% Make sure ?TEST_DIR/ and ebin/ directory exists (append dummy module)
|
||||||
ok = filelib:ensure_dir(filename:join(eunit_dir(), "dummy")),
|
ok = filelib:ensure_dir(filename:join(rebar_utils:test_dir(), "dummy")),
|
||||||
ok = filelib:ensure_dir(filename:join(ebin_dir(), "dummy")).
|
ok = filelib:ensure_dir(filename:join(rebar_utils:ebin_dir(), "dummy")).
|
||||||
|
|
||||||
setup_code_path() ->
|
setup_code_path() ->
|
||||||
%% Setup code path prior to compilation so that parse_transforms
|
%% 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
|
%% to the END of the code path so that we don't have to jump
|
||||||
%% through hoops to access the .app file
|
%% through hoops to access the .app file
|
||||||
CodePath = code:get_path(),
|
CodePath = code:get_path(),
|
||||||
true = code:add_patha(eunit_dir()),
|
true = code:add_patha(rebar_utils:test_dir()),
|
||||||
true = code:add_pathz(ebin_dir()),
|
true = code:add_pathz(rebar_utils:ebin_dir()),
|
||||||
CodePath.
|
CodePath.
|
||||||
|
|
||||||
eunit_dir() ->
|
|
||||||
filename:join(rebar_utils:get_cwd(), ?EUNIT_DIR).
|
|
||||||
|
|
||||||
ebin_dir() ->
|
|
||||||
filename:join(rebar_utils:get_cwd(), "ebin").
|
|
||||||
|
|
||||||
filter_modules(Config, Modules) ->
|
filter_modules(Config, Modules) ->
|
||||||
RawSuites = rebar_utils:get_deprecated_global(Config, suite, suites,
|
RawSuites = rebar_utils:get_deprecated_global(Config, suite, suites,
|
||||||
[], "soon"),
|
[], "soon"),
|
||||||
|
@ -216,10 +161,10 @@ filter_modules1(Modules, Suites) ->
|
||||||
perform_eunit(Config, FilteredModules) ->
|
perform_eunit(Config, FilteredModules) ->
|
||||||
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 ?TEST_DIR while we run tests so any generated files
|
||||||
%% are created there (versus in the source dir)
|
%% are created there (versus in the source dir)
|
||||||
Cwd = rebar_utils:get_cwd(),
|
Cwd = rebar_utils:get_cwd(),
|
||||||
ok = file:set_cwd(?EUNIT_DIR),
|
ok = file:set_cwd(?TEST_DIR),
|
||||||
|
|
||||||
EunitResult = (catch eunit:test(FilteredModules, EunitOpts)),
|
EunitResult = (catch eunit:test(FilteredModules, EunitOpts)),
|
||||||
|
|
||||||
|
@ -239,51 +184,6 @@ get_eunit_opts(Config) ->
|
||||||
|
|
||||||
BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []).
|
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(Config, BeamFiles, SrcModules) ->
|
||||||
perform_cover(rebar_config:get(Config, cover_enabled, false),
|
perform_cover(rebar_config:get(Config, cover_enabled, false),
|
||||||
Config, BeamFiles, SrcModules).
|
Config, BeamFiles, SrcModules).
|
||||||
|
@ -308,7 +208,7 @@ cover_analyze(Config, FilteredModules, SrcModules) ->
|
||||||
[html])
|
[html])
|
||||||
end, Coverage),
|
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]),
|
?CONSOLE("Cover analysis: ~s\n", [Index]),
|
||||||
|
|
||||||
%% Print coverage report, if configured
|
%% Print coverage report, if configured
|
||||||
|
@ -328,7 +228,7 @@ cover_init(false, _BeamFiles) ->
|
||||||
{ok, not_enabled};
|
{ok, not_enabled};
|
||||||
cover_init(true, BeamFiles) ->
|
cover_init(true, BeamFiles) ->
|
||||||
%% Attempt to start the cover server, then set it's group leader to
|
%% 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
|
%% to stdout. If the cover server is already started we'll reuse that
|
||||||
%% pid.
|
%% pid.
|
||||||
{ok, CoverPid} = case cover:start() of
|
{ok, CoverPid} = case cover:start() of
|
||||||
|
@ -341,7 +241,7 @@ cover_init(true, BeamFiles) ->
|
||||||
end,
|
end,
|
||||||
|
|
||||||
{ok, F} = OkOpen = file:open(
|
{ok, F} = OkOpen = file:open(
|
||||||
filename:join([?EUNIT_DIR, "cover.log"]),
|
filename:join([?TEST_DIR, "cover.log"]),
|
||||||
[write]),
|
[write]),
|
||||||
|
|
||||||
group_leader(F, CoverPid),
|
group_leader(F, CoverPid),
|
||||||
|
@ -416,7 +316,7 @@ align_notcovered_count(Module, Covered, NotCovered, true) ->
|
||||||
{Module, Covered, NotCovered - 1}.
|
{Module, Covered, NotCovered - 1}.
|
||||||
|
|
||||||
cover_write_index(Coverage, SrcModules) ->
|
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"),
|
ok = file:write(F, "<html><head><title>Coverage Summary</title></head>\n"),
|
||||||
IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
|
IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
|
||||||
{SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
|
{SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
|
||||||
|
@ -474,7 +374,7 @@ cover_print_coverage(Coverage) ->
|
||||||
?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]).
|
?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]).
|
||||||
|
|
||||||
cover_file(Module) ->
|
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) ->
|
percentage(0, 0) ->
|
||||||
"not executed";
|
"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,
|
get_deprecated_local/4, get_deprecated_local/5,
|
||||||
delayed_halt/1,
|
delayed_halt/1,
|
||||||
erl_opts/1,
|
erl_opts/1,
|
||||||
src_dirs/1
|
src_dirs/1,
|
||||||
]).
|
test_dir/0,
|
||||||
|
ebin_dir/0]).
|
||||||
|
|
||||||
-include("rebar.hrl").
|
-include("rebar.hrl").
|
||||||
|
|
||||||
|
@ -306,6 +307,12 @@ src_dirs([]) ->
|
||||||
src_dirs(SrcDirs) ->
|
src_dirs(SrcDirs) ->
|
||||||
SrcDirs.
|
SrcDirs.
|
||||||
|
|
||||||
|
test_dir() ->
|
||||||
|
filename:join(rebar_utils:get_cwd(), ?TEST_DIR).
|
||||||
|
|
||||||
|
ebin_dir() ->
|
||||||
|
filename:join(rebar_utils:get_cwd(), "ebin").
|
||||||
|
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
%% Assuming this test is run inside the rebar 'eunit'
|
%% 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(REBAR_SCRIPT, "../rebar").
|
||||||
|
|
||||||
-define(TMP_DIR, "tmp_eunit/").
|
-define(TMP_DIR, "tmp_eunit/").
|
||||||
|
@ -70,7 +70,7 @@ cover_test_() ->
|
||||||
|
|
||||||
{"Only production modules get coverage reports",
|
{"Only production modules get coverage reports",
|
||||||
assert_files_not_in("the temporary eunit directory",
|
assert_files_not_in("the temporary eunit directory",
|
||||||
[".eunit/myapp_mymod_tests.COVER.html"])}]}.
|
[".test/myapp_mymod_tests.COVER.html"])}]}.
|
||||||
|
|
||||||
cover_with_suite_test_() ->
|
cover_with_suite_test_() ->
|
||||||
{"Ensure Cover runs with Tests in a test dir and a test suite",
|
{"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",
|
[{"Cover reports are generated for module",
|
||||||
assert_files_in("the temporary eunit directory",
|
assert_files_in("the temporary eunit directory",
|
||||||
[".eunit/index.html",
|
[".test/index.html",
|
||||||
".eunit/mysuite.COVER.html"])},
|
".test/mysuite.COVER.html"])},
|
||||||
|
|
||||||
{"Only production modules get coverage reports",
|
{"Only production modules get coverage reports",
|
||||||
assert_files_not_in("the temporary eunit directory",
|
assert_files_not_in("the temporary eunit directory",
|
||||||
[".eunit/myapp_app.COVER.html",
|
[".test/myapp_app.COVER.html",
|
||||||
".eunit/myapp_mymod.COVER.html",
|
".test/myapp_mymod.COVER.html",
|
||||||
".eunit/myapp_sup.COVER.html",
|
".test/myapp_sup.COVER.html",
|
||||||
".eunit/myapp_mymod_tests.COVER.html"])}]}.
|
".test/myapp_mymod_tests.COVER.html"])}]}.
|
||||||
|
|
||||||
expected_cover_generated_files() ->
|
expected_cover_generated_files() ->
|
||||||
[".eunit/index.html",
|
[".test/index.html",
|
||||||
".eunit/myapp_app.COVER.html",
|
".test/myapp_app.COVER.html",
|
||||||
".eunit/myapp_mymod.COVER.html",
|
".test/myapp_mymod.COVER.html",
|
||||||
".eunit/myapp_sup.COVER.html"].
|
".test/myapp_sup.COVER.html"].
|
||||||
|
|
||||||
cover_coverage_test_() ->
|
cover_coverage_test_() ->
|
||||||
{"Coverage is accurately calculated",
|
{"Coverage is accurately calculated",
|
||||||
|
@ -246,7 +246,7 @@ assert_files_not_in(_, []) -> [].
|
||||||
|
|
||||||
assert_full_coverage(Mod) ->
|
assert_full_coverage(Mod) ->
|
||||||
fun() ->
|
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"),
|
Result = [X || X <- string:tokens(binary_to_list(F), "\n"),
|
||||||
string:str(X, Mod) =/= 0,
|
string:str(X, Mod) =/= 0,
|
||||||
string:str(X, "100%") =/= 0],
|
string:str(X, "100%") =/= 0],
|
||||||
|
|
Loading…
Reference in a new issue