diff options
Diffstat (limited to 'lib/stdlib/test/queue_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/queue_SUITE.erl | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/lib/stdlib/test/queue_SUITE.erl b/lib/stdlib/test/queue_SUITE.erl new file mode 100644 index 0000000000..ec3080baa0 --- /dev/null +++ b/lib/stdlib/test/queue_SUITE.erl @@ -0,0 +1,604 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-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(queue_SUITE). +-export([all/1]). + +-export([do/1, to_list/1, io_test/1, op_test/1, error/1, oops/1]). + +-export([init_per_testcase/2, fin_per_testcase/2]). + +-include("test_server.hrl"). + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +init_per_testcase(_Case, Config) -> + ?line Dog = ?t:timetrap(?default_timeout), + [{watchdog, Dog} | Config]. +fin_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +all(doc) -> + ["Test cases for queue."]; +all(suite) -> + [do, to_list, io_test, op_test, error, oops]. + +do(doc) -> + [""]; +do(suite) -> + []; +do(Config) when list(Config) -> + ?line L = [{in, 1}, + {in, 2}, + {out, {value, 1}}, + {in, 3}, + {out, {value, 2}}, + {out, {value, 3}}, + {out, empty} + ], + + ?line E = queue:new(), + ?line [] = queue:to_list(E), + ?line Q = do_queue(E, L), + ?line true = queue:is_empty(Q), + ?line 0 = queue:len(Q), + ok. + +to_list(doc) -> + ["OTP-2701"]; +to_list(suite) -> + []; +to_list(Config) when list(Config) -> + ?line E = queue:new(), + ?line Q = do_queue(E, [{in, 1}, + {in, 2}, + {in, 3}, + {out, {value, 1}}, + {in, 4}, + {in, 5}]), + ?line true = queue:is_queue(Q), + ?line 4 = queue:len(Q), + ?line case queue:to_list(Q) of + [2,3,4,5] -> + ok; + Other1 -> + test_server:fail(Other1) + end, + ok. + +do_queue(Q, []) -> + Q; +do_queue(Q, [E | Rest]) -> + do_queue(do_queue_1(E, Q), Rest). + +do_queue_1({in, E}, Q) -> + queue:in(E, Q); +do_queue_1({out, E}, Q) -> + case queue:out(Q) of + {E, Q1} -> + Q1; + Other -> + test_server:fail({"out failed", E, Q, Other}) + end. + + +io_test(doc) -> + "Test input and output"; +io_test(suite) -> + []; +io_test(Config) when list(Config) -> + E = queue:new(), + do_io_test(E), + ok. + +do_io_test(E) -> + ?line [4,3,5] = + io([snoc,snoc,head,head,head,cons,cons,snoc], E, 1), + ?line [5,3,4] = + io([cons,cons,daeh,daeh,daeh,snoc,snoc,cons], E, 1), + ?line [4,3,5] = + io([in,in,out,out,out,in_r,in_r,in], E, 1), + ?line [5,3,4] = + io([in_r,in_r,out_r,out_r,out_r,in,in,in_r], E, 1), + %% + ?line [] = + io([snoc,snoc,head,snoc,snoc,head,head,snoc,head,head], E, 1), + ?line [] = + io([cons,cons,daeh,cons,cons,daeh,daeh,cons,daeh,daeh], E, 1), + ?line [] = + io([in,in,out,in,in,out,out,in,out,out], E, 1), + ?line [] = + io([in_r,in_r,out_r,in_r,in_r,out_r,out_r,in_r,out_r,out_r], + E, 1), + %% + ?line [5,6] = + io([snoc,snoc,snoc,head,head,snoc,snoc,snoc,head,head], E, 1), + ?line [6,5] = + io([cons,cons,cons,daeh,daeh,cons,cons,cons,daeh,daeh], E, 1), + ?line [5,6] = + io([in,in,in,out,out,in,in,in,out,out], E, 1), + ?line [6,5] = + io([in_r,in_r,in_r,out_r,out_r,in_r,in_r,in_r,out_r,out_r], + E, 1), + %% + ?line [5] = + io([snoc,head,head,snoc,head,snoc,head,snoc,head,snoc], E, 1), + ?line [5] = + io([cons,daeh,daeh,cons,daeh,cons,daeh,cons,daeh,cons], E, 1), + ?line [5] = + io([in,out,out,in,out,in,out,in,out,in], E, 1), + ?line [5] = + io([in_r,out_r,out_r,in_r,out_r,in_r,out_r,in_r,out_r,in_r], + E, 1), + %% + ?line [] = + io([snoc,head,snoc,snoc,head,head,snoc,snoc,snoc,head,head,head], + E, 1), + ?line [] = + io([cons,daeh,cons,cons,daeh,daeh,cons,cons,cons,daeh,daeh,daeh], + E, 1), + ?line [] = + io([in,out,in,in,out,out,in,in,in,out,out,out], + E, 1), + ?line [] = + io([in_r,out_r,in_r,in_r,out_r,out_r,in_r,in_r,in_r,out_r,out_r,out_r], + E, 1), + %% + ?line [3] = io([cons,cons,cons,snoc,daeh,daeh,daeh], E, 1), + ?line [3] = io([snoc,snoc,snoc,cons,head,head,head], E, 1), + ?line [3] = io([in,in,in,in_r,out,out,out], E, 1), + ?line [3] = io([in_r,in_r,in_r,in,out_r,out_r,out_r], E, 1), + %% + ?line Q2 = queue:join(queue:cons(1, E),queue:cons(2, E)), + ?line Q1 = queue:reverse(Q2), + ?line [1] = io([head], Q1, 3), + ?line [1] = io([out], Q1, 3), + ?line [1] = io([daeh], Q2, 3), + ?line [1] = io([out_r], Q2, 3), +% ?line [2] = io([cons,cons,snoc,daeh,daeh], [], 1), +% ?line [2] = io([snoc,snoc,cons,head,head], [], 1), +% ?line [2] = io([in,in,in_r,out,out], [], 1), +% ?line [2] = io([in_r,in_r,in,out_r,out_r], [], 1), + %% + ?line [2] = + io([in,peek,peek_r,drop,in_r,peek,peek_r,in,peek,peek_r,drop_r], E, 1), + %% Malformed queues UGLY-GUTS-ALL-OVER-THE-PLACE + ?line [2,1] = io([peek], {[1,2],[]}, 1), + ?line [1,2] = io([peek_r], {[],[1,2]}, 1), + %% + ok. + +%% Perform a list of operations to a queue. +%% Keep a reference queue on the side; just a list. +%% Compare the read values between the queues. +%% Return the resulting queue as a list. +%% Inserted values are increments of the previously inserted. +io(Ops, Q, X) -> + io(Ops, Q, queue:to_list(Q), X). + +io([head | Tail], Q, [], X) -> + true = queue:is_empty(Q), + {'EXIT',{empty,_}} = (catch {ok,queue:head(Q)}), + {'EXIT',{empty,_}} = (catch {ok,queue:tail(Q)}), + io(Tail, Q, [], X); +io([head | Tail], Q, [H | T], X) -> + H = queue:head(Q), + false = queue:is_empty(Q), + io(Tail, queue:tail(Q), T, X); +io([daeh | Tail], Q, [], X) -> + true = queue:is_empty(Q), + {'EXIT',{empty,_}} = (catch {ok,queue:daeh(Q)}), + {'EXIT',{empty,_}} = (catch {ok,queue:liat(Q)}), + {'EXIT',{empty,_}} = (catch {ok,queue:lait(Q)}), + io(Tail, Q, [], X); +io([daeh | Tail], Q, QQ, X) -> + H = queue:daeh(Q), + false = queue:is_empty(Q), + [H | T] = lists:reverse(QQ), + io(Tail, queue:liat(Q), lists:reverse(T), X); +io([out | Tail], Q, [], X) -> + {empty, Q1} = queue:out(Q), + io(Tail, Q1, [], X); +io([out | Tail], Q, [H | T], X) -> + {{value,H}, Q1} = queue:out(Q), + io(Tail, Q1, T, X); +io([out_r | Tail], Q, [], X) -> + {empty, Q1} = queue:out_r(Q), + io(Tail, Q1, [], X); +io([out_r | Tail], Q, QQ, X) -> + {{value,H}, Q1} = queue:out_r(Q), + [H | T] = lists:reverse(QQ), + io(Tail, Q1, lists:reverse(T), X); +io([cons | Tail], Q, QQ, X) -> + io(Tail, queue:cons(X,Q), [X|QQ], X+1); +io([snoc | Tail], Q, QQ, X) -> + io(Tail, queue:snoc(Q,X), QQ++[X], X+1); +io([in_r | Tail], Q, QQ, X) -> + io(Tail, queue:in_r(X,Q), [X|QQ], X+1); +io([in | Tail], Q, QQ, X) -> + io(Tail, queue:in(X,Q), QQ++[X], X+1); +io([peek | Tail], Q, [], X) -> + empty = queue:peek(Q), + io(Tail, Q, [], X); +io([peek | Tail], Q, [H|_]=Q0, X) -> + {value,H} = queue:peek(Q), + io(Tail, Q, Q0, X); +io([peek_r | Tail], Q, [], X) -> + empty = queue:peek_r(Q), + io(Tail, Q, [], X); +io([peek_r | Tail], Q, Q0, X) -> + E = lists:last(Q0), + {value,E} = queue:peek_r(Q), + io(Tail, Q, Q0, X); +io([drop | Tail], Q, [], X) -> + try queue:drop(Q) of + V -> + test_server:fail({?MODULE,?LINE,V}) + catch + error:empty -> + io(Tail, Q, [], X) + end; +io([drop | Tail], Q, [_ | T], X) -> + Q1 = queue:drop(Q), + io(Tail, Q1, T, X); +io([drop_r | Tail], Q, [], X) -> + try queue:drop_r(Q) of + V -> + test_server:fail({?MODULE,?LINE,V}) + catch + error:empty -> + io(Tail, Q, [], X) + end; +io([drop_r | Tail], Q, L, X) -> + io:format("~p~n", [{drop_r,Tail,Q,L,X}]), + Q1 = queue:drop_r(Q), + [_ | T] = lists:reverse(L), + io:format("~p~n", [{drop_r,Q1,T}]), + io(Tail, Q1, lists:reverse(T), X); +io([], Q, QQ, _X) -> + QQ = queue:to_list(Q), + Length = length(QQ), + Length = queue:len(Q), + QQ. + + +op_test(doc) -> + "Test operations on whole queues"; +op_test(suite) -> + []; +op_test(Config) when list(Config) -> + do_op_test(fun id/1), + ok. + +do_op_test(F) -> + ?line Len = 50, + ?line Len2 = 2*Len, + ?line L1 = lists:seq(1, Len), + ?line L1r = lists:reverse(L1), + ?line L2 = lists:seq(Len+1, Len2), + ?line L2r = lists:reverse(L2), + ?line L3 = L1++L2, + ?line L3r = L2r++L1r, + ?line Q0 = F(queue:new()), + ?line [] = queue:to_list(Q0), + ?line Q0 = F(queue:from_list([])), + ?line Q1 = F(queue:from_list(L1)), + ?line Q2 = F(queue:from_list(L2)), + ?line Q3 = F(queue:from_list(L3)), + ?line Len = queue:len(Q1), + ?line Len = queue:len(Q2), + ?line Len2 = queue:len(Q3), + ?line L1 = queue:to_list(Q1), + ?line L2 = queue:to_list(Q2), + ?line L3 = queue:to_list(Q3), + ?line Q3b = queue:join(Q0, queue:join(queue:join(Q1, Q2), Q0)), + ?line L3 = queue:to_list(Q3b), + ?line {Q0, Q3New1} = queue:split(0, Q3), + ?line L3 = queue:to_list(Q3New1), + ?line {Q3New2, Q0} = queue:split(Len2, Q3), + ?line L3 = queue:to_list(Q3New2), + ?line {Q1a, Q2a} = queue:split(Len, Q3), + ?line L1 = queue:to_list(Q1a), + ?line L2 = queue:to_list(Q2a), + ?line {Q3c, Q3d} = queue:split(2, Q3), + ?line L3 = queue:to_list(Q3c) ++ queue:to_list(Q3d), + ?line {Q1b, Q2b} = queue:split(Len, Q3b), + ?line L1 = queue:to_list(Q1b), + ?line L2 = queue:to_list(Q2b), + ?line Len = queue:len(Q1b), + ?line Len = queue:len(Q2b), + ?line Len2 = queue:len(Q3b), + ?line Q1r = queue:reverse(Q1), + ?line Q2r = queue:reverse(Q2), + ?line Q1ar = queue:reverse(Q1a), + ?line Q2ar = queue:reverse(Q2a), + ?line Q1br = queue:reverse(Q1b), + ?line Q2br = queue:reverse(Q2b), + ?line Q3br = queue:reverse(Q3b), + ?line L1r = queue:to_list(Q1r), + ?line L1r = queue:to_list(Q1ar), + ?line L1r = queue:to_list(Q1br), + ?line L2r = queue:to_list(Q2r), + ?line L2r = queue:to_list(Q2ar), + ?line L2r = queue:to_list(Q2br), + ?line L3r = queue:to_list(Q3br), + ?line Len = queue:len(Q1br), + ?line Len = queue:len(Q2br), + ?line Len2 = queue:len(Q3br), + ?line false = queue:member([], Q0), + ?line false = queue:member(0, Q0), + ?line false = queue:member(0, Q1), + ?line false = queue:member([], Q1), + ?line true = queue:member(1, Q1), + ?line false = queue:member(1.0, Q1), + ?line true = queue:member(Len, Q1), + %% + %% Additional coverage. + ?line {MyL1r,MyL2r} = lists:split(Len-2, L1r), + ?line MyQ0r = queue:reverse(F(queue:from_list(L1))), + ?line {MyQ1r,MyQ2r} = queue:split(Len-2, MyQ0r), + ?line MyL1r = queue:to_list(MyQ1r), + ?line MyL2r = queue:to_list(MyQ2r), + ?line MyQ3r = queue:filter( + fun (X) when X rem 4 >= 2 -> false; + (X) when X rem 8 == 0 -> [float(X),{X}]; + (X) when X rem 2 >= 1 -> [{X}]; + (_) -> true + end, MyQ1r), + ?line MyL3r = lists:flatten( + [if X rem 8 == 0 -> [float(X),{X}]; + X rem 2 >= 1 -> {X}; + true -> X + end || X <- MyL1r, + X rem 4 < 2]), + ?line MyL3r = queue:to_list(MyQ3r), + ?line MyQ4 = F(queue:from_list([11,22,33,44])), + ?line [11,22] = queue:to_list(queue:filter(fun(X) when X < 27 -> true; + (_) -> [] end, MyQ4)), + ?line [33,44] = queue:to_list(queue:filter(fun(X) when X < 27 -> false; + (X) -> [X] end, MyQ4)), + %% + ok. + +error(doc) -> + "Test queue errors"; +error(suite) -> + []; +error(Config) when list(Config) -> + do_error(fun id/1, illegal_queue), + do_error(fun id/1, {[],illegal_queue}), + do_error(fun id/1, {illegal_queue,[17]}), + ok. + +trycatch(F, Args) -> + trycatch(queue, F, Args). + +trycatch(M, F, Args) -> + try apply(M, F, Args) of + V -> {value,V} + catch + C:R -> {C,R} + end. + +do_error(F, IQ) -> + ?line io:format("Illegal Queue: ~p~n", [IQ]), + %% + ?line {error,badarg} = trycatch(in, [1, IQ]), + ?line {error,badarg} = trycatch(out, [IQ]), + ?line {error,badarg} = trycatch(in_r ,[1, IQ]), + ?line {error,badarg} = trycatch(out_r ,[IQ]), + ?line {error,badarg} = trycatch(to_list ,[IQ]), + %% + ?line {error,badarg} = trycatch(from_list, [no_list]), + ?line {error,badarg} = trycatch(is_empty, [IQ]), + ?line {error,badarg} = trycatch(len, [IQ]), + %% + ?line {error,badarg} = trycatch(cons, [1, IQ]), + ?line {error,badarg} = trycatch(head, [IQ]), + ?line {error,badarg} = trycatch(tail, [IQ]), + %% + ?line {error,badarg} = trycatch(snoc, [IQ, 1]), + ?line {error,badarg} = trycatch(last, [IQ]), + ?line {error,badarg} = trycatch(daeh, [IQ]), + ?line {error,badarg} = trycatch(liat, [IQ]), + ?line {error,badarg} = trycatch(lait, [IQ]), + ?line {error,badarg} = trycatch(init, [IQ]), + %% + ?line {error,badarg} = trycatch(reverse, [IQ]), + ?line {error,badarg} = trycatch(join, [F(queue:new()), IQ]), + ?line {error,badarg} = trycatch(join, [IQ, F(queue:new())]), + ?line {error,badarg} = trycatch(split, [17, IQ]), + ?line {error,badarg} = trycatch(head, [IQ]), + %% + ?line Q0 = F(queue:new()), + ?line {error,badarg} = trycatch(split, [1, Q0]), + ?line {error,badarg} = trycatch(split, [2, queue:snoc(Q0, 1)]), + %% + ?line {value,false} = trycatch(is_queue, [IQ]), + ?line {error,badarg} = trycatch(get, [IQ]), + ?line {error,badarg} = trycatch(peek, [IQ]), + ?line {error,badarg} = trycatch(peek_r, [IQ]), + ?line {error,badarg} = trycatch(filter, [fun id/1, IQ]), + ?line {error,badarg} = trycatch(filter, [no_fun, Q0]), + %% + ?line {error,badarg} = trycatch(member, [1, IQ]), + ok. + +id(X) -> + X. + +oops(doc) -> + "Test queue errors"; +oops(suite) -> + []; +oops(Config) when list(Config) -> + ?line N = 3142, + ?line Optab = optab(), + ?line Seed0 = random:seed0(), + ?line {Is,Seed} = random_list(N, tuple_size(Optab), Seed0, []), + ?line io:format("~p ", [Is]), + ?line QA = queue:new(), + ?line QB = {[]}, + ?line emul([QA], [QB], Seed, [element(I, Optab) || I <- Is]). + +optab() -> + {{new,[], q, fun () -> {[]} end}, + {is_queue,[q], v, fun (_) -> true end}, + {is_empty,[q], v, fun (Q) -> + case Q of + {[]} -> true; + _ -> false + end end}, + {len,[q], v, fun ({L}) -> length(L) end}, + {to_list,[q], v, fun ({L}) -> L end}, + {from_list,[l], q, fun (L) -> {L} end}, + {in,[t,q], q, fun (X,{L}) -> {L++[X]} end}, + {in_r,[t,q], q, fun (X,{L}) -> {[X|L]} end}, + {out,[q], {v,q}, fun ({L}=Q) -> + case L of + [] -> {empty,Q}; + [X|T] -> {{value,X},{T}} + end + end}, + {out_r,[q], {v,q}, fun ({L}=Q) -> + case L of + [] -> {empty,Q}; + _ -> + [X|R] = lists:reverse(L), + T = lists:reverse(R), + {{value,X},{T}} + end + end}, + {get,[q], v, fun ({[]}) -> erlang:error(empty); + ({[H|_]}) -> H + end}, + {get_r,[q], v, fun ({[]}) -> erlang:error(empty); + ({L}) -> lists:last(L) + end}, + {peek,[q], v, fun ({[]}) -> empty; + ({[H|_]}) -> {value,H} + end}, + {peek_r,[q], v, fun ({[]}) -> empty; + ({L}) -> {value,lists:last(L)} + end}, + {drop,[q], q, fun ({[]}) -> erlang:error(empty); + ({[_|T]}) -> {T} + end}, + {drop_r,[q], q, fun ({[]}) -> erlang:error(empty); + ({L}) -> [_|R] = lists:reverse(L), + {lists:reverse(R)} + end}, + {reverse,[q], q, fun ({L}) -> {lists:reverse(L)} end}, + {join,[q,q], q, fun ({L1}, {L2}) -> {L1++L2} end}, + {split,[n,q], {q,q}, fun (N, {L}) -> {L1,L2} = lists:split(N, L), + {{L1},{L2}} end}, + {member,[t,q], v, fun (X, {L}) -> lists:member(X, L) end} + }. + +emul(_, _, _, []) -> + ok; +emul(QsA0, QsB0, Seed0, [{Op,Ts,S,Fun}|Ops]) -> + {AsA,Seed} = args(Ts, QsA0, Seed0, []), + {AsB,Seed} = args(Ts, QsB0, Seed0, []), + io:format("~n% ~w % ~p ", [Op,AsA]), + io:format("% ~p :", [AsB]), + XX = call({queue,Op}, AsA), + YY = call(Fun, AsB), + case {XX,YY} of + {{value,X},{value,Y}} -> + {[Qa|_]=QsA,[{Lb}|_]=QsB} = chk(QsA0, QsB0, S, X, Y), + case queue:to_list(Qa) of + Lb -> + io:format("|~p| ", [Lb]), + emul(QsA, QsB, Seed, Ops); + La -> + throw({to_list,[XX,YY,Op,AsA,AsB,La,Lb]}) + end; + {Exception,Exception} -> + io:format("!~p! ", [Exception]), + emul(QsA0, QsB0, Seed, Ops); + _ -> + throw({diff,[XX,YY,Op,AsA,AsB]}) + end. + +args([], _, Seed, R) -> + {lists:reverse(R),Seed}; +args([q|Ts], [Q|Qs]=Qss, Seed, R) -> + args(Ts, if Qs =:= [] -> Qss; true -> Qs end, Seed, [Q|R]); +args([l|Ts], Qs, Seed0, R) -> + {N,Seed1} = random:uniform_s(17, Seed0), + {L,Seed} = random_list(N, 4711, Seed1, []), + args(Ts, Qs, Seed, [L|R]); +args([t|Ts], Qs, Seed0, R) -> + {T,Seed} = random:uniform_s(4711, Seed0), + args(Ts, Qs, Seed, [T|R]); +args([n|Ts], Qs, Seed0, R) -> + {N,Seed} = random:uniform_s(17, Seed0), + args(Ts, Qs, Seed, [N|R]). + +random_list(0, _, Seed, R) -> + {R,Seed}; +random_list(N, M, Seed0, R) -> + {X,Seed} = random:uniform_s(M, Seed0), + random_list(N-1, M, Seed, [X|R]). + +call(Func, As) -> + try case Func of + {M,F} -> apply(M, F, As); + _ -> apply(Func, As) + end of + V -> + {value,V} + catch + Class:Reason -> + {Class,Reason} + end. + +chk(QsA, QsB, v, X, X) -> + io:format("<~p> ", [X]), + {QsA,QsB}; +chk(_, _, v, X, Y) -> + throw({diff,v,[X,Y]}); +chk(QsA, QsB, q, Qa, {Lb}=Qb) -> + case queue:to_list(Qa) of + Lb -> + io:format("|~p| ", [Lb]), + {[Qa|QsA],[Qb|QsB]}; + La -> + throw({diff,q,[Qa,La,Lb]}) + end; +chk(QsA, QsB, T, X, Y) + when tuple_size(T) =:= tuple_size(X), tuple_size(T) =:= tuple_size(Y) -> + io:format("{"), + try + chk_tuple(QsA, QsB, T, X, Y, 1) + after + io:format("}") + end; +chk(_, _, T, X, Y) + when is_tuple(T), is_tuple(X), is_tuple(Y) -> + throw({diff,T,[X,Y]}). + +chk_tuple(QsA, QsB, T, _, _, N) when N > tuple_size(T) -> + {QsA,QsB}; +chk_tuple(QsA0, QsB0, T, X, Y, N) -> + {QsA,QsB} = chk(QsA0, QsB0, element(N, T), element(N, X), element(N, Y)), + chk_tuple(QsA, QsB, T, X, Y, N+1). |