You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
12 KiB

  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. -module(rebar_xref_SUITE).
  4. -export([suite/0,
  5. init_per_suite/1,
  6. end_per_suite/1,
  7. init_per_testcase/2,
  8. end_per_testcase/2,
  9. all/0,
  10. xref_test/1,
  11. xref_ignore_test/1,
  12. xref_dep_hook/1,
  13. xref_undef_behaviour/1]).
  14. -include_lib("common_test/include/ct.hrl").
  15. -include_lib("eunit/include/eunit.hrl").
  16. -include_lib("kernel/include/file.hrl").
  17. %% ===================================================================
  18. %% common_test callbacks
  19. %% ===================================================================
  20. suite() ->
  21. [].
  22. init_per_suite(Config) ->
  23. Config.
  24. end_per_suite(_Config) ->
  25. ok.
  26. init_per_testcase(xref_dep_hook, Config) ->
  27. Src = filename:join([?config(data_dir, Config), "recursive"]),
  28. Dst = filename:join([?config(priv_dir, Config), "recursive"]),
  29. ok = rebar_file_utils:cp_r([Src], Dst),
  30. GlobalDir = filename:join([?config(priv_dir, Config), "cache"]),
  31. State = rebar_state:new([{base_dir, filename:join([Dst, "_build"])}
  32. ,{global_rebar_dir, GlobalDir}
  33. ,{root_dir, Dst}]),
  34. [{apps, Dst}, {state, State} | Config];
  35. init_per_testcase(Case, Config) ->
  36. UpdConfig = rebar_test_utils:init_rebar_state(Config),
  37. AppDir = ?config(apps, UpdConfig),
  38. file:set_cwd(AppDir),
  39. Name = rebar_test_utils:create_random_name("xrefapp_"),
  40. Vsn = rebar_test_utils:create_random_vsn(),
  41. rebar_test_utils:create_empty_app(AppDir, Name, Vsn, [kernel, stdlib]),
  42. AppModules = [behaviour1, behaviour2, mymod, othermod, ignoremod, ignoremod2],
  43. [write_src_file(AppDir, Name, Module, ignore_xref(Case)) || Module <- AppModules],
  44. IgnoreMod = list_to_atom(Name ++ "_" ++ "ignoremod"),
  45. RebarConfig = [{erl_opts, [debug_info]},
  46. {xref_ignores, [IgnoreMod]},
  47. {xref_checks, [deprecated_function_calls,deprecated_functions,
  48. undefined_function_calls,undefined_functions,
  49. exports_not_used,locals_not_used]}],
  50. [{app_name, Name},
  51. {rebar_config, RebarConfig} | UpdConfig].
  52. end_per_testcase(_, _Config) ->
  53. ok.
  54. all() ->
  55. [xref_test, xref_ignore_test, xref_dep_hook, xref_undef_behaviour].
  56. %% ===================================================================
  57. %% Test cases
  58. %% ===================================================================
  59. xref_test(Config) ->
  60. AppDir = ?config(apps, Config),
  61. State = ?config(state, Config),
  62. Name = ?config(app_name, Config),
  63. RebarConfig = ?config(rebar_config, Config),
  64. Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
  65. verify_results(xref_test, Name, Result).
  66. xref_ignore_test(Config) ->
  67. AppDir = ?config(apps, Config),
  68. State = ?config(state, Config),
  69. Name = ?config(app_name, Config),
  70. RebarConfig = ?config(rebar_config, Config),
  71. Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
  72. verify_results(xref_ignore_test, Name, Result).
  73. xref_dep_hook(Config) ->
  74. rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, []}).
  75. xref_undef_behaviour(Config) ->
  76. AppDir = ?config(apps, Config),
  77. State = ?config(state, Config),
  78. Name = ?config(app_name, Config),
  79. RebarConfig = ?config(rebar_config, Config),
  80. %% delete one of the behaviours, which should create new warnings
  81. delete_src_file(AppDir, Name, behaviour1),
  82. %% just ensure this does not crash
  83. Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
  84. verify_results(xref_undef_behaviour, Name, Result).
  85. %% ===================================================================
  86. %% Helper functions
  87. %% ===================================================================
  88. ignore_xref(xref_ignore_test) ->
  89. true;
  90. ignore_xref(_) ->
  91. false.
  92. verify_results(TestCase, AppName, Results) ->
  93. {error, {rebar_prv_xref,
  94. {xref_issues, XrefResults, QueryResults}}} = Results,
  95. verify_test_results(TestCase, AppName, XrefResults, QueryResults).
  96. verify_test_results(xref_test, AppName, XrefResults, _QueryResults) ->
  97. AppModules = ["behaviour1", "behaviour2", "mymod", "othermod", "somemod"],
  98. [Behaviour1Mod, Behaviour2Mod, MyMod, OtherMod, SomeMod] =
  99. [list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
  100. UndefFuns = proplists:get_value(undefined_functions, XrefResults),
  101. UndefFunCalls = proplists:get_value(undefined_function_calls, XrefResults),
  102. LocalsNotUsed = proplists:get_value(locals_not_used, XrefResults),
  103. ExportsNotUsed = proplists:get_value(exports_not_used, XrefResults),
  104. DeprecatedFuns = proplists:get_value(deprecated_functions, XrefResults),
  105. DeprecatedFunCalls = proplists:get_value(deprecated_function_calls, XrefResults),
  106. ?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
  107. ?assert(lists:member({{OtherMod, somefunc, 0}, {SomeMod, notavailable, 1}},
  108. UndefFunCalls)),
  109. ?assert(lists:member({MyMod, fdeprecated, 0}, DeprecatedFuns)),
  110. ?assert(lists:member({{OtherMod, somefunc, 0}, {MyMod, fdeprecated, 0}},
  111. DeprecatedFunCalls)),
  112. ?assert(lists:member({MyMod, localfunc2, 0}, LocalsNotUsed)),
  113. ?assert(lists:member({Behaviour1Mod, behaviour_info, 1}, ExportsNotUsed)),
  114. ?assert(lists:member({Behaviour2Mod, behaviour_info, 1}, ExportsNotUsed)),
  115. ?assert(lists:member({MyMod, other2, 1}, ExportsNotUsed)),
  116. ?assert(lists:member({OtherMod, somefunc, 0}, ExportsNotUsed)),
  117. ?assertNot(lists:member({MyMod, bh1_a, 1}, ExportsNotUsed)),
  118. ?assertNot(lists:member({MyMod, bh1_b, 1}, ExportsNotUsed)),
  119. ?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)),
  120. ?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)),
  121. ok;
  122. verify_test_results(xref_undef_behaviour, AppName, XrefResults, _QueryResults) ->
  123. AppModules = ["behaviour2", "mymod", "othermod", "somemod"],
  124. [Behaviour2Mod, MyMod, OtherMod, SomeMod] =
  125. [list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
  126. UndefFuns = proplists:get_value(undefined_functions, XrefResults),
  127. UndefFunCalls = proplists:get_value(undefined_function_calls, XrefResults),
  128. LocalsNotUsed = proplists:get_value(locals_not_used, XrefResults),
  129. ExportsNotUsed = proplists:get_value(exports_not_used, XrefResults),
  130. DeprecatedFuns = proplists:get_value(deprecated_functions, XrefResults),
  131. DeprecatedFunCalls = proplists:get_value(deprecated_function_calls, XrefResults),
  132. ?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
  133. ?assert(lists:member({{OtherMod, somefunc, 0}, {SomeMod, notavailable, 1}},
  134. UndefFunCalls)),
  135. ?assert(lists:member({MyMod, fdeprecated, 0}, DeprecatedFuns)),
  136. ?assert(lists:member({{OtherMod, somefunc, 0}, {MyMod, fdeprecated, 0}},
  137. DeprecatedFunCalls)),
  138. ?assert(lists:member({MyMod, localfunc2, 0}, LocalsNotUsed)),
  139. ?assert(lists:member({Behaviour2Mod, behaviour_info, 1}, ExportsNotUsed)),
  140. ?assert(lists:member({MyMod, other2, 1}, ExportsNotUsed)),
  141. ?assert(lists:member({OtherMod, somefunc, 0}, ExportsNotUsed)),
  142. ?assert(lists:member({MyMod, bh1_a, 1}, ExportsNotUsed)),
  143. ?assert(lists:member({MyMod, bh1_b, 1}, ExportsNotUsed)),
  144. ?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)),
  145. ?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)),
  146. ok;
  147. verify_test_results(xref_ignore_test, AppName, XrefResults, _QueryResults) ->
  148. AppModules = ["behaviour1", "behaviour2", "mymod", "othermod", "somemod",
  149. "ignoremod", "ignoremod2"],
  150. [_Behaviour1Mod, _Behaviour2Mod, _MyMod, _OtherMod, SomeMod, IgnoreMod, IgnoreMod2] =
  151. [list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
  152. UndefFuns = proplists:get_value(undefined_functions, XrefResults),
  153. ?assertNot(lists:keymember(undefined_function_calls, 1, XrefResults)),
  154. ?assertNot(lists:keymember(locals_not_used, 1, XrefResults)),
  155. ?assertNot(lists:keymember(exports_not_used, 1, XrefResults)),
  156. ?assertNot(lists:keymember(deprecated_functions, 1, XrefResults)),
  157. ?assertNot(lists:keymember(deprecated_function_calls, 1, XrefResults)),
  158. ?assertNot(lists:member({IgnoreMod, notavailable, 1}, UndefFuns)),
  159. ?assertNot(lists:member({IgnoreMod2, notavailable, 1}, UndefFuns)),
  160. ?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
  161. ok.
  162. write_src_file(Dir, AppName, Module, IgnoreXref) ->
  163. Erl = filename:join([Dir, "src", module_name(AppName, Module)]),
  164. ok = filelib:ensure_dir(Erl),
  165. ok = ec_file:write(Erl, get_module_body(Module, AppName, IgnoreXref)).
  166. delete_src_file(Dir, AppName, Module) ->
  167. Erl = filename:join([Dir, "src", module_name(AppName, Module)]),
  168. ok = file:delete(Erl).
  169. module_name(AppName, Module) ->
  170. lists:flatten([AppName, "_", atom_to_list(Module), ".erl"]).
  171. get_module_body(behaviour1, AppName, IgnoreXref) ->
  172. ["-module(", AppName, "_behaviour1).\n",
  173. "-export([behaviour_info/1]).\n",
  174. ["-ignore_xref([ignoremod,{behaviour_info,1}]).\n"
  175. || X <- [IgnoreXref], X =:= true],
  176. "behaviour_info(callbacks) -> [{bh1_a,1},{bh1_b,1}];\n",
  177. "behaviour_info(_Other) -> undefined.\n"];
  178. get_module_body(behaviour2, AppName, IgnoreXref) ->
  179. ["-module(", AppName, "_behaviour2).\n",
  180. "-export([behaviour_info/1]).\n",
  181. ["-ignore_xref({behaviour_info,1}).\n"
  182. || X <- [IgnoreXref], X =:= true],
  183. "behaviour_info(callbacks) -> [{bh2_a,1},{bh2_b,1}];\n",
  184. "behaviour_info(_Other) -> undefined.\n"];
  185. get_module_body(mymod, AppName, IgnoreXref) ->
  186. ["-module(", AppName, "_mymod).\n",
  187. "-export([bh1_a/1,bh1_b/1,bh2_a/1,bh2_b/1,"
  188. "other1/1,other2/1,fdeprecated/0]).\n",
  189. ["-ignore_xref([{other2,1},{localfunc2,0},{fdeprecated,0}]).\n"
  190. || X <- [IgnoreXref], X =:= true],
  191. "-behaviour(", AppName, "_behaviour1).\n", % 2 behaviours
  192. "-behavior(", AppName, "_behaviour2).\n",
  193. "-deprecated({fdeprecated,0}).\n", % deprecated function
  194. "bh1_a(A) -> localfunc1(bh1_a, A).\n", % behaviour functions
  195. "bh1_b(A) -> localfunc1(bh1_b, A).\n",
  196. "bh2_a(A) -> localfunc1(bh2_a, A).\n",
  197. "bh2_b(A) -> localfunc1(bh2_b, A).\n",
  198. "other1(A) -> localfunc1(other1, A).\n", % regular exported functions
  199. "other2(A) -> localfunc1(other2, A).\n",
  200. "localfunc1(A, B) -> {A, B}.\n", % used local
  201. "localfunc2() -> ok.\n", % unused local
  202. "fdeprecated() -> ok.\n" % deprecated function
  203. ];
  204. get_module_body(ignoremod, AppName, IgnoreXref) ->
  205. ["-module(", AppName, "_ignoremod).\n",
  206. "-export([]).\n",
  207. [["-ignore_xref(", AppName, "_ignoremod).\n"]
  208. || X <- [IgnoreXref], X =:= true],
  209. "localfunc1(A, B) -> {A, B}.\n", % used local
  210. "localfunc2() -> ok.\n", % unused local
  211. "fdeprecated() -> ok.\n" % deprecated function
  212. ];
  213. get_module_body(ignoremod2, AppName, IgnoreXref) ->
  214. ["-module(", AppName, "_ignoremod2).\n",
  215. "-export([]).\n",
  216. [["-ignore_xref(", AppName, "_ignoremod2).\n"]
  217. || X <- [IgnoreXref], X =:= true],
  218. "localfunc1(A, B) -> {A, B}.\n", % used local
  219. "localfunc2() -> ok.\n", % unused local
  220. "fdeprecated() -> ok.\n" % deprecated function
  221. ];
  222. get_module_body(othermod, AppName, IgnoreXref) ->
  223. ["-module(", AppName, "_othermod).\n",
  224. "-export([somefunc/0]).\n",
  225. [["-ignore_xref([{", AppName, "_somemod,notavailable,1},{somefunc,0}]).\n",
  226. "-ignore_xref({", AppName, "_mymod,fdeprecated,0}).\n"]
  227. || X <- [IgnoreXref], X =:= true],
  228. "somefunc() ->\n",
  229. " ", AppName, "_mymod:other1(arg),\n",
  230. " ", AppName, "_somemod:notavailable(arg),\n",
  231. " ", AppName, "_mymod:fdeprecated(),\n",
  232. " ", AppName, "_ignoremod:notavailable().\n"].