|
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
|
|
%% ex: ts=4 sw=4 et
|
|
-module(rebar_xref_SUITE).
|
|
|
|
-export([suite/0,
|
|
init_per_suite/1,
|
|
end_per_suite/1,
|
|
init_per_testcase/2,
|
|
end_per_testcase/2,
|
|
all/0,
|
|
xref_test/1,
|
|
xref_ignore_test/1,
|
|
xref_dep_hook/1,
|
|
xref_undef_behaviour/1]).
|
|
|
|
-include_lib("common_test/include/ct.hrl").
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include_lib("kernel/include/file.hrl").
|
|
|
|
%% ===================================================================
|
|
%% common_test callbacks
|
|
%% ===================================================================
|
|
|
|
suite() ->
|
|
[].
|
|
|
|
init_per_suite(Config) ->
|
|
Config.
|
|
|
|
end_per_suite(_Config) ->
|
|
ok.
|
|
|
|
init_per_testcase(xref_dep_hook, Config) ->
|
|
Src = filename:join([?config(data_dir, Config), "recursive"]),
|
|
Dst = filename:join([?config(priv_dir, Config), "recursive"]),
|
|
ok = rebar_file_utils:cp_r([Src], Dst),
|
|
GlobalDir = filename:join([?config(priv_dir, Config), "cache"]),
|
|
State = rebar_state:new([{base_dir, filename:join([Dst, "_build"])}
|
|
,{global_rebar_dir, GlobalDir}
|
|
,{root_dir, Dst}]),
|
|
[{apps, Dst}, {state, State} | Config];
|
|
init_per_testcase(Case, Config) ->
|
|
UpdConfig = rebar_test_utils:init_rebar_state(Config),
|
|
AppDir = ?config(apps, UpdConfig),
|
|
file:set_cwd(AppDir),
|
|
Name = rebar_test_utils:create_random_name("xrefapp_"),
|
|
Vsn = rebar_test_utils:create_random_vsn(),
|
|
rebar_test_utils:create_empty_app(AppDir, Name, Vsn, [kernel, stdlib]),
|
|
AppModules = [behaviour1, behaviour2, mymod, othermod, ignoremod, ignoremod2],
|
|
[write_src_file(AppDir, Name, Module, ignore_xref(Case)) || Module <- AppModules],
|
|
IgnoreMod = list_to_atom(Name ++ "_" ++ "ignoremod"),
|
|
RebarConfig = [{erl_opts, [debug_info]},
|
|
{xref_ignores, [IgnoreMod]},
|
|
{xref_checks, [deprecated_function_calls,deprecated_functions,
|
|
undefined_function_calls,undefined_functions,
|
|
exports_not_used,locals_not_used]}],
|
|
[{app_name, Name},
|
|
{rebar_config, RebarConfig} | UpdConfig].
|
|
|
|
end_per_testcase(_, _Config) ->
|
|
ok.
|
|
|
|
all() ->
|
|
[xref_test, xref_ignore_test, xref_dep_hook, xref_undef_behaviour].
|
|
|
|
%% ===================================================================
|
|
%% Test cases
|
|
%% ===================================================================
|
|
|
|
xref_test(Config) ->
|
|
AppDir = ?config(apps, Config),
|
|
State = ?config(state, Config),
|
|
Name = ?config(app_name, Config),
|
|
RebarConfig = ?config(rebar_config, Config),
|
|
Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
|
|
verify_results(xref_test, Name, Result).
|
|
|
|
xref_ignore_test(Config) ->
|
|
AppDir = ?config(apps, Config),
|
|
State = ?config(state, Config),
|
|
Name = ?config(app_name, Config),
|
|
RebarConfig = ?config(rebar_config, Config),
|
|
Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
|
|
verify_results(xref_ignore_test, Name, Result).
|
|
|
|
xref_dep_hook(Config) ->
|
|
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, []}).
|
|
|
|
xref_undef_behaviour(Config) ->
|
|
AppDir = ?config(apps, Config),
|
|
State = ?config(state, Config),
|
|
Name = ?config(app_name, Config),
|
|
RebarConfig = ?config(rebar_config, Config),
|
|
%% delete one of the behaviours, which should create new warnings
|
|
delete_src_file(AppDir, Name, behaviour1),
|
|
%% just ensure this does not crash
|
|
Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
|
|
verify_results(xref_undef_behaviour, Name, Result).
|
|
|
|
%% ===================================================================
|
|
%% Helper functions
|
|
%% ===================================================================
|
|
|
|
ignore_xref(xref_ignore_test) ->
|
|
true;
|
|
ignore_xref(_) ->
|
|
false.
|
|
|
|
verify_results(TestCase, AppName, Results) ->
|
|
{error, {rebar_prv_xref,
|
|
{xref_issues, XrefResults, QueryResults}}} = Results,
|
|
verify_test_results(TestCase, AppName, XrefResults, QueryResults).
|
|
|
|
verify_test_results(xref_test, AppName, XrefResults, _QueryResults) ->
|
|
AppModules = ["behaviour1", "behaviour2", "mymod", "othermod", "somemod"],
|
|
[Behaviour1Mod, Behaviour2Mod, MyMod, OtherMod, SomeMod] =
|
|
[list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
|
|
UndefFuns = proplists:get_value(undefined_functions, XrefResults),
|
|
UndefFunCalls = proplists:get_value(undefined_function_calls, XrefResults),
|
|
LocalsNotUsed = proplists:get_value(locals_not_used, XrefResults),
|
|
ExportsNotUsed = proplists:get_value(exports_not_used, XrefResults),
|
|
DeprecatedFuns = proplists:get_value(deprecated_functions, XrefResults),
|
|
DeprecatedFunCalls = proplists:get_value(deprecated_function_calls, XrefResults),
|
|
?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
|
|
?assert(lists:member({{OtherMod, somefunc, 0}, {SomeMod, notavailable, 1}},
|
|
UndefFunCalls)),
|
|
?assert(lists:member({MyMod, fdeprecated, 0}, DeprecatedFuns)),
|
|
?assert(lists:member({{OtherMod, somefunc, 0}, {MyMod, fdeprecated, 0}},
|
|
DeprecatedFunCalls)),
|
|
?assert(lists:member({MyMod, localfunc2, 0}, LocalsNotUsed)),
|
|
?assert(lists:member({Behaviour1Mod, behaviour_info, 1}, ExportsNotUsed)),
|
|
?assert(lists:member({Behaviour2Mod, behaviour_info, 1}, ExportsNotUsed)),
|
|
?assert(lists:member({MyMod, other2, 1}, ExportsNotUsed)),
|
|
?assert(lists:member({OtherMod, somefunc, 0}, ExportsNotUsed)),
|
|
?assertNot(lists:member({MyMod, bh1_a, 1}, ExportsNotUsed)),
|
|
?assertNot(lists:member({MyMod, bh1_b, 1}, ExportsNotUsed)),
|
|
?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)),
|
|
?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)),
|
|
ok;
|
|
verify_test_results(xref_undef_behaviour, AppName, XrefResults, _QueryResults) ->
|
|
AppModules = ["behaviour2", "mymod", "othermod", "somemod"],
|
|
[Behaviour2Mod, MyMod, OtherMod, SomeMod] =
|
|
[list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
|
|
UndefFuns = proplists:get_value(undefined_functions, XrefResults),
|
|
UndefFunCalls = proplists:get_value(undefined_function_calls, XrefResults),
|
|
LocalsNotUsed = proplists:get_value(locals_not_used, XrefResults),
|
|
ExportsNotUsed = proplists:get_value(exports_not_used, XrefResults),
|
|
DeprecatedFuns = proplists:get_value(deprecated_functions, XrefResults),
|
|
DeprecatedFunCalls = proplists:get_value(deprecated_function_calls, XrefResults),
|
|
?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
|
|
?assert(lists:member({{OtherMod, somefunc, 0}, {SomeMod, notavailable, 1}},
|
|
UndefFunCalls)),
|
|
?assert(lists:member({MyMod, fdeprecated, 0}, DeprecatedFuns)),
|
|
?assert(lists:member({{OtherMod, somefunc, 0}, {MyMod, fdeprecated, 0}},
|
|
DeprecatedFunCalls)),
|
|
?assert(lists:member({MyMod, localfunc2, 0}, LocalsNotUsed)),
|
|
?assert(lists:member({Behaviour2Mod, behaviour_info, 1}, ExportsNotUsed)),
|
|
?assert(lists:member({MyMod, other2, 1}, ExportsNotUsed)),
|
|
?assert(lists:member({OtherMod, somefunc, 0}, ExportsNotUsed)),
|
|
?assert(lists:member({MyMod, bh1_a, 1}, ExportsNotUsed)),
|
|
?assert(lists:member({MyMod, bh1_b, 1}, ExportsNotUsed)),
|
|
?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)),
|
|
?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)),
|
|
ok;
|
|
verify_test_results(xref_ignore_test, AppName, XrefResults, _QueryResults) ->
|
|
AppModules = ["behaviour1", "behaviour2", "mymod", "othermod", "somemod",
|
|
"ignoremod", "ignoremod2"],
|
|
[_Behaviour1Mod, _Behaviour2Mod, _MyMod, _OtherMod, SomeMod, IgnoreMod, IgnoreMod2] =
|
|
[list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
|
|
UndefFuns = proplists:get_value(undefined_functions, XrefResults),
|
|
?assertNot(lists:keymember(undefined_function_calls, 1, XrefResults)),
|
|
?assertNot(lists:keymember(locals_not_used, 1, XrefResults)),
|
|
?assertNot(lists:keymember(exports_not_used, 1, XrefResults)),
|
|
?assertNot(lists:keymember(deprecated_functions, 1, XrefResults)),
|
|
?assertNot(lists:keymember(deprecated_function_calls, 1, XrefResults)),
|
|
?assertNot(lists:member({IgnoreMod, notavailable, 1}, UndefFuns)),
|
|
?assertNot(lists:member({IgnoreMod2, notavailable, 1}, UndefFuns)),
|
|
?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
|
|
ok.
|
|
|
|
write_src_file(Dir, AppName, Module, IgnoreXref) ->
|
|
Erl = filename:join([Dir, "src", module_name(AppName, Module)]),
|
|
ok = filelib:ensure_dir(Erl),
|
|
ok = ec_file:write(Erl, get_module_body(Module, AppName, IgnoreXref)).
|
|
|
|
delete_src_file(Dir, AppName, Module) ->
|
|
Erl = filename:join([Dir, "src", module_name(AppName, Module)]),
|
|
ok = file:delete(Erl).
|
|
|
|
module_name(AppName, Module) ->
|
|
lists:flatten([AppName, "_", atom_to_list(Module), ".erl"]).
|
|
|
|
get_module_body(behaviour1, AppName, IgnoreXref) ->
|
|
["-module(", AppName, "_behaviour1).\n",
|
|
"-export([behaviour_info/1]).\n",
|
|
["-ignore_xref([ignoremod,{behaviour_info,1}]).\n"
|
|
|| X <- [IgnoreXref], X =:= true],
|
|
"behaviour_info(callbacks) -> [{bh1_a,1},{bh1_b,1}];\n",
|
|
"behaviour_info(_Other) -> undefined.\n"];
|
|
get_module_body(behaviour2, AppName, IgnoreXref) ->
|
|
["-module(", AppName, "_behaviour2).\n",
|
|
"-export([behaviour_info/1]).\n",
|
|
["-ignore_xref({behaviour_info,1}).\n"
|
|
|| X <- [IgnoreXref], X =:= true],
|
|
"behaviour_info(callbacks) -> [{bh2_a,1},{bh2_b,1}];\n",
|
|
"behaviour_info(_Other) -> undefined.\n"];
|
|
get_module_body(mymod, AppName, IgnoreXref) ->
|
|
["-module(", AppName, "_mymod).\n",
|
|
"-export([bh1_a/1,bh1_b/1,bh2_a/1,bh2_b/1,"
|
|
"other1/1,other2/1,fdeprecated/0]).\n",
|
|
["-ignore_xref([{other2,1},{localfunc2,0},{fdeprecated,0}]).\n"
|
|
|| X <- [IgnoreXref], X =:= true],
|
|
"-behaviour(", AppName, "_behaviour1).\n", % 2 behaviours
|
|
"-behavior(", AppName, "_behaviour2).\n",
|
|
"-deprecated({fdeprecated,0}).\n", % deprecated function
|
|
"bh1_a(A) -> localfunc1(bh1_a, A).\n", % behaviour functions
|
|
"bh1_b(A) -> localfunc1(bh1_b, A).\n",
|
|
"bh2_a(A) -> localfunc1(bh2_a, A).\n",
|
|
"bh2_b(A) -> localfunc1(bh2_b, A).\n",
|
|
"other1(A) -> localfunc1(other1, A).\n", % regular exported functions
|
|
"other2(A) -> localfunc1(other2, A).\n",
|
|
"localfunc1(A, B) -> {A, B}.\n", % used local
|
|
"localfunc2() -> ok.\n", % unused local
|
|
"fdeprecated() -> ok.\n" % deprecated function
|
|
];
|
|
get_module_body(ignoremod, AppName, IgnoreXref) ->
|
|
["-module(", AppName, "_ignoremod).\n",
|
|
"-export([]).\n",
|
|
[["-ignore_xref(", AppName, "_ignoremod).\n"]
|
|
|| X <- [IgnoreXref], X =:= true],
|
|
"localfunc1(A, B) -> {A, B}.\n", % used local
|
|
"localfunc2() -> ok.\n", % unused local
|
|
"fdeprecated() -> ok.\n" % deprecated function
|
|
|
|
];
|
|
get_module_body(ignoremod2, AppName, IgnoreXref) ->
|
|
["-module(", AppName, "_ignoremod2).\n",
|
|
"-export([]).\n",
|
|
[["-ignore_xref(", AppName, "_ignoremod2).\n"]
|
|
|| X <- [IgnoreXref], X =:= true],
|
|
"localfunc1(A, B) -> {A, B}.\n", % used local
|
|
"localfunc2() -> ok.\n", % unused local
|
|
"fdeprecated() -> ok.\n" % deprecated function
|
|
|
|
];
|
|
get_module_body(othermod, AppName, IgnoreXref) ->
|
|
["-module(", AppName, "_othermod).\n",
|
|
"-export([somefunc/0]).\n",
|
|
[["-ignore_xref([{", AppName, "_somemod,notavailable,1},{somefunc,0}]).\n",
|
|
"-ignore_xref({", AppName, "_mymod,fdeprecated,0}).\n"]
|
|
|| X <- [IgnoreXref], X =:= true],
|
|
"somefunc() ->\n",
|
|
" ", AppName, "_mymod:other1(arg),\n",
|
|
" ", AppName, "_somemod:notavailable(arg),\n",
|
|
" ", AppName, "_mymod:fdeprecated(),\n",
|
|
" ", AppName, "_ignoremod:notavailable().\n"].
|