basho / rebar
Erlang Build Tools -- Please file bug and feature requests at http://issues.basho.com.
| commit 183: | f96f3921c732 |
| parent 175: | bf0185304d32 |
| branch: | default |
Fixed 3 bugs in rebar_eunit. Added EUnit tests to capture them.
1. When running the eunit command with the convention of putting
tests in "*_tests" modules, eunit would run those tests twice. This
is because: 1) eunit:test/1 will naturally look for foo's tests both
in foo, and in foo_tests, and 2) eunit:test/1 was being folded over
all project modules. The fix is to filter "*_tests" modules from the
list passed to eunit:test/1.
2. When running the eunit command with cover enabled and tests in a
'test' directory, cover would error because it couldn't find the
source code for those tests. This is because cover:analyze/3 will
only find module source in "." and "../src". This is hard-coded in
cover :-(. Since cover shouldn't be calculating code coverage on test
code anyway, the fix is to not fold cover:analyze/3 over
non-production code.
3. When running the eunit command with cover enabled and a test suite
defined, cover would only attempt to calculate coverage on the the
test suite itself. This was because only the suite was passed to
cover:analyze/3. The fix is to fold cover:analyze/3 over all the
production code, filtering out the suite module if it is defined.
Changed (Δ8.3 KB):
raw changeset »
src/rebar_eunit.erl (11 lines added, 10 lines removed)
test/rebar_eunit_tests.erl (229 lines added, 0 lines removed)
Up to file-list src/rebar_eunit.erl:
| … | … | @@ -72,12 +72,15 @@ eunit(Config, _File) -> |
72 |
72 |
%% and eunit testing. Normally you can just tell cover and/or eunit to |
73 |
73 |
%% scan the directory for you, but eunit does a code:purge in conjunction |
74 |
74 |
%% with that scan and causes any cover compilation info to be lost. |
75 |
|
|
75 |
%% Filter out "*_tests" modules so eunit won't doubly run them and |
|
76 |
%% so cover only calculates coverage on production code. |
|
77 |
BeamFiles = [N || N <- rebar_utils:beams(?EUNIT_DIR), |
|
78 |
string:str(N, "_tests.beam") =:= 0], |
|
76 |
79 |
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- BeamFiles], |
77 |
80 |
|
78 |
81 |
cover_init(Config, BeamFiles), |
79 |
82 |
EunitResult = perform_eunit(Config, Modules), |
80 |
perform_cover(Config, |
|
83 |
perform_cover(Config, Modules), |
|
81 |
84 |
|
82 |
85 |
case EunitResult of |
83 |
86 |
ok -> |
| … | … | @@ -176,18 +179,16 @@ perform_cover(Config, BeamFiles) -> |
176 |
179 |
perform_cover(false, _Config, _BeamFiles) -> |
177 |
180 |
ok; |
178 |
181 |
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, |
|
182 |
cover_analyze(Config, BeamFiles). |
|
184 |
183 |
|
185 |
184 |
cover_analyze(_Config, []) -> |
186 |
185 |
ok; |
187 |
cover_analyze(_Config, BeamFiles) -> |
|
188 |
Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- BeamFiles], |
|
186 |
cover_analyze(_Config, Modules) -> |
|
187 |
Suite = list_to_atom(rebar_config:get_global(suite, "")), |
|
188 |
FilteredModules = [M || M <- Modules, M =/= Suite], |
|
189 |
||
189 |
190 |
%% Generate coverage info for all the cover-compiled modules |
190 |
Coverage = [cover_analyze_mod(M) || M <- |
|
191 |
Coverage = [cover_analyze_mod(M) || M <- FilteredModules], |
|
191 |
192 |
|
192 |
193 |
%% Write index of coverage info |
193 |
194 |
cover_write_index(lists:sort(Coverage)), |
Up to file-list test/rebar_eunit_tests.erl:
1 |
%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- |
|
2 |
%% ex: ts=4 sw=4 et |
|
3 |
%% ------------------------------------------------------------------- |
|
4 |
%% |
|
5 |
%% rebar: Erlang Build Tools |
|
6 |
%% |
|
7 |
%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) |
|
8 |
%% |
|
9 |
%% Permission is hereby granted, free of charge, to any person obtaining a copy |
|
10 |
%% of this software and associated documentation files (the "Software"), to deal |
|
11 |
%% in the Software without restriction, including without limitation the rights |
|
12 |
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
13 |
%% copies of the Software, and to permit persons to whom the Software is |
|
14 |
%% furnished to do so, subject to the following conditions: |
|
15 |
%% |
|
16 |
%% The above copyright notice and this permission notice shall be included in |
|
17 |
%% all copies or substantial portions of the Software. |
|
18 |
%% |
|
19 |
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
20 |
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
21 |
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
22 |
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
23 |
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
24 |
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
25 |
%% THE SOFTWARE. |
|
26 |
%% ------------------------------------------------------------------- |
|
27 |
%% @author Chris Bernard <cebernard@gmail.com> |
|
28 |
%% @doc This tests functionality provided by the rebar command 'eunit'. |
|
29 |
%% @copyright 2009, 2010 Dave Smith |
|
30 |
%% ------------------------------------------------------------------- |
|
31 |
-module(rebar_eunit_tests). |
|
32 |
||
33 |
-compile(export_all). |
|
34 |
||
35 |
-include_lib("eunit/include/eunit.hrl"). |
|
36 |
||
37 |
%% Assuming this test is run inside the rebar 'eunit' |
|
38 |
%% command, the current working directory will be '.eunit' |
|
39 |
-define(REBAR_SCRIPT, "../rebar"). |
|
40 |
||
41 |
-define(TMP_DIR, "tmp_eunit/"). |
|
42 |
||
43 |
%% ==================================================================== |
|
44 |
%% Rebar EUnit and Cover Tests |
|
45 |
%% ==================================================================== |
|
46 |
||
47 |
eunit_test_() -> |
|
48 |
{"Ensure EUnit runs with tests in a 'test' dir and no defined suite", |
|
49 |
setup, fun() -> setup_basic_project(), rebar("-v eunit") end, |
|
50 |
fun teardown/1, |
|
51 |
fun(RebarOut) -> |
|
52 |
[{"Tests in 'test' directory are found and run", |
|
53 |
?_assert(string:str(RebarOut, "myapp_mymod_tests:") =/= 0)}, |
|
54 |
||
55 |
{"Tests in 'src' directory are found and run", |
|
56 |
?_assert(string:str(RebarOut, "myapp_mymod:") =/= 0)}, |
|
57 |
||
58 |
{"Tests are only run once", |
|
59 |
?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}] |
|
60 |
end}. |
|
61 |
||
62 |
cover_test_() -> |
|
63 |
{"Ensure Cover runs with tests in a test dir and no defined suite", |
|
64 |
setup, fun() -> setup_cover_project(), rebar("-v eunit") end, |
|
65 |
fun teardown/1, |
|
66 |
||
67 |
[{"All cover reports are generated", |
|
68 |
assert_files_in("the temporary eunit directory", |
|
69 |
expected_cover_generated_files())}, |
|
70 |
||
71 |
{"Only production modules get coverage reports", |
|
72 |
assert_files_not_in("the temporary eunit directory", |
|
73 |
[".eunit/myapp_mymod_tests.COVER.html"])}]}. |
|
74 |
||
75 |
cover_with_suite_test_() -> |
|
76 |
{"Ensure Cover runs with Tests in a test dir and a test suite", |
|
77 |
setup, |
|
78 |
fun() -> |
|
79 |
setup_cover_project_with_suite(), |
|
80 |
rebar("-v eunit suite=mysuite") |
|
81 |
end, |
|
82 |
fun teardown/1, |
|
83 |
||
84 |
[{"All cover reports are generated", |
|
85 |
assert_files_in("the temporary eunit directory", |
|
86 |
expected_cover_generated_files())}, |
|
87 |
||
88 |
{"Only production modules get coverage reports", |
|
89 |
assert_files_not_in("the temporary eunit directory", |
|
90 |
[".eunit/myapp_mymod_tests.COVER.html", |
|
91 |
".eunit/mysuite"])}]}. |
|
92 |
||
93 |
expected_cover_generated_files() -> |
|
94 |
[".eunit/index.html", |
|
95 |
".eunit/myapp_app.COVER.html", |
|
96 |
".eunit/myapp_mymod.COVER.html", |
|
97 |
".eunit/myapp_sup.COVER.html"]. |
|
98 |
||
99 |
%% ==================================================================== |
|
100 |
%% Environment and Setup Tests |
|
101 |
%% ==================================================================== |
|
102 |
||
103 |
environment_test_() -> |
|
104 |
{"Sanity check the testing environment", |
|
105 |
setup, fun make_tmp_dir/0, fun remove_tmp_dir/1, |
|
106 |
||
107 |
[{"Ensure a test project can be created", |
|
108 |
?_assert(filelib:is_dir(?TMP_DIR))}, |
|
109 |
||
110 |
{"Ensure the rebar script can be found, copied, and run", |
|
111 |
[?_assert(filelib:is_file(?REBAR_SCRIPT)), |
|
112 |
fun assert_rebar_runs/0]}]}. |
|
113 |
||
114 |
assert_rebar_runs() -> |
|
115 |
prepare_rebar_script(), |
|
116 |
?assert(string:str(os:cmd("./" ++ ?TMP_DIR ++ "rebar"), "Usage: rebar") =/= 0). |
|
117 |
||
118 |
basic_setup_test_() -> |
|
119 |
{"Create a simple project with a 'test' directory, a test, and a module", |
|
120 |
setup, fun setup_basic_project/0, fun teardown/1, |
|
121 |
||
122 |
%% Test the setup function |
|
123 |
assert_dirs_in("Basic Project", |
|
124 |
["src", "ebin", "test"]) ++ |
|
125 |
assert_files_in("Basic Project", |
|
126 |
["test/myapp_mymod_tests.erl", "src/myapp_mymod.erl"])}. |
|
127 |
||
128 |
%% ==================================================================== |
|
129 |
%% Setup and Teardown |
|
130 |
%% ==================================================================== |
|
131 |
||
132 |
-define(myapp_mymod, |
|
133 |
["-module(myapp_mymod).\n", |
|
134 |
"-export([myfunc/0]).\n", |
|
135 |
"-include_lib(\"eunit/include/eunit.hrl\").\n", |
|
136 |
"myfunc() -> ok.\n", |
|
137 |
"myprivate_test() -> ?assert(true).\n"]). |
|
138 |
||
139 |
-define(myapp_mymod_tests, |
|
140 |
["-module(myapp_mymod_tests).\n", |
|
141 |
"-compile([export_all]).\n", |
|
142 |
"-include_lib(\"eunit/include/eunit.hrl\").\n", |
|
143 |
"myfunc_test() -> ?assertMatch(ok, myapp_mymod:myfunc()).\n"]). |
|
144 |
||
145 |
-define(mysuite, |
|
146 |
["-module(mysuite).\n", |
|
147 |
"-export([all_test_/0]).\n", |
|
148 |
"-include_lib(\"eunit/include/eunit.hrl\").\n", |
|
149 |
"all_test_() -> [myapp_mymod_defined_in_mysuite_tests].\n"]). |
|
150 |
||
151 |
-define(myapp_mymod_defined_in_mysuite_tests, |
|
152 |
["-module(myapp_mymod_defined_in_mysuite_tests).\n", |
|
153 |
"-compile([export_all]).\n", |
|
154 |
"-include_lib(\"eunit/include/eunit.hrl\").\n", |
|
155 |
"myfunc_test() -> ?assertMatch(ok, myapp_mymod:myfunc()).\n"]). |
|
156 |
||
157 |
make_tmp_dir() -> |
|
158 |
file:make_dir(?TMP_DIR). |
|
159 |
||
160 |
setup_environment() -> |
|
161 |
make_tmp_dir(), |
|
162 |
prepare_rebar_script(), |
|
163 |
file:set_cwd(?TMP_DIR). |
|
164 |
||
165 |
setup_basic_project() -> |
|
166 |
setup_environment(), |
|
167 |
rebar("create-app appid=myapp"), |
|
168 |
file:make_dir("test"), |
|
169 |
file:write_file("test/myapp_mymod_tests.erl", ?myapp_mymod_tests), |
|
170 |
file:write_file("src/myapp_mymod.erl", ?myapp_mymod). |
|
171 |
||
172 |
setup_cover_project() -> |
|
173 |
setup_basic_project(), |
|
174 |
file:write_file("rebar.config", "{cover_enabled, true}.\n"). |
|
175 |
||
176 |
setup_cover_project_with_suite() -> |
|
177 |
setup_cover_project(), |
|
178 |
file:write_file("test/mysuite.erl", ?mysuite), |
|
179 |
file:write_file("test/myapp_mymod_defined_in_mysuite_tests.erl", |
|
180 |
?myapp_mymod_defined_in_mysuite_tests). |
|
181 |
||
182 |
teardown(_) -> |
|
183 |
file:set_cwd(".."), |
|
184 |
remove_tmp_dir(), |
|
185 |
ok. |
|
186 |
||
187 |
remove_tmp_dir() -> |
|
188 |
remove_tmp_dir(arg_for_eunit). |
|
189 |
||
190 |
remove_tmp_dir(_) -> |
|
191 |
case os:type() of |
|
192 |
{unix, _} -> |
|
193 |
os:cmd("rm -rf " ++ ?TMP_DIR ++ " 2>/dev/null"); |
|
194 |
{win32, _} -> |
|
195 |
%% os:cmd("rmdir /S /Q " ++ ?TMP_DIR ++ " 2>NUL") |
|
196 |
exit("Windows is not supported yet.") |
|
197 |
end. |
|
198 |
||
199 |
%% ==================================================================== |
|
200 |
%% Helper Functions |
|
201 |
%% ==================================================================== |
|
202 |
||
203 |
prepare_rebar_script() -> |
|
204 |
{ok, _} = file:copy(?REBAR_SCRIPT, ?TMP_DIR ++ "rebar"), |
|
205 |
[] = os:cmd("chmod u+x " ++ ?TMP_DIR ++ "rebar"). |
|
206 |
||
207 |
rebar() -> |
|
208 |
rebar([]). |
|
209 |
||
210 |
rebar(Args) when is_list(Args) -> |
|
211 |
Out = os:cmd("./rebar " ++ Args), |
|
212 |
%?debugMsg("**** Begin"), ?debugMsg(Out), ?debugMsg("**** End"), |
|
213 |
Out. |
|
214 |
||
215 |
assert_dirs_in(Name, [Dir|T]) -> |
|
216 |
[{Name ++ " has directory: " ++ Dir, ?_assert(filelib:is_dir(Dir))} | |
|
217 |
assert_dirs_in(Name, T)]; |
|
218 |
assert_dirs_in(_, []) -> []. |
|
219 |
||
220 |
assert_files_in(Name, [File|T]) -> |
|
221 |
[{Name ++ " has file: " ++ File, ?_assert(filelib:is_file(File))} | |
|
222 |
assert_files_in(Name, T)]; |
|
223 |
assert_files_in(_, []) -> []. |
|
224 |
||
225 |
assert_files_not_in(Name, [File|T]) -> |
|
226 |
[{Name ++ " does not have file: " ++ File, ?_assertNot(filelib:is_file(File))} | |
|
227 |
assert_files_not_in(Name, T)]; |
|
228 |
assert_files_not_in(_, []) -> []. |
|
229 |
