-module(test).
-export([test/0, slave/2]).
-import(

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
linda  % <-- CHANGE THIS TO YOUR TUPLESPACE MODULE NAME 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
,[in/2,out/2,new/0]
). 

%% dave Oct 2000
%% karol Jan 2010

%% Module for performing a quick test on lab3 module.
%% To test your lab, compile it: 
%% c(linda).
%% then compile the test: 
%% c(test).
%% then run:
%% test:test().

%% Here is a run of the test on a correct tuplespace:

%% (node@chalmers)1> c(test).
%% {ok,test}
%% (node@chalmers)2> test:test().
%% TEST: new tuplespace TS created
%% TEST: in(TS, {fish,any}) by process <0.46.0>
%% TEST: in(TS, {fowl,any}) by process <0.47.0>
%% TEST: out(TS, {fish,salmon})
%% TEST: out(TS, {fowl,chicken})
%% Process <0.47.0>
%%      Correct. in operation: {fowl,any} returned tuple: {fowl,chicken}
%% Process <0.46.0>
%%      Correct. in operation: {fish,any} returned tuple: {fish,salmon}
%% TEST: out(TS, {fish,chips})
%% TEST: in(TS, {any,chips}) by process <0.48.0>
%% Process <0.48.0>
%%      Correct. in operation: {any,chips} returned tuple: {fish,chips}
%% TEST: in(TS, {any,any}) by process <0.49.0>
%% Correct. Tuplespace appears to be empty.
%% finished
%% (node@chalmers)3> 

test() ->
    Delay = 500,
    process_flag(trap_exit, true),
    TS = new(),
    link(TS),
    io:format("TEST: new tuplespace TS created~n", []),

    Slave1 = spawn_in_test(TS, {fish,any}),
    sleep(Delay),

    Slave2 = spawn_in_test(TS, {fowl,any}),
    sleep(Delay),

    out_test(TS, {fish,salmon}),
    sleep(Delay),

    out_test(TS, {fowl,chicken}), 
    sleep(Delay),

    replytest(Slave2, {fowl,any}, {fowl,chicken}),
    sleep(Delay),

    replytest(Slave1, {fish,any}, {fish,salmon}),
    sleep(Delay),

    out_test(TS, {fish,chips}), 
    sleep(Delay),

    Slave3 = spawn_in_test(TS, {any,chips}),
    sleep(Delay),

    replytest(Slave3, {any,chips}, {fish,chips}),
    sleep(Delay),

    Slave4 = spawn_in_test(TS, {any,any}),
    sleep(Delay),

    receive
	{Slave4, Tup} ->
	    io:format("Error. Empty tuplespace, but received: ~w~n",[Tup])
    after
        1000 ->   
	    io:format("Correct. Tuplespace appears to be empty.~n"),
	    exit(Slave4, this_is_it),
	    exit(TS, this_is_it),
	    collect_exits([Slave1, Slave2, Slave3, Slave4, TS]),
	    finished
    end.

%%% Helper functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sleep(T) ->
    receive
    after
	T -> true
    end.

out_test(Tuplespace, Tup) ->
    io:format("TEST: out(TS, ~w)~n", [Tup]),
    out(Tuplespace, Tup).

% spawns a slave task to perform an in test. This function 
% returns the slave's Pid. The slave will forward the result of the 
% in operation to the caller.

spawn_in_test(Tuplespace, Pat) -> 
    S = spawn_link(test, slave, [Tuplespace, {self(), Pat}]),
    io:format("TEST: in(TS, ~w) by process ~w~n", [Pat, S]),
    S.

%% Get a tuple matching Item from Tuplespace T and send it to Pid
slave(T, {Pid,Item}) ->
    case in(T, Item) of
	R -> Pid!{self(), R}
    end.

%% Tests whether the reply from a Slave task matches the expected Tuple
replytest(Slave, Pat, Tup) -> 
    io:format("Process ~w~n", [Slave]),
    receive
	{Slave,Tup} ->
	    io:format("     Correct. in operation: ~w returned tuple: ~w~n", [Pat, Tup]);
        {Slave,Bad} ->
	    io:format("     Error. in with pattern: ~w returned tuple: ~w~n", [Pat, Bad])
    after 
        5000 ->   
	    io:format("     Error. No response for in operation with pattern: ~w~n", [Pat])
    end.

collect_exits([]) ->
    done;
collect_exits([Pid | Pids]) ->
    receive
	{'EXIT', Pid, _} ->
	    collect_exits(Pids)
    end.