aboutsummaryrefslogtreecommitdiffstats
path: root/lib/et/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/et/test')
-rw-r--r--lib/et/test/Makefile81
-rw-r--r--lib/et/test/README30
-rw-r--r--lib/et/test/et.spec2
-rw-r--r--lib/et/test/et_test_lib.erl329
-rw-r--r--lib/et/test/et_test_lib.hrl90
-rw-r--r--lib/et/test/et_wx_SUITE.erl100
-rwxr-xr-xlib/et/test/ett55
-rw-r--r--lib/et/test/ett.erl154
8 files changed, 841 insertions, 0 deletions
diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile
new file mode 100644
index 0000000000..9aedf96ce9
--- /dev/null
+++ b/lib/et/test/Makefile
@@ -0,0 +1,81 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ ett \
+ et_wx_SUITE \
+ et_test_lib
+
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+HRL_FILES= et_test_lib.hrl
+
+TARGET_FILES= \
+ $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+INSTALL_PROGS= $(TARGET_FILES)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/et_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+#ERL_COMPILE_FLAGS +=
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) et.spec $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+ $(INSTALL_PROGRAM) ett $(INSTALL_PROGS) $(RELSYSDIR)
+# chmod -f -R u+w $(RELSYSDIR)
+# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
+
+
diff --git a/lib/et/test/README b/lib/et/test/README
new file mode 100644
index 0000000000..4269186c93
--- /dev/null
+++ b/lib/et/test/README
@@ -0,0 +1,30 @@
+
+Testing and running et tests.
+
+Testing gui api/applications can be hard, but we can at least
+test that wxerlang behaves as we expected, i.e. that the api
+is consistent and that it don't crash.
+
+The tests are structured as they are because we want you to
+be able to run them in three different ways.
+ - direct via an erlang shell
+ - via common_test application
+ - via erlang/OTP inhouse ts tool.
+
+To run all the tests compile them and on unix
+run ./ett to create an erlang terminal.
+
+Invoke ett:t(). in the erlang shell to run all regression tests.
+If you want to specific tests invoke ett:t(Module)
+or ett:t(Module, TestCase).
+
+To run all tests including the ones that require manual intervention run.
+ett:t(all, [{user,true}]).
+
+To see every test_case window use
+ett:t(all, [{user,step}]).
+This requires that you manually close each window to step to the
+next test_case.
+
+If you want to run specific test_cases use:
+ett:t({Module,TestCase}, [{user,step}]).
diff --git a/lib/et/test/et.spec b/lib/et/test/et.spec
new file mode 100644
index 0000000000..69cd8d7582
--- /dev/null
+++ b/lib/et/test/et.spec
@@ -0,0 +1,2 @@
+{topcase, {dir, "../et_test"}}.
+
diff --git a/lib/et/test/et_test_lib.erl b/lib/et/test/et_test_lib.erl
new file mode 100644
index 0000000000..b91b63786c
--- /dev/null
+++ b/lib/et/test/et_test_lib.erl
@@ -0,0 +1,329 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(et_test_lib).
+-compile(export_all).
+
+-include("et_test_lib.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_per_suite(Config) when is_list(Config)->
+ incr_timetrap(Config, 5).
+
+end_per_suite(Config) when is_list(Config)->
+ ok.
+
+incr_timetrap(Config, Times) ->
+ Key = tc_timeout,
+ KeyPos = 1,
+ NewTime =
+ case lists:keysearch(Key, KeyPos, Config) of
+ {value, {Key, OldTime}} ->
+ (timer:minutes(1) + OldTime) * Times;
+ false ->
+ timer:minutes(1) * Times
+ end,
+ lists:keystore(Key, KeyPos, Config, {Key, NewTime}).
+
+set_kill_timer(Config) ->
+ case init:get_argument(et_test_timeout) of
+ {ok, _} ->
+ Config;
+ _ ->
+ Time =
+ case lookup_config(tc_timeout, Config) of
+ [] ->
+ timer:minutes(5);
+ ConfigTime when is_integer(ConfigTime) ->
+ ConfigTime
+ end,
+ WatchDog = test_server:timetrap(Time),
+ [{kill_timer, WatchDog} | Config]
+ end.
+
+reset_kill_timer(Config) ->
+ DogKiller =
+ case get(et_test_server) of
+ true ->
+ fun(P) when is_pid(P) -> P ! stop;
+ (_) -> ok
+ end;
+ _ ->
+ fun(Ref) -> test_server:timetrap_cancel(Ref) end
+ end,
+ case lists:keysearch(kill_timer, 1, Config) of
+ {value, {kill_timer, WatchDog}} ->
+ DogKiller(WatchDog),
+ lists:keydelete(kill_timer, 1, Config);
+ _ ->
+ Config
+ end.
+
+lookup_config(Key,Config) ->
+ case lists:keysearch(Key, 1, Config) of
+ {value,{Key,Val}} ->
+ Val;
+ _ ->
+ []
+ end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+wx_init_per_suite(Config) ->
+ {_Pid, Ref} =
+ spawn_monitor(fun() ->
+ %% Avoid test case crash if wx master process dies
+ process_flag(trap_exit, true),
+ try
+ case os:type() of
+ {unix,darwin} ->
+ exit({skipped, "Can not test on MacOSX"});
+ {unix, _} ->
+ io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
+ case proplists:get_value(xserver, Config, none) of
+ none -> ignore;
+ Server -> os:putenv("DISPLAY", Server)
+ end;
+ _ ->
+ ignore
+ end,
+ wx:new(),
+ wx:destroy()
+ catch
+ error:undef ->
+ exit({skipped, "No wx compiled for this platform"});
+ _:Reason ->
+ exit({skipped, lists:flatten(io_lib:format("Start wx failed: ~p", [Reason]))})
+ end,
+ exit(normal)
+ end),
+ receive
+ {'DOWN', Ref, _, _, normal} ->
+ init_per_suite(Config);
+ {'DOWN', Ref, _, _, {skipped, _} = Skipped} ->
+ Skipped;
+ {'DOWN', Ref, _, _, Reason} ->
+ exit({wx_init_per_suite, Reason})
+ after timer:minutes(1) ->
+ exit({wx_init_per_suite, timeout})
+ end.
+
+wx_end_per_suite(Config) ->
+ end_per_suite(Config).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_per_testcase(_Func, Config) when is_list(Config) ->
+ set_kill_timer(Config),
+ global:register_name(et_global_logger, group_leader()),
+ Config.
+
+end_per_testcase(_Func, Config) when is_list(Config) ->
+ global:unregister_name(et_global_logger),
+ reset_kill_timer(Config),
+ Config.
+
+%% Backwards compatible with test_server
+tc_info(suite) -> [];
+tc_info(doc) -> "".
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Use ?log(Format, Args) as wrapper
+log(Format, Args, LongFile, Line) ->
+ File = filename:basename(LongFile),
+ Format2 = lists:concat([File, "(", Line, ")", ": ", Format]),
+ log(Format2, Args).
+
+log(Format, Args) ->
+ case global:whereis_name(et_global_logger) of
+ undefined ->
+ io:format(user, Format, Args);
+ Pid ->
+ io:format(Pid, Format, Args)
+ end.
+
+verbose(Format, Args, File, Line) ->
+ Arg = et_test_verbose,
+ case get(Arg) of
+ false ->
+ ok;
+ true ->
+ log(Format, Args, File, Line);
+ undefined ->
+ case init:get_argument(Arg) of
+ {ok, List} when is_list(List) ->
+ case lists:last(List) of
+ ["true"] ->
+ put(Arg, true),
+ log(Format, Args, File, Line);
+ _ ->
+ put(Arg, false),
+ ok
+ end;
+ _ ->
+ put(Arg, false),
+ ok
+ end
+ end.
+
+error(Format, Args, File, Line) ->
+ global:send(et_global_logger, {failed, File, Line}),
+ Fail = {filename:basename(File),Line,Args},
+ case global:whereis_name(et_test_case_sup) of
+ undefined -> ignore;
+ Pid -> Pid ! Fail
+ %% global:send(et_test_case_sup, Fail),
+ end,
+ log("<ERROR>~n" ++ Format, Args, File, Line).
+
+
+pick_msg() ->
+ receive
+ Message -> Message
+ after 4000 -> timeout
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Utility functions
+
+user_available(Config) ->
+ false /= proplists:get_value(user, Config, false).
+
+
+wx_destroy(Frame, Config) ->
+ case proplists:get_value(user, Config, false) of
+ false ->
+ timer:sleep(100),
+ ?m(ok, wxFrame:destroy(Frame)),
+ ?m(ok, wx:destroy());
+ true ->
+ timer:sleep(500),
+ ?m(ok, wxFrame:destroy(Frame)),
+ ?m(ok, wx:destroy());
+ step -> %% Wait for user to close window
+ ?m(ok, wxEvtHandler:connect(Frame, close_window, [{skip,true}])),
+ wait_for_close()
+ end.
+
+wait_for_close() ->
+ receive
+ #wx{event=#wxClose{}} ->
+ ?log("Got close~n",[]),
+ ?m(ok, wx:destroy());
+ #wx{obj=Obj, event=Event} ->
+ try
+ Name = wxTopLevelWindow:getTitle(Obj),
+ ?log("~p Event: ~p~n", [Name, Event])
+ catch _:_ ->
+ ?log("Event: ~p~n", [Event])
+ end,
+ wait_for_close();
+ Other ->
+ ?log("Unexpected: ~p~n", [Other]),
+ wait_for_close()
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% A small test server, which can be run standalone in a shell
+
+run_test(Test = {_,_},Config) ->
+ run_test([Test],Config);
+run_test([{Module, TC} | Rest], Config) ->
+ log("\n\n=== Eval test suite: ~w ===~n", [Module]),
+ case catch Module:init_per_suite(Config) of
+ {skipped, Reason} ->
+ log("Test suite skipped: ~s~n", [Reason]),
+ [{skipped, Reason}];
+ NewConfig when is_list(NewConfig) ->
+ Res =
+ if
+ TC =:= all ->
+ [do_run_test(Module, Test, NewConfig) || Test <- Module:all()];
+ is_list(TC) ->
+ [do_run_test(Module, Test, NewConfig) || Test <- TC];
+ true ->
+ [do_run_test(Module, TC, NewConfig)]
+ end,
+ Module:end_per_suite(NewConfig),
+ Res ++ run_test(Rest, NewConfig);
+ Error ->
+ ?error("Test suite skipped: ~w~n", [Error]),
+ [{skipped, Error}]
+ end;
+run_test([], _Config) ->
+ [].
+
+do_run_test(Module, all, Config) ->
+ All = [{Module, Test} || Test <- Module:all()],
+ run_test(All, Config);
+do_run_test(Module, TestCase, Config) ->
+ log("Eval test case: ~w~n", [{Module, TestCase}]),
+ Sec = timer:seconds(1) * 1000,
+ {T, Res} =
+ timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]),
+ log("Tested ~w in ~w sec~n", [TestCase, T div Sec]),
+ {T div Sec, Res}.
+
+eval_test_case(Mod, Fun, Config) ->
+ flush(),
+ global:register_name(et_test_case_sup, self()),
+ Flag = process_flag(trap_exit, true),
+ Pid = spawn_link(?MODULE, test_case_evaluator, [Mod, Fun, [Config]]),
+ R = wait_for_evaluator(Pid, Mod, Fun, Config),
+ global:unregister_name(et_test_case_sup),
+ process_flag(trap_exit, Flag),
+ R.
+
+test_case_evaluator(Mod, Fun, [Config]) ->
+ NewConfig = Mod:init_per_testcase(Fun, Config),
+ R = apply(Mod, Fun, [NewConfig]),
+ Mod:fin_per_testcase(Fun, NewConfig),
+ exit({test_case_ok, R}).
+
+wait_for_evaluator(Pid, Mod, Fun, Config) ->
+ receive
+ {'EXIT', Pid, {test_case_ok, _PidRes}} ->
+ Errors = flush(),
+ Res =
+ case Errors of
+ [] -> ok;
+ Errors -> failed
+ end,
+ {Res, {Mod, Fun}, Errors};
+ {'EXIT', Pid, {skipped, Reason}} ->
+ log("<WARNING> Test case ~w skipped, because ~p~n",
+ [{Mod, Fun}, Reason]),
+ Mod:fin_per_testcase(Fun, Config),
+ {skip, {Mod, Fun}, Reason};
+ {'EXIT', Pid, Reason} ->
+ log("<ERROR> Eval process ~w exited, because\n\t~p~n",
+ [{Mod, Fun}, Reason]),
+ Mod:fin_per_testcase(Fun, Config),
+ {crash, {Mod, Fun}, Reason}
+ end.
+
+flush() ->
+ receive Msg -> [Msg | flush()]
+ after 0 -> []
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/et/test/et_test_lib.hrl b/lib/et/test/et_test_lib.hrl
new file mode 100644
index 0000000000..0d75318d99
--- /dev/null
+++ b/lib/et/test/et_test_lib.hrl
@@ -0,0 +1,90 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-include_lib("wx/include/wx.hrl").
+
+-define(log(Format,Args), et_test_lib:log(Format,Args,?FILE,?LINE)).
+-define(warning(Format,Args), ?log("<WARNING>\n " ++ Format,Args)).
+-define(error(Format,Args), et_test_lib:error(Format,Args,?FILE,?LINE)).
+-define(verbose(Format,Args), et_test_lib:verbose(Format,Args,?FILE,?LINE)).
+
+-define(fatal(Format,Args),
+ ?error(Format, Args),
+ exit({test_case_fatal, Format, Args, ?FILE, ?LINE})).
+
+-define(skip(Format,Args),
+ ?warning(Format, Args),
+ exit({skipped, ?flat_format(Format, Args)})).
+
+-define(ignore(Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ ?verbose("ok: ~p\n",[AcTuAlReS])
+ end()).
+
+-define(msym(ExpectedRes, Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ case AcTuAlReS of
+ ExpectedRes ->
+ ?verbose("ok: ~p\n",[AcTuAlReS]),
+ AcTuAlReS;
+ _ ->
+ et_test_lib:error("Not matching actual result was:\n ~p \nExpected ~s\n",
+ [AcTuAlReS, ??ExpectedRes],
+ ?FILE, ?LINE),
+ AcTuAlReS
+ end
+ end()).
+
+-define(m(ExpectedRes, Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ case AcTuAlReS of
+ ExpectedRes ->
+ ?verbose("ok: ~p\n",[AcTuAlReS]),
+ AcTuAlReS;
+ _ ->
+ et_test_lib:error("Not matching actual result was:\n\t~p \nExpected:\n\t~p\n",
+ [AcTuAlReS, ExpectedRes],
+ ?FILE, ?LINE),
+ AcTuAlReS
+ end
+ end()).
+
+-define(m_receive(ExpectedMsg),
+ ?m(ExpectedMsg,et_test_lib:pick_msg())).
+
+-define(m_multi_receive(ExpectedMsgs),
+ fun() ->
+ TmPeXpCtEdMsGs = lists:sort(ExpectedMsgs),
+ AcTuAlReS =
+ lists:sort(lists:map(fun(_) ->
+ et_test_lib:pick_msg()
+ end, TmPeXpCtEdMsGs)),
+ case AcTuAlReS of
+ TmPeXpCtEdMsGs ->
+ ?verbose("ok: ~p\n",[AcTuAlReS]),
+ AcTuAlReS;
+ _ ->
+ et_test_lib:error("Not matching actual result was:\n ~p \nExpected ~p\n",
+ [AcTuAlReS, ExpectedMsgs],
+ ?FILE, ?LINE),
+ AcTuAlReS
+ end
+ end()).
diff --git a/lib/et/test/et_wx_SUITE.erl b/lib/et/test/et_wx_SUITE.erl
new file mode 100644
index 0000000000..1a16ca69a3
--- /dev/null
+++ b/lib/et/test/et_wx_SUITE.erl
@@ -0,0 +1,100 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(et_wx_SUITE).
+
+-export([all/0, init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+
+-compile(export_all).
+
+-include("et_test_lib.hrl").
+
+%% Initialization functions.
+init_per_suite(Config) ->
+ et_test_lib:wx_init_per_suite(Config).
+
+end_per_suite(Config) ->
+ et_test_lib:wx_end_per_suite(Config).
+
+init_per_testcase(Func,Config) ->
+ et_test_lib:init_per_testcase(Func,Config).
+end_per_testcase(Func,Config) ->
+ et_test_lib:end_per_testcase(Func,Config).
+fin_per_testcase(Func,Config) -> %% For test_server
+ et_test_lib:end_per_testcase(Func,Config).
+
+%% SUITE specification
+all() ->
+ all(suite).
+all(suite) ->
+ [
+ start_all_windows
+ ].
+
+%% The test cases
+
+%% Display all windows and see if something crashes
+start_all_windows(TestInfo) when is_atom(TestInfo) ->
+ et_test_lib:tc_info(TestInfo);
+start_all_windows(_Config) ->
+ process_flag(trap_exit, true),
+ {ok, ViewerPid} = ?msym({ok, _}, et_viewer:start_link([])),
+ CollectorPid = et_viewer:get_collector_pid(ViewerPid),
+ ?msym({ok, _}, et_collector:report_event(CollectorPid,
+ 60,
+ some_from_actor,
+ some_to_actor,
+ some_label,
+ "Some details")),
+ timer:sleep(timer:seconds(1)),
+
+ {ok, EventPid1} = ?msym({ok, _}, et_viewer:open_event(ViewerPid, 1)),
+ {ok, EventPid2} = ?msym({ok, _}, et_viewer:open_event(ViewerPid, 1)),
+ timer:sleep(timer:seconds(10)),
+
+ ?msym(alive, process_state(ViewerPid)),
+ ?msym(alive, process_state(CollectorPid)),
+ ?msym(alive, process_state(EventPid1)),
+ ?msym(alive, process_state(EventPid2)),
+
+ ?m(ok, et_wx_contents_viewer:stop(EventPid1)),
+ timer:sleep(timer:seconds(1)),
+
+ ?msym(alive, process_state(ViewerPid)),
+ ?msym(alive, process_state(CollectorPid)),
+ ?msym(dead, process_state(EventPid1)),
+ ?msym(alive, process_state(EventPid2)),
+
+ ?m(ok, et_viewer:stop(ViewerPid)),
+ timer:sleep(timer:seconds(1)),
+
+ ?msym(dead, process_state(ViewerPid)),
+ ?msym(dead, process_state(CollectorPid)),
+ ?msym(dead, process_state(EventPid1)),
+ ?msym(dead, process_state(EventPid2)),
+
+ ?m([], et_test_lib:flush()),
+
+ ok.
+
+process_state(Pid) ->
+ case process_info(Pid, group_leader) of
+ {group_leader, _} -> alive;
+ undefined -> dead
+ end.
diff --git a/lib/et/test/ett b/lib/et/test/ett
new file mode 100755
index 0000000000..da2443df61
--- /dev/null
+++ b/lib/et/test/ett
@@ -0,0 +1,55 @@
+#! /bin/sh -f
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+# Usage: ett [-cerl] <args to erlang startup script>
+
+emu=erl
+while [ $# -gt 0 ]; do
+ case "$1" in
+ "-cerl")
+ shift
+ emu=cerl
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+log=test_log_$$
+latest=test_log_latest
+args=${1+"$@"}
+
+erlcmd="$emu -sname test_server -smp -pa ../../et/ebin $p $args -et_test_verbose true -et_test_timeout"
+
+echo "Give the following command in order to see the outcome:"
+echo ""
+echo " less $log"
+
+rm "$latest" 2>/dev/null
+ln -s "$log" "$latest"
+touch "$log"
+
+ostype=`uname -s`
+if [ "$ostype" = "SunOS" ] ; then
+ /usr/openwin/bin/xterm -T "Testing et" -l -lf "$log" -e $erlcmd &
+else
+ xterm -T "Testing et" -e script -f -c "$erlcmd" "$log" &
+fi
+
+tail -f "$log" | egrep 'Eval|<ERROR>|NYI'
diff --git a/lib/et/test/ett.erl b/lib/et/test/ett.erl
new file mode 100644
index 0000000000..1896e65842
--- /dev/null
+++ b/lib/et/test/ett.erl
@@ -0,0 +1,154 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(ett).
+-compile(export_all).
+
+%% Modules or suites can be shortcuts, for example wx expands to et_wx_SUITE.
+%%
+%% t(Tests) run et testcases.
+%% Tests can be module, {module, test_case} or [module|{module,test_case}]
+
+t() ->
+ t(read_test_case()).
+t(Test) ->
+ t(Test, []).
+
+t(Mod, TC) when is_atom(Mod), is_atom(TC) ->
+ t({Mod,TC}, []);
+t(all, Config) when is_list(Config) ->
+ Fs = filelib:wildcard("et_*_SUITE.erl"),
+ t([list_to_atom(filename:rootname(File)) || File <- Fs], Config);
+t(Test,Config) when is_list(Config) ->
+ Tests = resolve(Test),
+ write_test_case(Test),
+ Res = et_test_lib:run_test(Tests, Config),
+ append_test_case_info(Test, Res).
+
+user() ->
+ user(read_test_case()).
+user(Mod) ->
+ t(Mod, [{user,step}]).
+user(Mod,Tc) when is_atom(Tc) ->
+ t({Mod,Tc}, [{user,step}]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Resolves the name of test suites and test cases
+%% according to the alias definitions. Single atoms
+%% are assumed to be the name of a test suite.
+
+resolve(Suite0) when is_atom(Suite0) ->
+ case alias(Suite0) of
+ Suite when is_atom(Suite) ->
+ {Suite, all};
+ {Suite, Case} ->
+ {Suite, Case}
+ end;
+resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) ->
+ case alias(Suite0) of
+ Suite when is_atom(Suite) ->
+ {Suite, Case};
+ {Suite, Case2} ->
+ {Suite, Case2}
+ end;
+resolve(List) when is_list(List) ->
+ [resolve(Case) || Case <- List].
+
+alias(Suite) when is_atom(Suite) ->
+ Str = atom_to_list(Suite),
+ case {Str, lists:reverse(Str)} of
+ {"et" ++ _, "ETIUS" ++ _} ->
+ Suite;
+ _ ->
+ list_to_atom("et_" ++ Str ++ "_SUITE")
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+config_fname() ->
+ "et_test_case_config".
+
+%% Read default config file
+read_config() ->
+ Fname = config_fname(),
+ et_test_lib:log("Consulting file ~s...~n", [Fname]),
+ case file:consult(Fname) of
+ {ok, Config} ->
+ et_test_lib:log("Read config ~w~n", [Config]),
+ Config;
+ _Error ->
+ Config = et_test_lib:default_config(),
+ et_test_lib:log("<>WARNING<> Using default config: ~w~n", [Config]),
+ Config
+ end.
+
+%% Write new default config file
+write_config(Config) when is_list(Config) ->
+ Fname = config_fname(),
+ {ok, Fd} = file:open(Fname, write),
+ write_list(Fd, Config),
+ file:close(Fd).
+
+write_list(Fd, [H | T]) ->
+ ok = io:format(Fd, "~p.~n",[H]),
+ write_list(Fd, T);
+write_list(_, []) ->
+ ok.
+
+test_case_fname() ->
+ "et_test_case_info".
+
+%% Read name of test case
+read_test_case() ->
+ Fname = test_case_fname(),
+ case file:open(Fname, [read]) of
+ {ok, Fd} ->
+ Res = io:read(Fd, []),
+ file:close(Fd),
+ case Res of
+ {ok, TestCase} ->
+ et_test_lib:log("Using test case ~w from file ~s~n",
+ [TestCase, Fname]),
+ TestCase;
+ {error, _} ->
+ default_test_case(Fname)
+ end;
+ {error, _} ->
+ default_test_case(Fname)
+ end.
+
+default_test_case(Fname) ->
+ TestCase = all,
+ et_test_lib:log("<>WARNING<> Cannot read file ~s, "
+ "using default test case: ~w~n",
+ [Fname, TestCase]),
+ TestCase.
+
+write_test_case(TestCase) ->
+ Fname = test_case_fname(),
+ {ok, Fd} = file:open(Fname, write),
+ ok = io:format(Fd, "~p.~n",[TestCase]),
+ file:close(Fd).
+
+append_test_case_info(TestCase, TestCaseInfo) ->
+ Fname = test_case_fname(),
+ {ok, Fd} = file:open(Fname, [read, write]),
+ ok = io:format(Fd, "~p.~n",[TestCase]),
+ ok = io:format(Fd, "~p.~n",[TestCaseInfo]),
+ file:close(Fd),
+ TestCaseInfo.