1%% Licensed under the Apache License, Version 2.0 (the "License"); you may 2%% not use this file except in compliance with the License. You may obtain 3%% a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0> 4%% 5%% Unless required by applicable law or agreed to in writing, software 6%% distributed under the License is distributed on an "AS IS" BASIS, 7%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8%% See the License for the specific language governing permissions and 9%% limitations under the License. 10%% 11%% Alternatively, you may use this file under the terms of the GNU Lesser 12%% General Public License (the "LGPL") as published by the Free Software 13%% Foundation; either version 2.1, or (at your option) any later version. 14%% If you wish to allow use of your version of this file only under the 15%% terms of the LGPL, you should delete the provisions above and replace 16%% them with the notice and other provisions required by the LGPL; see 17%% <http://www.gnu.org/licenses/>. If you do not delete the provisions 18%% above, a recipient may use your version of this file under the terms of 19%% either the Apache License or the LGPL. 20%% 21%% @copyright 2004-2009 Mickaël Rémond, Richard Carlsson 22%% @author Mickaël Rémond <mickael.remond@process-one.net> 23%% [http://www.process-one.net/] 24%% @author Richard Carlsson <carlsson.richard@gmail.com> 25%% @version {@version}, {@date} {@time} 26%% @doc This module is the main EUnit user interface. 27 28-module(eunit). 29 30-include("eunit.hrl"). 31-include("eunit_internal.hrl"). 32 33%% Official exports 34-export([start/0, stop/0, test/1, test/2]). 35 36%% Experimental; may be removed or relocated 37-export([start/1, stop/1, test/3, submit/1, submit/2, submit/3, watch/1, 38 watch/2, watch/3, watch_path/1, watch_path/2, watch_path/3, 39 watch_regexp/1, watch_regexp/2, watch_regexp/3, watch_app/1, 40 watch_app/2, watch_app/3]). 41 42%% EUnit entry points 43 44%% TODO: Command line interface similar to that of edoc? 45 46%% @doc Starts the EUnit server. Normally, you don't need to call this 47%% function; it is started automatically. 48start() -> 49 start(?SERVER). 50 51%% @private 52%% @doc See {@link start/0}. 53start(Server) -> 54 eunit_server:start(Server). 55 56%% @doc Stops the EUnit server. Normally, you don't need to call this 57%% function. 58stop() -> 59 stop(?SERVER). 60 61%% @private 62%% @doc See {@link stop/0}. 63stop(Server) -> 64 eunit_server:stop(Server). 65 66%% @private 67watch(Target) -> 68 watch(Target, []). 69 70%% @private 71watch(Target, Options) -> 72 watch(?SERVER, Target, Options). 73 74%% @private 75watch(Server, Target, Options) -> 76 eunit_server:watch(Server, Target, Options). 77 78%% @private 79watch_path(Target) -> 80 watch_path(Target, []). 81 82%% @private 83watch_path(Target, Options) -> 84 watch_path(?SERVER, Target, Options). 85 86%% @private 87watch_path(Server, Target, Options) -> 88 eunit_server:watch_path(Server, Target, Options). 89 90%% @private 91watch_regexp(Target) -> 92 watch_regexp(Target, []). 93 94%% @private 95watch_regexp(Target, Options) -> 96 watch_regexp(?SERVER, Target, Options). 97 98%% @private 99watch_regexp(Server, Target, Options) -> 100 eunit_server:watch_regexp(Server, Target, Options). 101 102%% @private 103watch_app(Name) -> 104 watch_app(Name, []). 105 106%% @private 107watch_app(Name, Options) -> 108 watch_app(?SERVER, Name, Options). 109 110%% @private 111watch_app(Server, Name, Options) -> 112 case code:lib_dir(Name) of 113 Path when is_list(Path) -> 114 watch_path(Server, filename:join(Path, "ebin"), Options); 115 _ -> 116 error 117 end. 118 119%% @equiv test(Tests, []) 120test(Tests) -> 121 test(Tests, []). 122 123%% @spec test(Tests::term(), Options::[term()]) -> ok | {error, term()} 124%% @doc Runs a set of tests. The format of `Tests' is described in the 125%% section <a 126%% href="overview-summary.html#EUnit_test_representation">EUnit test 127%% representation</a> of the overview. 128%% 129%% Example: ```eunit:test(fred)''' runs all tests in the module `fred' 130%% and also any tests in the module `fred_tests', if that module exists. 131%% 132%% Options: 133%% <dl> 134%% <dt>`verbose'</dt> 135%% <dd>Displays more details about the running tests.</dd> 136%% <dt>`print_depth'</dt> 137%% <dd>Maximum depth to which terms are printed in case of error.</dd> 138%% </dl> 139%% 140%% Options in the environment variable EUNIT are also included last in 141%% the option list, i.e., have lower precedence than those in `Options'. 142%% @see test/1 143test(Tests, Options) -> 144 test(?SERVER, Tests, all_options(Options)). 145 146%% @private 147%% @doc See {@link test/2}. 148test(Server, Tests, Options) -> 149 Listeners = listeners(Options), 150 Serial = eunit_serial:start(Listeners), 151 case eunit_server:start_test(Server, Serial, Tests, Options) of 152 {ok, Reference} -> test_run(Reference, Listeners); 153 {error, R} -> {error, R} 154 end. 155 156test_run(Reference, Listeners) -> 157 receive 158 {start, Reference} -> 159 cast(Listeners, {start, Reference}) 160 end, 161 receive 162 {done, Reference} -> 163 cast(Listeners, {stop, Reference, self()}), 164 wait_until_listeners_have_terminated(Listeners), 165 receive 166 {result, Reference, Result} -> 167 Result 168 end 169 end. 170 171cast([P | Ps], Msg) -> 172 P ! Msg, 173 cast(Ps, Msg); 174cast([], _Msg) -> 175 ok. 176 177wait_until_listeners_have_terminated([P | Ps]) -> 178 MRef = erlang:monitor(process, P), 179 receive 180 {'DOWN', MRef, process, P, _} -> 181 wait_until_listeners_have_terminated(Ps) 182 end; 183wait_until_listeners_have_terminated([]) -> 184 ok. 185 186%% TODO: functions that run tests on a given node, not a given server 187%% TODO: maybe some functions could check for a globally registered server? 188%% TODO: some synchronous but completely quiet interface function 189 190%% @private 191submit(T) -> 192 submit(T, []). 193 194%% @private 195submit(T, Options) -> 196 submit(?SERVER, T, Options). 197 198%% @private 199submit(Server, T, Options) -> 200 Dummy = spawn(fun devnull/0), 201 eunit_server:start_test(Server, Dummy, T, Options). 202 203listeners(Options) -> 204 %% note that eunit_tty must always run, because it sends the final 205 %% {result,...} message that the test_run() function is waiting for 206 Ls = [{eunit_tty, Options} | proplists:get_all_values(report, Options)], 207 Ps = start_listeners(Ls), 208 %% the event_log option is for debugging, to view the raw events 209 case proplists:get_value(event_log, Options) of 210 undefined -> 211 Ps; 212 X -> 213 LogFile = if is_list(X) -> X; 214 true -> "eunit-events.log" 215 end, 216 [spawn_link(fun () -> event_logger(LogFile) end) | Ps] 217 end. 218 219start_listeners([P | Ps]) when is_pid(P) ; is_atom(P) -> 220 [P | start_listeners(Ps)]; 221start_listeners([{Mod, Opts} | Ps]) when is_atom(Mod) -> 222 [Mod:start(Opts) | start_listeners(Ps)]; 223start_listeners([]) -> 224 []. 225 226%% TODO: make this report file errors 227event_logger(LogFile) -> 228 case file:open(LogFile, [write]) of 229 {ok, FD} -> 230 receive 231 {start, Reference} -> 232 event_logger_loop(Reference, FD) 233 end; 234 Error -> 235 exit(Error) 236 end. 237 238event_logger_loop(Reference, FD) -> 239 receive 240 {status, _Id, _Info}=Msg -> 241 io:fwrite(FD, "~tp.\n", [Msg]), 242 event_logger_loop(Reference, FD); 243 {stop, Reference, _ReplyTo} -> 244 %% no need to reply, just exit 245 file:close(FD), 246 exit(normal) 247 end. 248 249%% TODO: make a proper logger for asynchronous execution with submit/3 250 251devnull() -> 252 receive _ -> devnull() end. 253 254%% including options from EUNIT environment variable 255 256all_options(Opts) -> 257 try os:getenv("EUNIT") of 258 false -> Opts; 259 S -> 260 {ok, Ts, _} = erl_scan:string(S), 261 {ok, V} = erl_parse:parse_term(Ts ++ [{dot,erl_anno:new(1)}]), 262 if is_list(V) -> Opts ++ V; 263 true -> Opts ++ [V] 264 end 265 catch 266 _:_ -> Opts 267 end. 268