basho / rebar
Erlang Build Tools -- Please file bug and feature requests at http://issues.basho.com.
| commit 172: | e2b8d83ce90a |
| parent 171: | e1c6070a85d8 |
| branch: | default |
Refactor eunit and cover
Ian Wilkinson /
theiw
5 months ago
5 months ago
Changed (Δ228 bytes):
raw changeset »
src/rebar_eunit.erl (93 lines added, 106 lines removed)
Up to file-list src/rebar_eunit.erl:
| … | … | @@ -58,7 +58,9 @@ eunit(Config, _File) -> |
58 |
58 |
%% Make sure ?EUNIT_DIR/ directory exists (tack on dummy module) |
59 |
59 |
ok = filelib:ensure_dir(?EUNIT_DIR ++ "/foo"), |
60 |
60 |
|
61 |
%% |
|
61 |
%% Obtain all the test modules for inclusion in the compile stage. |
|
62 |
%% Notice: this could also be achieved with the following rebar.config option: |
|
63 |
%% {eunit_compile_opts, [{src_dirs, ["test"]}]} |
|
62 |
64 |
TestErls = rebar_utils:find_files("test", ".*\\.erl\$"), |
63 |
65 |
|
64 |
66 |
%% Compile erlang code to ?EUNIT_DIR, using a tweaked config |
| … | … | @@ -69,106 +71,82 @@ eunit(Config, _File) -> |
69 |
71 |
%% Build a list of all the .beams in ?EUNIT_DIR -- use this for cover |
70 |
72 |
%% and eunit testing. Normally you can just tell cover and/or eunit to |
71 |
73 |
%% scan the directory for you, but eunit does a code:purge in conjunction |
72 |
%% with that scan and causes any cover compilation info to be lost. So, |
|
73 |
%% we do it by hand. :( |
|
74 |
%% |
|
75 |
%% TODO: Not currently compatible with package modules |
|
76 |
|
|
74 |
%% with that scan and causes any cover compilation info to be lost. |
|
75 |
BeamFiles = rebar_utils:beams(?EUNIT_DIR), |
|
76 |
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- BeamFiles], |
|
77 |
77 |
|
78 |
%% Grab two lists of test and non-test beam files |
|
79 |
{TestBeams, ModuleBeams} = lists:partition(fun(B) -> |
|
80 |
|
|
78 |
cover_init(Config, BeamFiles), |
|
79 |
EunitResult = perform_eunit(Config, Modules), |
|
80 |
perform_cover(Config, BeamFiles), |
|
81 |
81 |
|
82 |
case rebar_config:get_global(suite, undefined) of |
|
83 |
undefined -> |
|
84 |
%% no suite defined, so include all modules |
|
85 |
RealModules = ModuleBeams, |
|
82 |
case EunitResult of |
|
83 |
ok -> |
|
84 |
ok; |
|
85 |
_ -> |
|
86 |
?CONSOLE("One or more eunit tests failed.~n", []), |
|
87 |
?FAIL |
|
88 |
end, |
|
89 |
ok. |
|
86 |
90 |
|
87 |
%% exclude any test modules that have a matching module |
|
88 |
TestModules = [T || T <- TestBeams, |
|
89 |
lists:member(string:left(T, length(T) - 6), RealModules) == false]; |
|
90 |
SuiteName -> |
|
91 |
%% suite defined, so only specify the module that relates to the |
|
92 |
%% suite (if any) |
|
93 |
RealModules = [M || M <- ModuleBeams, SuiteName =:= M], |
|
91 |
clean(_Config, _File) -> |
|
92 |
rebar_file_utils:rm_rf(?EUNIT_DIR). |
|
94 |
93 |
|
95 |
%% only include the test suite if the main module doesn't exist |
|
96 |
TestModules = case length(RealModules) of |
|
97 |
0 -> [T || T <- TestBeams, T =:= SuiteName ++ "_tests"]; |
|
98 |
_ -> [] |
|
99 |
end |
|
100 |
end, |
|
94 |
%% =================================================================== |
|
95 |
%% Internal functions |
|
96 |
%% =================================================================== |
|
101 |
97 |
|
102 |
%% combine the modules and associated test modules into the resulting list |
|
103 |
%% of modules to run tests on. |
|
104 |
Modules = [list_to_atom(M) || M <- RealModules ++ TestModules], |
|
98 |
perform_eunit(Config, Modules) -> |
|
99 |
%% suite defined, so only specify the module that relates to the |
|
100 |
%% suite (if any) |
|
101 |
Suite = rebar_config:get_global(suite, undefined), |
|
102 |
EunitOpts = get_eunit_opts(Config), |
|
105 |
103 |
|
106 |
|
|
104 |
OrigEnv = set_proc_env(), |
|
105 |
EunitResult = perform_eunit(EunitOpts, Modules, Suite), |
|
106 |
restore_proc_env(OrigEnv), |
|
107 |
EunitResult. |
|
107 |
108 |
|
109 |
perform_eunit(EunitOpts, Modules, undefined) -> |
|
110 |
(catch eunit:test(Modules, EunitOpts)); |
|
111 |
perform_eunit(EunitOpts, _Modules, Suite) -> |
|
112 |
(catch eunit:test(list_to_atom(Suite), EunitOpts)). |
|
113 |
||
114 |
set_proc_env() -> |
|
108 |
115 |
%% Save current code path and then prefix ?EUNIT_DIR on it so that our modules |
109 |
116 |
%% are found there |
110 |
|
|
117 |
CodePath = code:get_path(), |
|
111 |
118 |
true = code:add_patha(?EUNIT_DIR), |
112 |
119 |
|
113 |
%% Enable verbose in eunit if so requested.. |
|
114 |
case rebar_config:is_verbose() of |
|
115 |
true -> |
|
116 |
BaseOpts = [verbose]; |
|
117 |
false -> |
|
118 |
BaseOpts = [] |
|
119 |
end, |
|
120 |
||
121 |
%% If cover support is requested, set it up |
|
122 |
case rebar_config:get(Config, cover_enabled, false) of |
|
123 |
true -> |
|
124 |
cover_init(Config); |
|
125 |
_ -> |
|
126 |
ok |
|
127 |
end, |
|
128 |
||
129 |
120 |
%% Move down into ?EUNIT_DIR while we run tests so any generated files |
130 |
121 |
%% are created there (versus in the source dir) |
131 |
122 |
Cwd = rebar_utils:get_cwd(), |
132 |
123 |
file:set_cwd(?EUNIT_DIR), |
124 |
{CodePath, Cwd}. |
|
133 |
125 |
|
134 |
%% Run eunit |
|
135 |
EunitOpts = BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []), |
|
136 |
EunitResult = (catch eunit:test(Modules, EunitOpts)), |
|
137 |
||
126 |
restore_proc_env({CodePath, Cwd}) -> |
|
138 |
127 |
%% Return to original working dir |
139 |
128 |
file:set_cwd(Cwd), |
129 |
%% Restore code path |
|
130 |
true = code:set_path(CodePath). |
|
140 |
131 |
|
141 |
%% Analyze cover modules |
|
142 |
cover_analyze(Config, cover:modules()), |
|
132 |
get_eunit_opts(Config) -> |
|
133 |
%% Enable verbose in eunit if so requested.. |
|
134 |
BaseOpts = case rebar_config:is_verbose() of |
|
135 |
true -> |
|
136 |
[verbose]; |
|
137 |
false -> |
|
138 |
[] |
|
139 |
end, |
|
143 |
140 |
|
144 |
case EunitResult of |
|
145 |
ok -> |
|
146 |
ok; |
|
147 |
_ -> |
|
148 |
?CONSOLE("One or more eunit tests failed.\n", []), |
|
149 |
?FAIL |
|
150 |
end, |
|
151 |
||
152 |
||
153 |
%% Restore code path |
|
154 |
true = code:set_path(InitCodePath), |
|
155 |
ok. |
|
156 |
||
157 |
clean(_Config, _File) -> |
|
158 |
rebar_file_utils:rm_rf(?EUNIT_DIR). |
|
159 |
||
160 |
||
161 |
%% =================================================================== |
|
162 |
%% Internal functions |
|
163 |
%% =================================================================== |
|
141 |
BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []). |
|
164 |
142 |
|
165 |
143 |
eunit_config(Config) -> |
166 |
case is_quickcheck_avail() of |
|
167 |
true -> |
|
168 |
EqcOpts = [{d, 'EQC'}]; |
|
169 |
false -> |
|
170 |
EqcOpts = [] |
|
171 |
end, |
|
144 |
EqcOpts = case is_quickcheck_avail() of |
|
145 |
true -> |
|
146 |
[{d, 'EQC'}]; |
|
147 |
false -> |
|
148 |
[] |
|
149 |
end, |
|
172 |
150 |
|
173 |
151 |
ErlOpts = rebar_config:get_list(Config, erl_opts, []), |
174 |
152 |
EunitOpts = rebar_config:get_list(Config, eunit_compile_opts, []), |
| … | … | @@ -192,36 +170,22 @@ is_quickcheck_avail() -> |
192 |
170 |
IsAvail |
193 |
171 |
end. |
194 |
172 |
|
195 |
cover_init(_Config) -> |
|
196 |
%% Make sure any previous runs of cover don't unduly influence |
|
197 |
cover:reset(), |
|
173 |
perform_cover(Config, BeamFiles) -> |
|
174 |
perform_cover(rebar_config:get(Config, cover_enabled, false), Config, BeamFiles). |
|
198 |
175 |
|
199 |
?INFO("Cover compiling ~s\n", [rebar_utils:get_cwd()]), |
|
200 |
||
201 |
case cover:compile_beam_directory(?EUNIT_DIR) of |
|
202 |
{error, Reason2} -> |
|
203 |
?ERROR("Cover compilation failed: ~p\n", [Reason2]), |
|
204 |
?FAIL; |
|
205 |
Modules -> |
|
206 |
%% It's not an error for cover compilation to fail partially, but we do want |
|
207 |
%% to warn about them |
|
208 |
[?CONSOLE("Cover compilation warning: ~p", [Desc]) || {error, Desc} <- Modules], |
|
209 |
||
210 |
%% Identify the modules that were compiled successfully |
|
211 |
case [ M || {ok, M} <- Modules] of |
|
212 |
[] -> |
|
213 |
%% No modules compiled successfully...fail |
|
214 |
?ERROR("Cover failed to compile any modules; aborting.\n", []), |
|
215 |
?FAIL; |
|
216 |
_ -> |
|
217 |
%% At least one module compiled successfully |
|
218 |
ok |
|
219 |
end |
|
220 |
end. |
|
176 |
perform_cover(false, _Config, _BeamFiles) -> |
|
177 |
ok; |
|
178 |
perform_cover(true, Config, BeamFiles) -> |
|
179 |
perform_cover(Config, BeamFiles, rebar_config:get_global(suite, undefined)); |
|
180 |
perform_cover(Config, BeamFiles, undefined) -> |
|
181 |
cover_analyze(Config, BeamFiles); |
|
182 |
perform_cover(Config, _BeamFiles, Suite) -> |
|
183 |
cover_analyze(Config, [filename:join([?EUNIT_DIR | string:tokens(Suite, ".")]) ++ ".beam"]). |
|
221 |
184 |
|
222 |
185 |
cover_analyze(_Config, []) -> |
223 |
186 |
ok; |
224 |
cover_analyze(_Config, |
|
187 |
cover_analyze(_Config, BeamFiles) -> |
|
188 |
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- BeamFiles], |
|
225 |
189 |
%% Generate coverage info for all the cover-compiled modules |
226 |
190 |
Coverage = [cover_analyze_mod(M) || M <- Modules], |
227 |
191 |
|
| … | … | @@ -234,6 +198,30 @@ cover_analyze(_Config, Modules) -> |
234 |
198 |
Index = filename:join([rebar_utils:get_cwd(), ?EUNIT_DIR, "index.html"]), |
235 |
199 |
?CONSOLE("Cover analysis: ~s\n", [Index]). |
236 |
200 |
|
201 |
cover_init(false, _BeamFiles) -> |
|
202 |
ok; |
|
203 |
cover_init(true, BeamFiles) -> |
|
204 |
%% Make sure any previous runs of cover don't unduly influence |
|
205 |
cover:reset(), |
|
206 |
||
207 |
?INFO("Cover compiling ~s\n", [rebar_utils:get_cwd()]), |
|
208 |
||
209 |
Compiled = [{Beam, cover:compile_beam(Beam)} || Beam <- BeamFiles], |
|
210 |
case [Module || {_, {ok, Module}} <- Compiled] of |
|
211 |
[] -> |
|
212 |
%% No modules compiled successfully...fail |
|
213 |
?ERROR("Cover failed to compile any modules; aborting.~n", []), |
|
214 |
?FAIL; |
|
215 |
_ -> |
|
216 |
%% At least one module compiled successfully |
|
217 |
||
218 |
%% It's not an error for cover compilation to fail partially, but we do want |
|
219 |
%% to warn about them |
|
220 |
[?CONSOLE("Cover compilation warning for ~p: ~p", [Beam, Desc]) || {Beam, {error, Desc}} <- Compiled] |
|
221 |
end, |
|
222 |
ok; |
|
223 |
cover_init(Config, BeamFiles) -> |
|
224 |
cover_init(rebar_config:get(Config, cover_enabled, false), BeamFiles). |
|
237 |
225 |
|
238 |
226 |
cover_analyze_mod(Module) -> |
239 |
227 |
case cover:analyze(Module, coverage, module) of |
| … | … | @@ -268,6 +256,5 @@ cover_write_index(Coverage) -> |
268 |
256 |
cover_file(Module) -> |
269 |
257 |
filename:join([?EUNIT_DIR, atom_to_list(Module) ++ ".COVER.html"]). |
270 |
258 |
|
271 |
||
272 |
259 |
percentage(Cov, NotCov) -> |
273 |
260 |
trunc((Cov / (Cov + NotCov)) * 100). |
