%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-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%
%%
%%%-------------------------------------------------------------------
%%% File : ethread_SUITE.erl
%%% Author : Rickard Green <[email protected]>
%%% Description :
%%%
%%% Created : 17 Jun 2004 by Rickard Green <[email protected]>
%%%-------------------------------------------------------------------
-module(ethread_SUITE).
-author('[email protected]').
%-define(line_trace, 1).
-define(DEFAULT_TIMEOUT, ?t:minutes(10)).
-export([all/1, init_per_testcase/2, fin_per_testcase/2]).
-export([create_join_thread/1,
equal_tids/1,
mutex/1,
try_lock_mutex/1,
recursive_mutex/1,
time_now/1,
cond_wait/1,
cond_timedwait/1,
broadcast/1,
detached_thread/1,
max_threads/1,
forksafety/1,
vfork/1,
tsd/1,
spinlock/1,
rwspinlock/1,
rwmutex/1,
atomic/1,
gate/1]).
-include_lib("test_server/include/test_server.hrl").
tests() ->
[create_join_thread,
equal_tids,
mutex,
try_lock_mutex,
recursive_mutex,
time_now,
cond_wait,
cond_timedwait,
broadcast,
detached_thread,
max_threads,
forksafety,
vfork,
tsd,
spinlock,
rwspinlock,
rwmutex,
atomic,
gate].
all(doc) -> [];
all(suite) -> tests().
%%
%%
%% The test-cases
%%
%%
create_join_thread(doc) ->
["Tests ethr_thr_create and ethr_thr_join."];
create_join_thread(suite) ->
[];
create_join_thread(Config) ->
run_case(Config, "create_join_thread", "").
equal_tids(doc) ->
["Tests ethr_equal_tids."];
equal_tids(suite) ->
[];
equal_tids(Config) ->
run_case(Config, "equal_tids", "").
mutex(doc) ->
["Tests mutexes."];
mutex(suite) ->
[];
mutex(Config) ->
run_case(Config, "mutex", "").
try_lock_mutex(doc) ->
["Tests try lock on mutex."];
try_lock_mutex(suite) ->
[];
try_lock_mutex(Config) ->
run_case(Config, "try_lock_mutex", "").
recursive_mutex(doc) ->
["Tests recursive mutexes."];
recursive_mutex(suite) ->
[];
recursive_mutex(Config) ->
run_case(Config, "recursive_mutex", "").
time_now(doc) ->
["Tests ethr_time_now by comparing time values with Erlang."];
time_now(suite) ->
[];
time_now(Config) ->
run_case(Config, "time_now", "", fun (P) ->
spawn_link(fun () ->
watchdog(P)
end)
end).
wd_dispatch(P) ->
receive
bye ->
?line true = port_command(P, "-1 "),
?line bye;
L when is_list(L) ->
?line true = port_command(P, L),
?line wd_dispatch(P)
end.
watchdog(Port) ->
?line process_flag(priority, max),
?line receive after 500 -> ok end,
?line random:seed(),
?line true = port_command(Port, "0 "),
?line lists:foreach(fun (T) ->
erlang:send_after(T,
self(),
integer_to_list(T)
++ " ")
end,
lists:usort(lists:map(fun (_) ->
random:uniform(4500)+500
end,
lists:duplicate(50,0)))),
?line erlang:send_after(5100, self(), bye),
wd_dispatch(Port).
cond_wait(doc) ->
["Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast."];
cond_wait(suite) ->
[];
cond_wait(Config) ->
run_case(Config, "cond_wait", "").
cond_timedwait(doc) ->
["Tests ethr_cond_timedwait with ethr_cond_signal and ethr_cond_broadcast."];
cond_timedwait(suite) ->
[];
cond_timedwait(Config) ->
run_case(Config, "cond_timedwait", "").
broadcast(doc) ->
["Tests that a ethr_cond_broadcast really wakes up all waiting threads"];
broadcast(suite) ->
[];
broadcast(Config) ->
run_case(Config, "broadcast", "").
detached_thread(doc) ->
["Tests detached threads."];
detached_thread(suite) ->
[];
detached_thread(Config) ->
run_case(Config, "detached_thread", "").
max_threads(doc) ->
["Tests maximum number of threads."];
max_threads(suite) ->
[];
max_threads(Config) ->
run_case(Config, "max_threads", "").
forksafety(doc) ->
["Tests forksafety."];
forksafety(suite) ->
[];
forksafety(Config) ->
run_case(Config, "forksafety", "").
vfork(doc) ->
["Tests vfork with threads."];
vfork(suite) ->
case ?t:os_type() of
{unix, osf1} ->
{skip, "vfork() known to hang multi-threaded applications on osf1"};
_ ->
[]
end;
vfork(Config) ->
run_case(Config, "vfork", "").
tsd(doc) ->
["Tests thread specific data."];
tsd(suite) ->
[];
tsd(Config) ->
run_case(Config, "tsd", "").
spinlock(doc) ->
["Tests spinlocks."];
spinlock(suite) ->
[];
spinlock(Config) ->
run_case(Config, "spinlock", "").
rwspinlock(doc) ->
["Tests rwspinlocks."];
rwspinlock(suite) ->
[];
rwspinlock(Config) ->
run_case(Config, "rwspinlock", "").
rwmutex(doc) ->
["Tests rwmutexes."];
rwmutex(suite) ->
[];
rwmutex(Config) ->
run_case(Config, "rwmutex", "").
atomic(doc) ->
["Tests atomics."];
atomic(suite) ->
[];
atomic(Config) ->
run_case(Config, "atomic", "").
gate(doc) ->
["Tests gates."];
gate(suite) ->
[];
gate(Config) ->
run_case(Config, "gate", "").
%%
%%
%% Auxiliary functions
%%
%%
init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, Dog}|Config].
fin_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
-define(TESTPROG, "ethread_tests").
-define(FAILED_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$F,$A,$I,$L,$U,$R,$E).
-define(SKIPPED_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$S,$K,$I,$P).
-define(SUCCESS_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$S,$U,$C,$C,$E,$S,$S).
-define(PID_MARKER, $E,$T,$H,$R,$-,$T,$E,$S,$T,$-,$P,$I,$D).
port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) ->
?line process_flag(trap_exit, true),
?line Ref = erlang:monitor(process, EProc),
?line receive
{'DOWN', Ref, _, _, Reason} when is_tuple(Reason),
element(1, Reason)
== timetrap_timeout ->
?line Cmd = "kill -9 " ++ OSProc,
?line ?t:format("Test case timed out. "
"Trying to kill port program.~n"
" Executing: ~p~n", [Cmd]),
?line case os:cmd(Cmd) of
[] ->
ok;
OsCmdRes ->
?line ?t:format(" ~s", [OsCmdRes])
end;
{'DOWN', Ref, _, _, _} ->
%% OSProc is assumed to have terminated by itself
?line ok
end.
get_line(_Port, eol, Data) ->
?line Data;
get_line(Port, noeol, Data) ->
?line receive
{Port, {data, {Flag, NextData}}} ->
?line get_line(Port, Flag, Data ++ NextData);
{Port, eof} ->
?line ?t:fail(port_prog_unexpectedly_closed)
end.
read_case_data(Port, TestCase) ->
?line receive
{Port, {data, {eol, [?SUCCESS_MARKER]}}} ->
?line ok;
{Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} ->
?line {comment, get_line(Port, Flag, CommentStart)};
{Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} ->
?line {skipped, get_line(Port, Flag, CommentStart)};
{Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} ->
?line ?t:fail(get_line(Port, Flag, ReasonStart));
{Port, {data, {eol, [?PID_MARKER | PidStr]}}} ->
?line ?t:format("Port program pid: ~s~n", [PidStr]),
?line CaseProc = self(),
?line list_to_integer(PidStr), % Sanity check
spawn_opt(fun () ->
port_prog_killer(CaseProc, PidStr)
end,
[{priority, max}, link]),
read_case_data(Port, TestCase);
{Port, {data, {Flag, LineStart}}} ->
?line ?t:format("~s~n", [get_line(Port, Flag, LineStart)]),
read_case_data(Port, TestCase);
{Port, eof} ->
?line ?t:fail(port_prog_unexpectedly_closed)
end.
run_case(Config, Test, TestArgs) ->
run_case(Config, Test, TestArgs, fun (_Port) -> ok end).
run_case(Config, Test, TestArgs, Fun) ->
TestProg = filename:join([?config(data_dir, Config), ?TESTPROG]),
Cmd = TestProg ++ " " ++ Test ++ " " ++ TestArgs,
case catch open_port({spawn, Cmd}, [stream,
use_stdio,
stderr_to_stdout,
eof,
{line, 1024}]) of
Port when is_port(Port) ->
?line Fun(Port),
?line CaseResult = read_case_data(Port, Test),
?line receive
{Port, eof} ->
?line ok
end,
?line CaseResult;
Error ->
?line ?t:fail({open_port_failed, Error})
end.