diff options
Diffstat (limited to 'lib/stdlib/test')
| -rw-r--r-- | lib/stdlib/test/erl_internal_SUITE.erl | 11 | ||||
| -rw-r--r-- | lib/stdlib/test/gen_statem_SUITE.erl | 139 | ||||
| -rw-r--r-- | lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl | 37 | ||||
| -rw-r--r-- | lib/stdlib/test/qlc_SUITE.erl | 85 | ||||
| -rw-r--r-- | lib/stdlib/test/shell_SUITE.erl | 93 | ||||
| -rw-r--r-- | lib/stdlib/test/sofs_SUITE.erl | 4 |
6 files changed, 328 insertions, 41 deletions
diff --git a/lib/stdlib/test/erl_internal_SUITE.erl b/lib/stdlib/test/erl_internal_SUITE.erl index 099f21f905..789a9d4363 100644 --- a/lib/stdlib/test/erl_internal_SUITE.erl +++ b/lib/stdlib/test/erl_internal_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ end_per_testcase(_Case, _Config) -> %% Check that the behaviour callbacks are correctly defined. behav(_) -> Modules = [application, gen_server, gen_fsm, gen_event, - supervisor_bridge, supervisor], + gen_statem, supervisor_bridge, supervisor], lists:foreach(fun check_behav/1, Modules). check_behav(Module) -> @@ -89,6 +89,10 @@ callbacks(gen_event) -> [{init,1}, {handle_event,2}, {handle_call,2}, {handle_info,2}, {terminate,2}, {code_change,3}, {format_status,2}]; +callbacks(gen_statem) -> + [{init, 1}, {callback_mode, 0}, {state_name, 3}, + {handle_event, 4}, {terminate, 3}, {code_change, 4}, + {format_status, 2}]; callbacks(supervisor_bridge) -> [{init,1}, {terminate,2}]; callbacks(supervisor) -> @@ -102,6 +106,9 @@ optional_callbacks(gen_fsm) -> [{handle_info, 3}, {terminate, 3}, {code_change, 4}, {format_status, 2}]; optional_callbacks(gen_event) -> [{handle_info, 2}, {terminate, 2}, {code_change, 3}, {format_status, 2}]; +optional_callbacks(gen_statem) -> + [{state_name, 3}, {handle_event, 4}, + {terminate, 3}, {code_change, 4}, {format_status, 2}]; optional_callbacks(supervisor_bridge) -> []; optional_callbacks(supervisor) -> diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index ac27c9fc79..05934b3953 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2016. All Rights Reserved. +%% Copyright Ericsson AB 2016-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -38,9 +38,10 @@ all() -> {group, abnormal}, {group, abnormal_handle_event}, shutdown, stop_and_reply, state_enter, event_order, - state_timeout, event_types, code_change, + state_timeout, event_types, generic_timers, code_change, {group, sys}, - hibernate, enter_loop]. + hibernate, enter_loop, {group, undef_callbacks}, + undef_in_terminate]. groups() -> [{start, [], tcs(start)}, @@ -50,7 +51,8 @@ groups() -> {abnormal, [], tcs(abnormal)}, {abnormal_handle_event, [], tcs(abnormal)}, {sys, [], tcs(sys)}, - {sys_handle_event, [], tcs(sys)}]. + {sys_handle_event, [], tcs(sys)}, + {undef_callbacks, [], tcs(undef_callbacks)}]. tcs(start) -> [start1, start2, start3, start4, start5, start6, start7, @@ -62,8 +64,9 @@ tcs(abnormal) -> tcs(sys) -> [sys1, call_format_status, error_format_status, terminate_crash_format, - get_state, replace_state]. - + get_state, replace_state]; +tcs(undef_callbacks) -> + [undef_code_change, undef_terminate1, undef_terminate2]. init_per_suite(Config) -> Config. @@ -77,6 +80,11 @@ init_per_group(GroupName, Config) GroupName =:= abnormal_handle_event; GroupName =:= sys_handle_event -> [{callback_mode,handle_event_function}|Config]; +init_per_group(undef_callbacks, Config) -> + DataDir = ?config(data_dir, Config), + StatemPath = filename:join(DataDir, "oc_statem.erl"), + {ok, oc_statem} = compile:file(StatemPath), + Config; init_per_group(_GroupName, Config) -> Config. @@ -834,6 +842,7 @@ event_types(_Config) -> {next_event,timeout,3}, {next_event,info,4}, {next_event,cast,5}, + {next_event,{timeout,6}, 6}, {next_event,Call,Req}]} end, state1 => @@ -857,6 +866,10 @@ event_types(_Config) -> {next_state, state6, undefined} end, state6 => + fun ({timeout,6}, 6, undefined) -> + {next_state, state7, undefined} + end, + state7 => fun ({call,From}, stop, undefined) -> {stop_and_reply, shutdown, [{reply,From,stopped}]} @@ -884,6 +897,69 @@ event_types(_Config) -> +generic_timers(_Config) -> + process_flag(trap_exit, true), + + Machine = + %% Abusing the internal format of From... + #{init => + fun () -> + {ok, start, undefined} + end, + start => + fun ({call,_} = Call, Req, undefined) -> + {next_state, state1, undefined, + [{{timeout,a},1500,1}, + {state_timeout,1500,1}, + {{timeout,b},1000,1}, + {next_event,Call,Req}]} + end, + state1 => + fun ({call,_} = Call, Req, undefined) -> + T = erlang:monotonic_time(millisecond) + 500, + {next_state, state2, undefined, + [{{timeout,c},T,2,{abs,true}}, + {{timeout,d},0,2,[{abs,false}]}, + {timeout,0,2}, + {{timeout,b},infinity,2}, + {{timeout,a},1000,{Call,Req}}]} + end, + state2 => + fun ({timeout,d}, 2, undefined) -> + {next_state, state3, undefined} + end, + state3 => + fun ({timeout,c}, 2, undefined) -> + {next_state, state4, undefined} + end, + state4 => + fun ({timeout,a}, {{call,From},stop}, undefined) -> + {stop_and_reply, shutdown, + [{reply,From,stopped}]} + end}, + {ok,STM} = + gen_statem:start_link( + ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]), + + stopped = gen_statem:call(STM, stop), + receive + {'EXIT',STM,shutdown} -> + ok + after 500 -> + ct:fail(did_not_stop) + end, + + {noproc,_} = + ?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason), + case flush() of + [] -> + ok; + Other2 -> + ct:fail({unexpected,Other2}) + end. + + + sys1(Config) -> {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []), {status, Pid, {module,gen_statem}, _} = sys:get_status(Pid), @@ -1393,6 +1469,51 @@ enter_loop(Reg1, Reg2) -> gen_statem:enter_loop(?MODULE, [], state0, []) end. +undef_code_change(_Config) -> + {ok, Statem} = gen_statem:start(oc_statem, [], []), + {error, {'EXIT', + {undef, [{oc_statem, code_change, [_, _, _, _], _}|_]}}} + = fake_upgrade(Statem, oc_statem). + +fake_upgrade(Pid, Mod) -> + sys:suspend(Pid), + sys:replace_state(Pid, fun(State) -> {new, State} end), + Ret = sys:change_code(Pid, Mod, old_vsn, []), + ok = sys:resume(Pid), + Ret. + +undef_terminate1(_Config) -> + {ok, Statem} = gen_statem:start(oc_statem, [], []), + MRef = monitor(process, Statem), + ok = gen_statem:stop(Statem), + verify_down(Statem, MRef, normal), + ok. + +undef_terminate2(_Config) -> + Reason = {error, test}, + {ok, Statem} = oc_statem:start(), + MRef = monitor(process, Statem), + ok = gen_statem:stop(Statem, Reason, infinity), + verify_down(Statem, MRef, Reason). + +undef_in_terminate(_Config) -> + Data = {undef_in_terminate, {?MODULE, terminate}}, + {ok, Statem} = gen_statem:start(?MODULE, {data, Data}, []), + try + gen_statem:stop(Statem), + ct:fail(should_crash) + catch + exit:{undef, [{?MODULE, terminate, _, _}|_]} -> + ok + end. + +verify_down(Statem, MRef, Reason) -> + receive + {'DOWN', MRef, process, Statem, Reason} -> + ok + after 5000 -> + ct:fail(default_terminate_failed) + end. %% Test the order for multiple {next_event,T,C} next_events(Config) -> @@ -1571,6 +1692,9 @@ callback_mode() -> terminate(_, _State, crash_terminate) -> exit({crash,terminate}); +terminate(_, _State, {undef_in_terminate, {Mod, Fun}}) -> + Mod:Fun(), + ok; terminate({From,stopped}, State, _Data) -> From ! {self(),{stopped,State}}, ok; @@ -1597,8 +1721,9 @@ idle({call,From}, {delayed_answer,T}, Data) -> throw({keep_state,Data}) end; idle({call,From}, {timeout,Time}, _Data) -> + AbsTime = erlang:monotonic_time(millisecond) + Time, {next_state,timeout,{From,Time}, - {timeout,Time,idle}}; + {timeout,AbsTime,idle,[{abs,true}]}}; idle(cast, next_event, _Data) -> {next_state,next_events,[a,b,c], [{next_event,internal,a}, diff --git a/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl b/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl new file mode 100644 index 0000000000..27c9e0718d --- /dev/null +++ b/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl @@ -0,0 +1,37 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(oc_statem). + +-behaviour(gen_statem). + +%% API +-export([start/0]). + +%% gen_statem callbacks +-export([init/1, callback_mode/0]). + +start() -> + gen_statem:start({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + {ok, state_name, #{}}. + +callback_mode() -> + handle_event_function. diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 2b5d52287e..5e9e03e410 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -1240,6 +1240,17 @@ string_to_handle(Config) when is_list(Config) -> {'EXIT', {no_lookup_to_carry_out, _}} = (catch qlc:e(qlc:string_to_handle(Q, {lookup,true}, Bs2))), ets:delete(Ets), + + %% References can be scanned and parsed. + E2 = ets:new(test, [bag]), + Ref = make_ref(), + true = ets:insert(E2, [{Ref,Ref}]), + S2 = "[{Val1} || {Ref1, Val1} <- ets:table("++io_lib:write(E2)++")," + "Ref1 =:= Ref].", + Bs = erl_eval:add_binding('Ref', Ref, erl_eval:new_bindings()), + [{Ref}] = qlc:e(qlc:string_to_handle(S2, [], Bs)), + ets:delete(E2), + ok. %% table @@ -4321,7 +4332,18 @@ ets(Config) when is_list(Config) -> R = qlc:e(Q), ets:delete(E), [] = R">>] - end + end, + + <<"E2 = ets:new(test, [bag]), + Ref = make_ref(), + true = ets:insert(E2, [{Ref,Ref}]), + Q2 = qlc:q([{Val1} || + {Ref1, Val1} <- ets:table(E2), + Ref1 =:= Ref]), + S = qlc:info(Q2), + true = is_list(S), + [{Ref}] = qlc:e(Q2), + ets:delete(E2)">> ], @@ -7071,7 +7093,7 @@ otp_12946(Config) when is_list(Config) -> %% Examples from qlc(3). manpage(Config) when is_list(Config) -> - + dets:start(), ok = compile_gb_table(Config), Ts = [ @@ -7138,11 +7160,14 @@ manpage(Config) when is_list(Config) -> \" [{X,Z}|{W,Y}] <- V2\n\" \" ])\n\" \"end\", - Info = + Info1 = re:replace(qlc:info(Q), - \"table\\\\(-*[0-9]*\", + \"table\\\\(#Ref<[\\.0-9]*>\", \"table(_\", [{return,list},global]), - L = Info, + F = fun(C) -> C =/= $\n andalso C =/= $\s end, + Info = lists:filter(F, Info1), + L1 = lists:filter(F, L), + L1 = Info, ets:delete(E1), ets:delete(E2)">>, @@ -7445,10 +7470,10 @@ etsc(F, Opts, Objs) -> V. join_info(H) -> - {qlc, S, Options} = strip_qlc_call(H), + {{qlc, S, Options}, Bs} = strip_qlc_call2(H), %% "Hide" the call to qlc_pt from the test in run_test(). LoadedPT = code:is_loaded(qlc_pt), - QH = qlc:string_to_handle(S, Options), + QH = qlc:string_to_handle(S, Options, Bs), _ = [unload_pt() || false <- [LoadedPT]], % doesn't take long... case {join_info_count(H), join_info_count(QH)} of {N, N} -> @@ -7458,30 +7483,34 @@ join_info(H) -> end. strip_qlc_call(H) -> + {Expr, _Bs} = strip_qlc_call2(H), + Expr. + +strip_qlc_call2(H) -> S = qlc:info(H, {flat, false}), - {ok, Tokens, _EndLine} = erl_scan:string(S++"."), - {ok, [Expr]} = erl_parse:parse_exprs(Tokens), - case Expr of - {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC]} -> - {qlc, lists:flatten([erl_pp:expr(LC), "."]), []}; - {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC, Opts]} -> - {qlc, lists:flatten([erl_pp:expr(LC), "."]), - erl_parse:normalise(Opts)}; - {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}},_} -> - {match_spec, Expr}; - {call,_,{remote,_,{atom,_,M},{atom,_,table}},_} -> - {table, M, Expr}; - _ -> - [] - end. + {ok, Tokens, _EndLine} = erl_scan:string(S++".", 1, [text]), + {ok, [Expr], Bs} = lib:extended_parse_exprs(Tokens), + {case Expr of + {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC]} -> + {qlc, lists:flatten([erl_pp:expr(LC), "."]), []}; + {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC, Opts]} -> + {qlc, lists:flatten([erl_pp:expr(LC), "."]), + erl_parse:normalise(Opts)}; + {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}},_} -> + {match_spec, Expr}; + {call,_,{remote,_,{atom,_,M},{atom,_,table}},_} -> + {table, M, Expr}; + _ -> + [] + end, Bs}. -record(ji, {nmerge = 0, nlookup = 0, nnested_loop = 0, nkeysort = 0}). %% Counts join options and (all) calls to qlc:keysort(). join_info_count(H) -> S = qlc:info(H, {flat, false}), - {ok, Tokens, _EndLine} = erl_scan:string(S++"."), - {ok, [Expr]} = erl_parse:parse_exprs(Tokens), + {ok, Tokens, _EndLine} = erl_scan:string(S++".", 1, [text]), + {ok, [Expr], _Bs} = lib:extended_parse_exprs(Tokens), #ji{nmerge = Nmerge, nlookup = Nlookup, nkeysort = NKeysort, nnested_loop = Nnested_loop} = ji(Expr, #ji{}), @@ -7524,8 +7553,8 @@ lookup_keys({list,Q,_}, L) -> lookup_keys({generate,_,Q}, L) -> lookup_keys(Q, L); lookup_keys({table,Chars}, L) when is_list(Chars) -> - {ok, Tokens, _} = erl_scan:string(lists:flatten(Chars++".")), - {ok, [Expr]} = erl_parse:parse_exprs(Tokens), + {ok, Tokens, _} = erl_scan:string(lists:flatten(Chars++"."), 1, [text]), + {ok, [Expr], _Bs} = lib:extended_parse_exprs(Tokens), case Expr of {call,_,_,[_fun,AKs]} -> case erl_parse:normalise(AKs) of @@ -7842,7 +7871,7 @@ run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) -> {module, _} = code:load_abs(AbsFile, Mod), Ms0 = erlang:process_info(self(),messages), - Before = {{get(), ets:all(), Ms0}, pps()}, + Before = {{get(), lists:sort(ets:all()), Ms0}, pps()}, %% Prepare the check that the qlc module does not call qlc_pt. _ = [unload_pt() || {file, Name} <- [code:is_loaded(qlc_pt)], @@ -7874,7 +7903,7 @@ run_test(Config, Extra, Body) -> wait_for_expected(R, {Strict0,PPS0}=Before, SourceFile, Wait) -> Ms = erlang:process_info(self(),messages), - After = {_,PPS1} = {{get(), ets:all(), Ms}, pps()}, + After = {_,PPS1} = {{get(), lists:sort(ets:all()), Ms}, pps()}, case {R, After} of {ok, Before} -> ok; diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 56002dda25..99411bc8fd 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -31,7 +31,7 @@ progex_lc/1, progex_funs/1, otp_5990/1, otp_6166/1, otp_6554/1, otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1, - otp_14285/1]). + otp_14285/1, otp_14296/1]). -export([ start_restricted_from_shell/1, start_restricted_on_command_line/1,restricted_local/1]). @@ -92,7 +92,7 @@ groups() -> progex_funs]}, {tickets, [], [otp_5990, otp_6166, otp_6554, otp_7184, - otp_7232, otp_8393, otp_10302, otp_13719, otp_14285]}]. + otp_7232, otp_8393, otp_10302, otp_13719, otp_14285, otp_14296]}]. init_per_suite(Config) -> Config. @@ -2841,6 +2841,95 @@ otp_14285(Config) -> test_server:stop_node(Node), ok. +otp_14296(Config) when is_list(Config) -> + fun() -> + F = fun() -> a end, + LocalFun = term_to_string(F), + S = LocalFun ++ ".", + "1: syntax error before: Fun" = comm_err(S) + end(), + + fun() -> + F = fun mod:func/1, + ExternalFun = term_to_string(F), + S = ExternalFun ++ ".", + R = ExternalFun ++ ".\n", + R = t(S) + end(), + + fun() -> + UnknownPid = "<100000.0.0>", + S = UnknownPid ++ ".", + "1: syntax error before: '<'" = comm_err(S) + end(), + + fun() -> + KnownPid = term_to_string(self()), + S = KnownPid ++ ".", + R = KnownPid ++ ".\n", + R = t(S) + end(), + + fun() -> + Port = open_port({spawn, "ls"}, [line]), + KnownPort = erlang:port_to_list(Port), + S = KnownPort ++ ".", + R = KnownPort ++ ".\n", + R = t(S) + end(), + + fun() -> + UnknownPort = "#Port<100000.0>", + S = UnknownPort ++ ".", + "1: syntax error before: Port" = comm_err(S) + end(), + + fun() -> + UnknownRef = "#Ref<100000.0.0.0>", + S = UnknownRef ++ ".", + "1: syntax error before: Ref" = comm_err(S) + end(), + + fun() -> + KnownRef = term_to_string(make_ref()), + S = KnownRef ++ ".", + R = KnownRef ++ ".\n", + R = t(S) + end(), + + %% Test lib:extended_parse_term/1 + TF = fun(S) -> + {ok, Ts, _} = erl_scan:string(S++".", 1, [text]), + case lib:extended_parse_term(Ts) of + {ok, Term} -> Term; + {error, _}=Error -> Error + end + end, + Fun = fun m:f/1, + Fun = TF(term_to_string(Fun)), + Fun = TF("fun m:f/1"), + Pid = self(), + Pid = TF(term_to_string(Pid)), + Ref = make_ref(), + Ref = TF(term_to_string(Ref)), + Term = {[10, a], {"foo", []}, #{x => <<"bar">>}}, + Term = TF(lists:flatten(io_lib:format("~p", [Term]))), + {$a, F1, "foo"} = TF("{$a, 1.0, \"foo\"}"), + true = is_float(F1), + 3 = TF("+3"), + $a = TF("+$a"), + true = is_float(TF("+1.0")), + true = -3 =:= TF("-3"), + true = -$a =:= TF("-$a"), + true = is_float(TF("-1.0")), + {error, {_, _, ["syntax error"++_|_]}} = TF("{1"), + {error, {_,_,"bad term"}} = TF("fun() -> foo end"), + {error, {_,_,"bad term"}} = TF("1, 2"), + ok. + +term_to_string(T) -> + lists:flatten(io_lib:format("~w", [T])). + scan(B) -> F = fun(Ts) -> case erl_parse:parse_term(Ts) of diff --git a/lib/stdlib/test/sofs_SUITE.erl b/lib/stdlib/test/sofs_SUITE.erl index f67bf16f0f..39e56c6df6 100644 --- a/lib/stdlib/test/sofs_SUITE.erl +++ b/lib/stdlib/test/sofs_SUITE.erl @@ -1783,7 +1783,7 @@ multiple_relative_product(Conf) when is_list(Conf) -> ok. digraph(Conf) when is_list(Conf) -> - T0 = ets:all(), + T0 = lists:sort(ets:all()), E = empty_set(), R = relation([{a,b},{b,c},{c,d},{d,a}]), F = relation_to_family(R), @@ -1833,7 +1833,7 @@ digraph(Conf) when is_list(Conf) -> true -> ok end, - true = T0 == ets:all(), + true = T0 == lists:sort(ets:all()), ok. digraph_fail(ExitReason, Fail) -> |
