aboutsummaryrefslogtreecommitdiffstats
path: root/lib/test_server/src/things
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/test_server/src/things
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/test_server/src/things')
-rw-r--r--lib/test_server/src/things/distr_startup_SUITE.erl238
-rw-r--r--lib/test_server/src/things/mnesia_power_SUITE.erl125
-rw-r--r--lib/test_server/src/things/random_kill_SUITE.erl81
-rw-r--r--lib/test_server/src/things/soft.gs.txt16
-rw-r--r--lib/test_server/src/things/verify.erl199
5 files changed, 659 insertions, 0 deletions
diff --git a/lib/test_server/src/things/distr_startup_SUITE.erl b/lib/test_server/src/things/distr_startup_SUITE.erl
new file mode 100644
index 0000000000..0d4e467570
--- /dev/null
+++ b/lib/test_server/src/things/distr_startup_SUITE.erl
@@ -0,0 +1,238 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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(distr_startup_SUITE).
+-compile([export_all]).
+%%-define(line_trace,1).
+-include("test_server.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all(suite) -> [reads,writes].
+
+-define(iterations,10000).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+app1() ->
+ {application, app1,
+ [{description, "ERTS CXC 138 10"},
+ {vsn, "2.0"},
+ {applications, [kernel, stdlib]},
+ {mod, {ch_sup, {app1, 1, 3}}}]}.
+
+app3() ->
+ {application, app3,
+ [{description, "ERTS CXC 138 10"},
+ {vsn, "2.0"},
+ {applications, [kernel, stdlib]},
+ {mod, {ch_sup, {app3, 7, 9}}}]}.
+
+
+config(Fd,C1,C2,C3) ->
+ io:format(Fd,
+ "[{kernel, [{sync_nodes_optional, ['~s','~s','~s']},"
+ "{sync_nodes_timeout, 1},"
+ "{distributed, [{app1, ['~s', '~s', '~s']},"
+ "{app2, 10000, ['~s', '~s', '~s']},"
+ "{app3, 5000, [{'~s', '~s'}, '~s']}]}]}].~n",
+ [C1,C2,C3, C1,C2,C3, C1,C2,C3, C1,C2,C3]).
+
+from(H, [H | T]) -> T;
+from(H, [_ | T]) -> from(H, T);
+from(H, []) -> [].
+
+%%-----------------------------------------------------------------
+%% Test suite for distributed applications, tests start, load
+%% etc indirectly.
+%% Should be started in a CC view with:
+%% erl -sname master -rsh ctrsh
+%%-----------------------------------------------------------------
+start_nodes(Conf) ->
+ % Write a config file
+ ?line Nodes = ?config(nodes,Conf),
+ ?line [C1,C2,C3|_] = Nodes, %% Need at least 3 nodes
+ ?line Dir = ?config(priv_dir,Conf),
+ ?line {ok, Fd} = file:open(Dir ++ "sys.config", write),
+ ?line config(Fd,C1,C2,C3),
+ ?line file:close(Fd),
+ ?line Config = Dir ++ "sys",
+
+ % Test [cp1, cp2, cp3]
+ ?line {ok, Cp1} = start_node(lists:nth(1,Nodes), Config),
+ ?line {ok, Cp2} = start_node(lists:nth(2,Nodes), Config),
+ ?line {ok, Cp3} = start_node(lists:nth(3,Nodes), Config),
+ % Start app1 and make sure cp1 starts it
+ %%?line rpc:multicall([Cp1, Cp2, Cp3], application, load, [app1()]),
+ %%?line rpc:multicall([Cp1, Cp2, Cp3], application, start,[app1,permanent]),
+ ?line test_server:sleep(1000),
+ {Cp1,Cp2,Cp3}.
+
+stop_nodes({Cp1,Cp2,Cp3}) ->
+ ?line stop_node(Cp1),
+ ?line stop_node(Cp2),
+ ?line stop_node(Cp3).
+
+start_node(NodeAtHost, Config) ->
+ ?line NodeAtHostStr = atom_to_list(NodeAtHost),
+ ?line HostStr = from($@,NodeAtHostStr),
+ ?line NodeStr = lists:reverse(from($@,lists:reverse(NodeAtHostStr))),
+ ?line Host = list_to_atom(HostStr),
+ ?line Node = list_to_atom(NodeStr),
+ ?line io:format("Launching slave node ~p@~p ~p",[Node,Host,Config]),
+ ?line slave:start(Host, Node, lists:concat(["-config ", Config])).
+
+stop_node(Node) ->
+ ?line rpc:cast(Node, erlang, halt, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_client_process(Cp,Mode,NodeNum) ->
+ io:format("Starting client process at ~p in mode ~p",[Cp,Mode]),
+ ?line case rpc:call(Cp, erlang, spawn,
+ [?MODULE, client,
+ [Mode,NodeNum,self(),random:uniform(1000)]]) of
+ {badrpc,Reason} ->
+ ?line exit({badrpc,{Cp,Reason}});
+ Client ->
+ ?line Client
+ end.
+
+start_clients(Mode,Conf) ->
+ ?line random:seed(4711,0,0),
+ ?line {Cp1,Cp2,Cp3} = start_nodes(Conf),
+ ?line Client1 = start_client_process(Cp1,Mode,1),
+ ?line Client2 = start_client_process(Cp2,Mode,2),
+ ?line Client3 = start_client_process(Cp3,Mode,3),
+ test_server:format(1,"All 3 nodes started, "
+ "power off client(s) any time...",[]),
+ Client1 ! go,
+ Client2 ! go,
+ Client3 ! go,
+ {{Cp1,Cp2,Cp3},{Client1,Client2,Client3}}.
+
+stop_clients(Cps) ->
+ test_server:format(1,"Test completed.",[]),
+ ?line stop_nodes(Cps).
+
+data() ->
+ {{self(),foo,bar,[1,2,3,4,5,6,7],{{{{}}}},
+ "We need pretty long packages, so that there is a big risk "
+ "of cutting it in the middle when suddenly turning off "
+ "the power or breaking the connection. "
+ "We don't check the contents of the data very much, but "
+ "at least there is a magic cookie at the end (123456)."
+ "If that one arrives correctly, the link is ok as far "
+ "as we are concerned."},
+ 123456}.
+
+reads(suite) -> [];
+reads(Conf) ->
+ ?line {Cps,_} = start_clients(w,Conf),
+ ?line read_loop(?iterations,0),
+ ?line stop_clients(Cps),
+ ok.
+
+read_loop(0,M) ->
+ ok;
+read_loop(N,M) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(0.5)),
+ M2 =
+ receive
+ {Node,Count,{_,123456}} ->
+ ?line setelement(Node,M,element(Node,M)+1);
+ {Node,Count,Data} ->
+ ?line exit({network_transmission_error,Data});
+ {nodedown,Node} ->
+ ?line test_server:format(1,"Node ~s went down",[Node]),
+ ?line M;
+ Other ->
+ ?line M
+ after test_server:seconds(0.1) ->
+ ?line io:format("No message!"),
+ ?line M
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ?line M3 =
+ case N rem 100 of
+ 0 -> io:format("~p reads to go (~w msgs)",[N,M2]),
+ {0,0,0};
+ _ -> M2
+ end,
+ ?line read_loop(N-1,M3).
+
+client(w,NodeNum,Pid,Seed) ->
+ random:seed(Seed,0,0),
+ receive
+ go -> ok
+ end,
+ client_write_loop(Pid,0,NodeNum,data());
+client(r,NodeNum,Pid,Seed) ->
+ random:seed(Seed,0,0),
+ receive
+ go -> ok
+ end,
+ client_read_loop(0).
+
+client_write_loop(Pid,N,NodeNum,Data) ->
+ test_server:sleep(random:uniform(20)),
+ Pid ! {NodeNum,N,Data},
+ client_write_loop(Pid,N+1,NodeNum,Data).
+
+writes(suite) -> [];
+writes(Conf) ->
+ ?line {Cps,{C1,C2,C3}} = start_clients(r,Conf),
+ ?line write_loop(2*?iterations,{C1,C2,C3},data()),
+ ?line stop_clients(Cps),
+ ok.
+
+write_loop(0,_,_) ->
+ ok;
+write_loop(N,Clients,Data) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(0.5)),
+ ?line Client = element(random:uniform(size(Clients)),Clients),
+ ?line Client ! {node(),N,Data},
+ ?line test_server:timetrap_cancel(Dog),
+ receive
+ {nodedown,Node} ->
+ ?line test_server:format(1,"Node ~s went down",[Node])
+ after 0 ->
+ ?line ok
+ end,
+ ?line case N rem 100 of
+ 0 -> io:format("~p writes to go",[N]);
+ _ -> ok
+ end,
+ ?line write_loop(N-1,Clients,Data).
+
+client_read_loop(N) ->
+ receive
+ {Node,Count,{_,123456}} ->
+ ?line ok;
+ {Node,Count,Data} ->
+ ?line io:format("~p(~p): transmission error from node ~p(~p): ~p",
+ [node(),N,Node,Count,Data]);
+ Other ->
+ ?line io:format("~p(~p): got a strange message: ~p",
+ [node(),N,Other])
+ end,
+ client_read_loop(N+1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
diff --git a/lib/test_server/src/things/mnesia_power_SUITE.erl b/lib/test_server/src/things/mnesia_power_SUITE.erl
new file mode 100644
index 0000000000..281dac7742
--- /dev/null
+++ b/lib/test_server/src/things/mnesia_power_SUITE.erl
@@ -0,0 +1,125 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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(mnesia_power_SUITE).
+-compile([export_all]).
+%%-define(line_trace,1).
+-include("test_server.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all(suite) -> [run].
+
+-define(iterations,3). %% nof power-off cycles to do before acceptance
+-define(rows,8). %% nof database rows to use (not too big, please)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(sum_table_1,{row,a,b,c,s}).
+
+run(suite) -> [];
+run(Config) ->
+ ?line mnesia:create_schema([node()]),
+ ?line mnesia:start(),
+ ?line mnesia:create_table([{name, sum_table_1}, {disc_copies,[node()]},
+ {attributes,record_info(fields,sum_table_1)}]),
+ ?line run_test(Config,?iterations).
+
+run(Config,N) ->
+ ?line mnesia:start(),
+ ?line check_consistency(sum_table_1),
+ case N of
+ 0 -> ?line ok;
+ N -> ?line run_test(Config,N)
+ end.
+
+run_test(Config,N) ->
+ ?line Pid1a = start_manipulator(sum_table_1),
+ ?line Pid1b = start_manipulator(sum_table_1),
+ ?line Pid1c = start_manipulator(sum_table_1),
+ ?line test_server:resume_point(?MODULE,run,[Config,N-1]),
+ ?line test_server:format(1,"Manipulating data like crazy now, "
+ "power off any time..."),
+ ?line test_server:sleep(infinity).
+
+start_manipulator(Table) ->
+ ?line spawn_link(?MODULE,manipulator_init,[Table]).
+
+manipulator_init(Table) ->
+ random:seed(4711,0,0),
+ manipulator(0,Table).
+
+manipulator(N,Table) ->
+ ?line Fun =
+ fun() ->
+ ?line Row = random:uniform(?rows),
+ ?line A = random:uniform(100000),
+ ?line B = random:uniform(100000),
+ ?line C = random:uniform(100000),
+ ?line Sum = A+B+C,
+ ?line case mnesia:write(#sum_table_1
+ {row=Row,a=A,b=B,c=C,s=Sum}) of
+ ok -> ok;
+ Other ->
+ ?line io:format("Trans failed: ~p\n",[Other])
+ end
+ end,
+ ?line mnesia:transaction(Fun),
+ case mnesia:table_info(sum_table_1,size) of
+ 0 -> exit(still_empty);
+ _ -> ok
+ end,
+ case N rem 2000 of
+ 0 -> io:format("~p did ~p operations",[self(),N]),
+ check_consistency(sum_table_1);
+ _ -> ok
+ end,
+ ?line manipulator(N+1,Table).
+
+check_consistency(Table) ->
+ io:format("Checking consistency of table ~p\n",[Table]),
+ All = mnesia:table_info(Table,wild_pattern),
+ ?line Fun =
+ fun() ->
+ mnesia:match_object(All)
+ end,
+ ?line case mnesia:transaction(Fun) of
+ {atomic,Val} ->
+ check_consistency_rows(Val,0);
+ Other ->
+ io:format("Trans failed: ~p\n",[Other]),
+ exit(failed),
+ check_consistency(Table)
+ end.
+
+check_consistency_rows([#sum_table_1{a=A,b=B,c=C,s=Sum}|Rows],N) ->
+ ?line Sum=A+B+C,
+ ?line check_consistency_rows(Rows,N+1);
+check_consistency_rows([],N) ->
+ io:format("All ~p rows were consistent\n",[N]),
+ {ok,N};
+check_consistency_rows(Thing,N) ->
+ io:format("Mnesia transaction returned:\n~p\n",[Thing]),
+ exit({bad_format,Thing}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+
+
diff --git a/lib/test_server/src/things/random_kill_SUITE.erl b/lib/test_server/src/things/random_kill_SUITE.erl
new file mode 100644
index 0000000000..4fcd63e3af
--- /dev/null
+++ b/lib/test_server/src/things/random_kill_SUITE.erl
@@ -0,0 +1,81 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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(random_kill_SUITE).
+-compile([export_all]).
+%%-define(line_trace,1).
+-include("test_server.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all(suite) -> [run].
+
+-define(iterations,25). %% Kill this many processes,
+ %% possibly with reboots in between
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+run(suite) -> [];
+run(Config) ->
+ registered(?iterations).
+
+registered(0) ->
+ ok;
+registered(N) ->
+ random:seed(3461*N,1159*N,351*N),
+ Pid = select_victim(registered),
+ test_server:resume_point(?MODULE,registered,[N-1]),
+ test_server:format("About to kill pid ~p (~p)\n~p",
+ [Pid,process_info(Pid,registered_name),info(Pid)]),
+ %%exit(Pid,kill),
+ registered(N-1).
+
+info(Pid) ->
+ Rest0 = tl(pid_to_list(Pid)),
+ {P1,Rest1} = get_until($.,Rest0),
+ {P2,Rest2} = get_until($.,Rest1),
+ {P3,_} = get_until($>,Rest2),
+ c:i(list_to_integer(P1),list_to_integer(P2),list_to_integer(P3)).
+
+get_until(Ch,L) ->
+ get_until(Ch,L,[]).
+get_until(Ch,[],Acc) ->
+ {lists:reverse(Acc),[]};
+get_until(Ch,[Ch|T],Acc) ->
+ {lists:reverse(Acc),T};
+get_until(Ch,[H|T],Acc) ->
+ get_until(Ch,T,[H|Acc]).
+
+select_victim(registered) ->
+ Pids =
+ lists:map(fun(Server)-> whereis(Server) end,registered()),
+ ImmunePids =
+ [self()|lists:map(fun(Job)-> element(2,Job) end,test_server:jobs())],
+ SuitablePids =
+ lists:filter(fun(Pid)-> case lists:member(Pid,ImmunePids) of
+ true -> false;
+ false -> true
+ end
+ end, Pids),
+ Selected = random:uniform(length(SuitablePids)),
+ io:format("Selected ~p if ~p",[Selected,length(SuitablePids)]),
+ lists:nth(Selected,SuitablePids).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
diff --git a/lib/test_server/src/things/soft.gs.txt b/lib/test_server/src/things/soft.gs.txt
new file mode 100644
index 0000000000..ec57884997
--- /dev/null
+++ b/lib/test_server/src/things/soft.gs.txt
@@ -0,0 +1,16 @@
+6> gs:start().
+RealTimeViolation, 478ms (after 1164 good)
+{1,<0.65.0>}
+RealTimeViolation, 352ms (after 0 good)
+RealTimeViolation, 492ms (after 0 good)
+RealTimeViolation, 166ms (after 0 good)
+RealTimeInfo, 18ms (after 7 good)
+RealTimeViolation, 115ms (after 13 good)
+7> application-specific initialization failed: couldn't connect to display ":0.0"
+RealTimeViolation, 20340ms (after 0 good)
+gs error: user backend died reason {port_handler,#Port,normal}
+
+RealTimeInfo, 31ms (after 21 good)
+RealTimeInfo, 21ms (after 69 good)
+RealTimeInfo, 21ms (after 119 good)
+RealTimeInfo, 21ms (after 169 good)
diff --git a/lib/test_server/src/things/verify.erl b/lib/test_server/src/things/verify.erl
new file mode 100644
index 0000000000..eac20c013e
--- /dev/null
+++ b/lib/test_server/src/things/verify.erl
@@ -0,0 +1,199 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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(verify).
+
+-export([dir/0, dir/1]).
+
+%% usage verify:dir()
+%% or verify:dir(Dir)
+%%
+%% runs tests on all files with the extension ".t1"
+%% creates an error log file verify.log in the directory where the
+%% tests were run
+
+-import(lists, [reverse/1, foldl/3, map/2]).
+
+dir() ->
+ dir(".").
+
+dir(Dir) ->
+ case file:list_dir(Dir) of
+ {ok, Files} ->
+ VFiles = collect_vers(Files, []),
+ VFiles1 = map(fun(F) -> Dir ++ "/" ++ F end, VFiles),
+ Nerrs = foldl(fun(F, Sum) ->
+ case file(F) of
+ {file,_,had,N,errors} ->
+ Sum + N;
+ no_errors ->
+ Sum;
+ Other ->
+ Sum + 1
+ end
+ end, 0, VFiles1),
+ case Nerrs of
+ 0 -> no_errors;
+ _ -> {dir,Dir,had,Nerrs,errors}
+ end;
+ _ ->
+ {error, cannot,list_dir, Dir}
+ end.
+
+collect_vers([H|T], L) ->
+ case reverse(H) of
+ [$1,$t,$.|T1] -> collect_vers(T, [reverse(T1)|L]);
+ _ -> collect_vers(T, L)
+ end;
+collect_vers([], L) ->
+ L.
+
+file(File) ->
+ case file:open(File ++ ".t1", read) of
+ {ok, S} ->
+ io:format("Verifying: ~s\n", [File]),
+ ErrFile = File ++ ".errs",
+ {ok, E} = file:open(ErrFile, write),
+ Bind0 = erl_eval:new_bindings(),
+ NErrs = do(S, {E, File, Bind0, 0}, 1),
+ file:close(S),
+ file:close(E),
+ case NErrs of
+ 0 ->
+ file:delete(ErrFile),
+ no_errors;
+ _ ->
+ {file,File,had,NErrs,errors}
+ end;
+ _ ->
+ error_in_opening_file
+ end.
+
+do(S, Env, Line) ->
+ R = io:scan_erl_exprs(S, '', Line),
+ do1(R, S, Env).
+
+do1({eof,_}, _, {_,_,_,NErrs}) ->
+ NErrs;
+do1({ok,Toks,Next}, S, Env0) ->
+ E1 = handle_toks(Toks, Next, Env0),
+ do(S, E1, Next);
+do1({error, {Line,Mod,Args}, Next}, S, E) ->
+ io:format("*** ~w ~p~n", [Line,Mod:format_error(Args)]),
+ E1 = add_error(E),
+ do(S, E1, Next).
+
+add_error({Stream, File, Bindings, N}) -> {Stream, File, Bindings, N+1}.
+
+handle_toks(Toks, Line, Env0) ->
+ %% io:format("Toks:~p\n", [Toks]).
+ case erl_parse:parse_exprs(Toks) of
+ {ok, Exprs} ->
+ %% io:format("Got:~p\n", [Exprs]),
+ eval(Exprs, Line, Env0);
+ {error, {LineNo, Mod, What}} ->
+ Str = apply(Mod, format_error, [What]),
+ io:format("*** Line:~w ***~s\n", [LineNo, Str]),
+ add_error(Env0);
+ Parse_error ->
+ io:format("Parse Error:~p\n",[Parse_error]),
+ add_error(Env0)
+ end.
+
+forget([{var,_,Name}], B0) -> erl_eval:del_binding(Name, B0);
+forget([], _) -> erl_eval:new_bindings().
+
+eval([{call,_,{atom,_,f}, Args}], _, {Stream, Bind0, Errs}) ->
+ Bind1 = forget(Args, Bind0),
+ {Stream, Bind1, Errs};
+eval(Exprs, Line, {Stream, File, Bind0, NErrs}) ->
+ %% io:format("Bindings >> ~p\n", [Bind0]),
+ %% io:format("Exprs >> ~p\n", [Exprs]),
+ case catch erl_eval:exprs(Exprs, Bind0) of
+ {'EXIT', Reason} ->
+ out_both(Stream, "----------------------------------~n", []),
+ out_both(Stream, "File:~s Error in:~s~n", [File, pp(Exprs)]),
+ print_bindings(Stream, Exprs, Bind0),
+ print_lhs(Stream, Exprs),
+ out_both(Stream, '*** Rhs evaluated to:~p~n',[rhs(Exprs, Bind0)]),
+ {Stream, File, Bind0, NErrs+1};
+ {value, _, Bind1} ->
+ {Stream, File, Bind1, NErrs}
+ end.
+
+pp([H]) -> erl_pp:expr(H);
+pp([H|T]) -> [erl_pp:expr(H),$,|pp(T)];
+pp([]) -> [].
+
+print_bindings(E, Form, Bindings) ->
+ case varsin(Form) of
+ [] ->
+ true;
+ Vars ->
+ print_vars(E, Vars, Bindings)
+ end.
+
+print_vars(E, [Var|T], Bindings) ->
+ case erl_eval:binding(Var, Bindings) of
+ {value, Val} ->
+ out_both(E, '~s = ~p\n',[Var, Val]);
+ unbound ->
+ out_both(E, '~s *is unbound*\n', [Var])
+ end,
+ print_vars(E, T, Bindings);
+print_vars(_, [], _) ->
+ true.
+
+
+out_both(E, Format, Data) ->
+ io:format(Format, Data),
+ io:format(E, Format, Data).
+
+print_lhs(E, [{match, _, Lhs, Rhs}]) ->
+ %% io:format(">>>> here:~w\n",[Lhs]),
+ out_both(E, '*** Lhs was:~s\n',[erl_pp:expr(Lhs)]);
+print_lhs(E, _) ->
+ out_both(E, '** UNDEFINED **', []).
+
+
+rhs([{match, _, Lhs, Rhs}], Bindings) ->
+ case catch erl_eval:exprs([Rhs], Bindings) of
+ {value, Val, _} -> Val;
+ Other -> undefined()
+ end;
+rhs(_, _) ->
+ undefined().
+
+varsin(X) -> varsin(X, []).
+
+varsin({var,_,'_'}, L) ->
+ L;
+varsin({var,_,V}, L) ->
+ case lists:member(V, L) of
+ true -> L;
+ false -> [V|L]
+ end;
+varsin([H|T], L) ->
+ varsin(T, varsin(H, L));
+varsin(T, L) when tuple(T) ->
+ varsin(tuple_to_list(T), L);
+varsin(_, L) ->
+ L.
+
+undefined() ->
+ '** UNDEFINED **'.