basho / rebar

Erlang Build Tools -- Please file bug and feature requests at http://issues.basho.com.

Clone this repository (size: 432.4 KB): HTTPS / SSH
$ hg clone http://hg.basho.com/rebar
commit 172: e2b8d83ce90a
parent 171: e1c6070a85d8
branch: default
Refactor eunit and cover
Ian Wilkinson / theiw
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
    %% grab all the test modules for inclusion in the compile stage
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
    Beams = [filename:basename(N, ".beam") || N <- rebar_utils:beams(?EUNIT_DIR)],
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
                                    lists:suffix("_tests", B) end, Beams),
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
    %% TODO: If there are other wildcards specified in eunit_sources, compile them
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
    InitCodePath = code:get_path(),
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, Modules) ->
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).