aboutsummaryrefslogtreecommitdiffstats
path: root/lib/eunit/src/eunit.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/eunit/src/eunit.erl')
-rw-r--r--lib/eunit/src/eunit.erl250
1 files changed, 250 insertions, 0 deletions
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
new file mode 100644
index 0000000000..59084a52fb
--- /dev/null
+++ b/lib/eunit/src/eunit.erl
@@ -0,0 +1,250 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit.erl 339 2009-04-05 14:10:47Z rcarlsson $
+%%
+%% @copyright 2004-2009 Micka�l R�mond, Richard Carlsson
+%% @author Micka�l R�mond <[email protected]>
+%% [http://www.process-one.net/]
+%% @author Richard Carlsson <[email protected]>
+%% [http://user.it.uu.se/~richardc/]
+%% @version {@version}, {@date} {@time}
+%% @doc This module is the main EUnit user interface.
+
+-module(eunit).
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+%% Official exports
+-export([start/0, stop/0, test/1, test/2]).
+
+%% Experimental; may be removed or relocated
+-export([start/1, stop/1, test/3, submit/1, submit/2, submit/3, watch/1,
+ watch/2, watch/3, watch_path/1, watch_path/2, watch_path/3,
+ watch_regexp/1, watch_regexp/2, watch_regexp/3, watch_app/1,
+ watch_app/2, watch_app/3]).
+
+%% EUnit entry points
+
+%% TODO: Command line interface similar to that of edoc?
+
+%% @doc Starts the EUnit server. Normally, you don't need to call this
+%% function; it is started automatically.
+start() ->
+ start(?SERVER).
+
+%% @private
+%% @doc See {@link start/0}.
+start(Server) ->
+ eunit_server:start(Server).
+
+%% @doc Stops the EUnit server. Normally, you don't need to call this
+%% function.
+stop() ->
+ stop(?SERVER).
+
+%% @private
+%% @doc See {@link stop/0}.
+stop(Server) ->
+ eunit_server:stop(Server).
+
+%% @private
+watch(Target) ->
+ watch(Target, []).
+
+%% @private
+watch(Target, Options) ->
+ watch(?SERVER, Target, Options).
+
+%% @private
+watch(Server, Target, Options) ->
+ eunit_server:watch(Server, Target, Options).
+
+%% @private
+watch_path(Target) ->
+ watch_path(Target, []).
+
+%% @private
+watch_path(Target, Options) ->
+ watch_path(?SERVER, Target, Options).
+
+%% @private
+watch_path(Server, Target, Options) ->
+ eunit_server:watch_path(Server, Target, Options).
+
+%% @private
+watch_regexp(Target) ->
+ watch_regexp(Target, []).
+
+%% @private
+watch_regexp(Target, Options) ->
+ watch_regexp(?SERVER, Target, Options).
+
+%% @private
+watch_regexp(Server, Target, Options) ->
+ eunit_server:watch_regexp(Server, Target, Options).
+
+%% @private
+watch_app(Name) ->
+ watch_app(Name, []).
+
+%% @private
+watch_app(Name, Options) ->
+ watch_app(?SERVER, Name, Options).
+
+%% @private
+watch_app(Server, Name, Options) ->
+ case code:lib_dir(Name) of
+ Path when is_list(Path) ->
+ watch_path(Server, filename:join(Path, "ebin"), Options);
+ _ ->
+ error
+ end.
+
+%% @equiv test(Tests, [])
+test(Tests) ->
+ test(Tests, []).
+
+%% @spec test(Tests::term(), Options::[term()]) -> ok | {error, term()}
+%% @doc Runs a set of tests. The format of `Tests' is described in the
+%% section <a
+%% href="overview-summary.html#EUnit_test_representation">EUnit test
+%% representation</a> of the overview.
+%%
+%% Example: ```eunit:test(fred)''' runs all tests in the module `fred'
+%% and also any tests in the module `fred_tests', if that module exists.
+%%
+%% Options:
+%% <dl>
+%% <dt>`verbose'</dt>
+%% <dd>Displays more details about the running tests.</dd>
+%% </dl>
+%%
+%% Options in the environment variable EUNIT are also included last in
+%% the option list, i.e., have lower precedence than those in `Options'.
+%% @see test/1
+test(Tests, Options) ->
+ test(?SERVER, Tests, all_options(Options)).
+
+%% @private
+%% @doc See {@link test/2}.
+test(Server, Tests, Options) ->
+ Listeners = [eunit_tty:start(Options) | listeners(Options)],
+ Serial = eunit_serial:start(Listeners),
+ case eunit_server:start_test(Server, Serial, Tests, Options) of
+ {ok, Reference} -> test_run(Reference, Listeners);
+ {error, R} -> {error, R}
+ end.
+
+test_run(Reference, Listeners) ->
+ receive
+ {start, Reference} ->
+ cast(Listeners, {start, Reference})
+ end,
+ receive
+ {done, Reference} ->
+ cast(Listeners, {stop, Reference, self()}),
+ receive
+ {result, Reference, Result} ->
+ Result
+ end
+ end.
+
+cast([P | Ps], Msg) ->
+ P ! Msg,
+ cast(Ps, Msg);
+cast([], _Msg) ->
+ ok.
+
+%% TODO: functions that run tests on a given node, not a given server
+%% TODO: maybe some functions could check for a globally registered server?
+%% TODO: some synchronous but completely quiet interface function
+
+%% @private
+submit(T) ->
+ submit(T, []).
+
+%% @private
+submit(T, Options) ->
+ submit(?SERVER, T, Options).
+
+%% @private
+submit(Server, T, Options) ->
+ Dummy = spawn(fun devnull/0),
+ eunit_server:start_test(Server, Dummy, T, Options).
+
+listeners(Options) ->
+ Ps = start_listeners(proplists:get_all_values(report, Options)),
+ %% the event_log option is for debugging, to view the raw events
+ case proplists:get_value(event_log, Options) of
+ undefined ->
+ Ps;
+ X ->
+ LogFile = if is_list(X) -> X;
+ true -> "eunit-events.log"
+ end,
+ [spawn_link(fun () -> event_logger(LogFile) end) | Ps]
+ end.
+
+start_listeners([P | Ps]) when is_pid(P) ; is_atom(P) ->
+ [P | start_listeners(Ps)];
+start_listeners([{Mod, Opts} | Ps]) when is_atom(Mod) ->
+ [Mod:start(Opts) | start_listeners(Ps)];
+start_listeners([]) ->
+ [].
+
+%% TODO: make this report file errors
+event_logger(LogFile) ->
+ case file:open(LogFile, [write]) of
+ {ok, FD} ->
+ receive
+ {start, Reference} ->
+ event_logger_loop(Reference, FD)
+ end;
+ Error ->
+ exit(Error)
+ end.
+
+event_logger_loop(Reference, FD) ->
+ receive
+ {status, _Id, _Info}=Msg ->
+ io:fwrite(FD, "~p.\n", [Msg]),
+ event_logger_loop(Reference, FD);
+ {stop, Reference, _ReplyTo} ->
+ %% no need to reply, just exit
+ file:close(FD),
+ exit(normal)
+ end.
+
+%% TODO: make a proper logger for asynchronous execution with submit/3
+
+devnull() ->
+ receive _ -> devnull() end.
+
+%% including options from EUNIT environment variable
+
+all_options(Opts) ->
+ try os:getenv("EUNIT") of
+ false -> Opts;
+ S ->
+ {ok, Ts, _} = erl_scan:string(S),
+ {ok, V} = erl_parse:parse_term(Ts ++ [{dot,1}]),
+ if is_list(V) -> Opts ++ V;
+ true -> Opts ++ [V]
+ end
+ catch
+ _:_ -> Opts
+ end.