1%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
2%% ex: ts=4 sw=4 et
3-module(rebar_xref_eunit).
4
5-include_lib("eunit/include/eunit.hrl").
6
7-define(REBAR_SCRIPT, "../rebar").
8
9-define(TMP_DIR, "tmp_xref_eunit/").
10
11xref_test_() ->
12    {"Test the various xref warnings",
13     setup, fun() -> setup_project(false), rebar("compile"), rebar("skip_deps=true xref") end,
14     fun teardown/1,
15     fun(RebarOut) ->
16             [
17              {"Undefined function", ?_assert(string:str(RebarOut,
18                "myapp_somemod:notavailable/1 is undefined function") =/= 0)},
19              {"Undefined function call", ?_assert(string:str(RebarOut,
20                "myapp_othermod:somefunc/0 calls undefined function myapp_somemod:notavailable/1") =/= 0)},
21              {"Deprecated function", ?_assert(string:str(RebarOut,
22                "myapp_mymod:fdeprecated/0 is deprecated function") =/= 0)},
23              {"Deprecated function call", ?_assert(string:str(RebarOut,
24                "myapp_othermod:somefunc/0 calls deprecated function myapp_mymod:fdeprecated/0") =/= 0)},
25              {"Unused local", ?_assert(string:str(RebarOut,
26                "myapp_mymod:localfunc2/0 is unused local function") =/= 0)},
27              {"Unused export 1", ?_assert(string:str(RebarOut,
28                "myapp_behaviour1:behaviour_info/1 is unused export") =/= 0)},
29              {"Unused export 2", ?_assert(string:str(RebarOut,
30                "myapp_behaviour2:behaviour_info/1 is unused export") =/= 0)},
31              {"Unused export 3", ?_assert(string:str(RebarOut,
32                "myapp_mymod:other2/1 is unused export") =/= 0)},
33              {"Unused export 4", ?_assert(string:str(RebarOut,
34                "myapp_othermod:somefunc/0 is unused export") =/= 0)},
35              {"Suppressed behaviour export 1", ?_assert(string:str(RebarOut,
36                "myapp_mymod:bh1_a/1 is unused export") =:= 0)},
37              {"Suppressed behaviour export 2", ?_assert(string:str(RebarOut,
38                "myapp_mymod:bh1_b/1 is unused export") =:= 0)},
39              {"Suppressed behaviour export 3", ?_assert(string:str(RebarOut,
40                "myapp_mymod:bh2_a/1 is unused export") =:= 0)},
41              {"Suppressed behaviour export 4", ?_assert(string:str(RebarOut,
42                "myapp_mymod:bh2_b/1 is unused export") =:= 0)}
43            ]
44     end}.
45
46xref_ignore_test_() ->
47    {"Test the suppression of xref warnings",
48     setup, fun() -> setup_project(ignore_xref), rebar("compile"), rebar("skip_deps=true xref") end,
49     fun teardown/1,
50     fun(RebarOut) ->
51             [
52              {"Undefined function can not be suppressed.", ?_assert(string:str(RebarOut,
53                "myapp_somemod:notavailable/1 is undefined function") =/= 0)},
54              {"Supppressed undefined function call", ?_assert(string:str(RebarOut,
55                "myapp_othermod:somefunc/0 calls undefined function myapp_somemod:notavailable/1") =:= 0)},
56              {"Supppressed deprecated function", ?_assert(string:str(RebarOut,
57                "myapp_mymod:fdeprecated/0 is deprecated function") =:= 0)},
58              {"Supppressed deprecated function call", ?_assert(string:str(RebarOut,
59                "myapp_othermod:somefunc/0 calls deprecated function myapp_mymod:fdeprecated/0") =:= 0)},
60              {"Supppressed unused local", ?_assert(string:str(RebarOut,
61                "myapp_mymod:localfunc2/0 is unused local function") =:= 0)},
62              {"Supppressed unused export 1", ?_assert(string:str(RebarOut,
63                "myapp_behaviour1:behaviour_info/1 is unused export") =:= 0)},
64              {"Supppressed unused export 2", ?_assert(string:str(RebarOut,
65                "myapp_behaviour2:behaviour_info/1 is unused export") =:= 0)},
66              {"Supppressed unused export 3", ?_assert(string:str(RebarOut,
67                "myapp_mymod:other2/1 is unused export") =:= 0)},
68              {"Supppressed unused export 4", ?_assert(string:str(RebarOut,
69                "myapp_othermod:somefunc/0 is unused export") =:= 0)},
70              {"Suppressed behaviour export 1", ?_assert(string:str(RebarOut,
71                "myapp_mymod:bh1_a/1 is unused export") =:= 0)},
72              {"Suppressed behaviour export 2", ?_assert(string:str(RebarOut,
73                "myapp_mymod:bh1_b/1 is unused export") =:= 0)},
74              {"Suppressed behaviour export 3", ?_assert(string:str(RebarOut,
75                "myapp_mymod:bh2_a/1 is unused export") =:= 0)},
76              {"Suppressed behaviour export 4", ?_assert(string:str(RebarOut,
77                "myapp_mymod:bh2_b/1 is unused export") =:= 0)}
78            ]
79
80     end}.
81
82
83%% ====================================================================
84%% Setup and Teardown
85%% ====================================================================
86
87-define(myapp_behaviour1,
88        ["-module(myapp_behaviour1).\n",
89         "-export([behaviour_info/1]).\n"]).
90-define(myapp_behaviour1_body,
91        ["behaviour_info(callbacks) -> [{bh1_a,1},{bh1_b,1}];\n",
92         "behaviour_info(_Other) -> undefined.\n"]).
93-define(myapp_behaviour1_ignorexref,
94        ["-ignore_xref({behaviour_info,1}).\n"]).
95
96-define(myapp_behaviour2,
97        ["-module(myapp_behaviour2).\n",
98         "-export([behaviour_info/1]).\n"]).
99-define(myapp_behaviour2_body,
100        ["behaviour_info(callbacks) -> [{bh2_a,1},{bh2_b,1}];\n",
101         "behaviour_info(_Other) -> undefined.\n"]).
102-define(myapp_behaviour2_ignorexref,
103        ["-ignore_xref({behaviour_info,1}).\n"]).
104
105-define(myapp_mymod,
106        ["-module(myapp_mymod).\n",
107         "-export([bh1_a/1,bh1_b/1,bh2_a/1,bh2_b/1,other1/1,other2/1,fdeprecated/0]).\n",
108         "-behaviour(myapp_behaviour1).\n",     % 2 behaviours
109         "-behaviour(myapp_behaviour2).\n",
110         "-deprecated({fdeprecated,0}).\n"]).      % deprecated function
111-define(myapp_mymod_body,
112        ["bh1_a(A) -> localfunc1(bh1_a, A).\n", % behaviour functions
113         "bh1_b(A) -> localfunc1(bh1_b, A).\n",
114         "bh2_a(A) -> localfunc1(bh2_a, A).\n",
115         "bh2_b(A) -> localfunc1(bh2_b, A).\n",
116         "other1(A) -> localfunc1(other1, A).\n", % regular exported functions
117         "other2(A) -> localfunc1(other2, A).\n",
118         "localfunc1(A, B) -> {A, B}.\n",       % used local
119         "localfunc2() -> ok.\n",               % unused local
120         "fdeprecated() -> ok.\n"              % deprecated function
121         ]).
122-define(myapp_mymod_ignorexref,
123        ["-ignore_xref([{other2,1},{localfunc2,0},{fdeprecated,0}]).\n"]).
124
125
126
127-define(myapp_othermod,
128        ["-module(myapp_othermod).\n",
129         "-export([somefunc/0]).\n"]).
130-define(myapp_othermod_body,
131        ["somefunc() ->\n",
132         "   myapp_mymod:other1(arg),\n",
133         "   myapp_somemod:notavailable(arg),\n",
134         "   myapp_mymod:fdeprecated().\n"
135         ]).
136-define(myapp_othermod_ignorexref,
137        ["-ignore_xref([{myapp_somemod,notavailable,1},{somefunc,0}]).\n",
138         "-ignore_xref({myapp_mymod,fdeprecated,0}).\n"]).
139
140
141-define(myapp_rebarconfig,
142        ["{erl_opts, [debug_info]}.\n",
143         "{xref_checks, [deprecated_function_calls,deprecated_functions,\n",
144         "               undefined_function_calls,undefined_functions,\n",
145         "               exports_not_used,locals_not_used]}.\n"
146         ]).
147
148setup_environment() ->
149    ok = file:make_dir(?TMP_DIR),
150    prepare_rebar_script(),
151    ok = file:set_cwd(?TMP_DIR).
152
153prepare_project() ->
154    setup_environment(),
155    rebar("create-app appid=myapp"),
156    ok = file:make_dir("ebin").
157
158setup_project(ignore_xref) ->
159    prepare_project(),
160    ok = file:write_file("src/myapp_behaviour1.erl", ?myapp_behaviour1 ++ ?myapp_behaviour1_ignorexref ++ ?myapp_behaviour1_body),
161    ok = file:write_file("src/myapp_behaviour2.erl", ?myapp_behaviour2 ++ ?myapp_behaviour2_ignorexref++ ?myapp_behaviour2_body),
162    ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod ++ ?myapp_mymod_ignorexref ++ ?myapp_mymod_body),
163    ok = file:write_file("src/myapp_othermod.erl", ?myapp_othermod ++ ?myapp_othermod_ignorexref ++ ?myapp_othermod_body),
164    ok = file:write_file("rebar.config", ?myapp_rebarconfig);
165
166setup_project(_) ->
167    prepare_project(),
168    ok = file:write_file("src/myapp_behaviour1.erl", ?myapp_behaviour1 ++ ?myapp_behaviour1_body),
169    ok = file:write_file("src/myapp_behaviour2.erl", ?myapp_behaviour2 ++ ?myapp_behaviour2_body),
170    ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod ++ ?myapp_mymod_body),
171    ok = file:write_file("src/myapp_othermod.erl", ?myapp_othermod ++ ?myapp_othermod_body),
172    ok = file:write_file("rebar.config", ?myapp_rebarconfig).
173
174
175teardown(_) ->
176    ok = file:set_cwd(".."),
177    ok = remove_tmp_dir().
178
179remove_tmp_dir() ->
180    ok = rebar_file_utils:rm_rf(?TMP_DIR).
181
182%% ====================================================================
183%% Helper Functions
184%% ====================================================================
185
186prepare_rebar_script() ->
187    Rebar = ?TMP_DIR ++ "rebar",
188    {ok, _} = file:copy(?REBAR_SCRIPT, Rebar),
189    case os:type() of
190        {unix, _} ->
191            [] = os:cmd("chmod u+x " ++ Rebar);
192        {win32, _} ->
193            {ok, _} = file:copy(?REBAR_SCRIPT ++ ".cmd",
194                                ?TMP_DIR ++ "rebar.cmd")
195    end.
196
197rebar(Args) when is_list(Args) ->
198    Out = os:cmd(filename:nativename("./rebar") ++ " " ++ Args),
199    %% ?debugMsg("**** Begin"), ?debugMsg(Out), ?debugMsg("**** End"),
200    Out.
201