aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test')
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/binary_SUITE.erl239
-rw-r--r--erts/emulator/test/code_parallel_load_SUITE.erl36
-rw-r--r--erts/emulator/test/driver_SUITE.erl68
-rw-r--r--erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c4
-rw-r--r--erts/emulator/test/driver_SUITE_data/otp_9302_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c4
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c4
-rw-r--r--erts/emulator/test/exception_SUITE.erl11
-rw-r--r--erts/emulator/test/fun_SUITE.erl10
-rw-r--r--erts/emulator/test/hash_SUITE.erl1
-rw-r--r--erts/emulator/test/map_SUITE.erl1064
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl9
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src4
-rw-r--r--erts/emulator/test/nif_SUITE.erl86
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c184
-rw-r--r--erts/emulator/test/port_SUITE.erl69
-rw-r--r--erts/emulator/test/process_SUITE.erl219
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl2
-rw-r--r--erts/emulator/test/send_term_SUITE.erl19
-rw-r--r--erts/emulator/test/send_term_SUITE_data/send_term_drv.c72
21 files changed, 1929 insertions, 179 deletions
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index f02ca3cb98..0b0568c31a 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -68,6 +68,7 @@ MODULES= \
hash_SUITE \
hibernate_SUITE \
list_bif_SUITE \
+ map_SUITE \
match_spec_SUITE \
module_info_SUITE \
monitor_SUITE \
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index a340a805b5..a390c536bb 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. 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
@@ -58,10 +58,10 @@
ordering/1,unaligned_order/1,gc_test/1,
bit_sized_binary_sizes/1,
otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1,
- otp_8180/1, ttb_trap/1]).
+ otp_8180/1, trapping/1]).
%% Internal exports.
--export([sleeper/0,ttb_loop/2]).
+-export([sleeper/0,trapping_loop/4]).
suite() -> [{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,2}}].
@@ -76,7 +76,7 @@ all() ->
bad_term_to_binary, more_bad_terms, otp_5484, otp_5933,
ordering, unaligned_order, gc_test,
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
- obsolete_funs, robustness, otp_8180, ttb_trap].
+ obsolete_funs, robustness, otp_8180, trapping].
groups() ->
[].
@@ -447,26 +447,26 @@ terms(Config) when is_list(Config) ->
Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 ->
ok
end,
- Term = binary_to_term(Bin),
- Term = binary_to_term(Bin, [safe]),
+ Term = binary_to_term_stress(Bin),
+ Term = binary_to_term_stress(Bin, [safe]),
Unaligned = make_unaligned_sub_binary(Bin),
- Term = binary_to_term(Unaligned),
- Term = binary_to_term(Unaligned, []),
- Term = binary_to_term(Bin, [safe]),
+ Term = binary_to_term_stress(Unaligned),
+ Term = binary_to_term_stress(Unaligned, []),
+ Term = binary_to_term_stress(Bin, [safe]),
BinC = erlang:term_to_binary(Term, [compressed]),
- Term = binary_to_term(BinC),
+ Term = binary_to_term_stress(BinC),
true = size(BinC) =< size(Bin),
Bin = term_to_binary(Term, [{compressed,0}]),
terms_compression_levels(Term, size(Bin), 1),
UnalignedC = make_unaligned_sub_binary(BinC),
- Term = binary_to_term(UnalignedC)
+ Term = binary_to_term_stress(UnalignedC)
end,
?line test_terms(TestFun),
ok.
terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 ->
BinC = erlang:term_to_binary(Term, [{compressed,Level}]),
- Term = binary_to_term(BinC),
+ Term = binary_to_term_stress(BinC),
Sz = byte_size(BinC),
true = Sz =< UncompressedSz,
terms_compression_levels(Term, UncompressedSz, Level+1);
@@ -476,9 +476,9 @@ terms_float(Config) when is_list(Config) ->
?line test_floats(fun(Term) ->
Bin0 = term_to_binary(Term),
Bin0 = term_to_binary(Term, [{minor_version,0}]),
- Term = binary_to_term(Bin0),
+ Term = binary_to_term_stress(Bin0),
Bin1 = term_to_binary(Term, [{minor_version,1}]),
- Term = binary_to_term(Bin1),
+ Term = binary_to_term_stress(Bin1),
true = size(Bin1) < size(Bin0),
Size0 = erlang:external_size(Term),
Size00 = erlang:external_size(Term, [{minor_version, 0}]),
@@ -490,7 +490,7 @@ terms_float(Config) when is_list(Config) ->
float_middle_endian(Config) when is_list(Config) ->
%% Testing for roundtrip is not enough.
?line <<131,70,63,240,0,0,0,0,0,0>> = term_to_binary(1.0, [{minor_version,1}]),
- ?line 1.0 = binary_to_term(<<131,70,63,240,0,0,0,0,0,0>>).
+ ?line 1.0 = binary_to_term_stress(<<131,70,63,240,0,0,0,0,0,0>>).
external_size(Config) when is_list(Config) ->
%% Build a term whose external size only fits in a big num (on 32-bit CPU).
@@ -506,8 +506,8 @@ external_size(Config) when is_list(Config) ->
io:format("Unaligned size: ~p\n", [Sz2]),
?line ?t:fail()
end,
- ?line erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}]),
- ?line erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}]).
+ true = (erlang:external_size(Bin) =:= erlang:external_size(Bin, [{minor_version, 1}])),
+ true = (erlang:external_size(Unaligned) =:= erlang:external_size(Unaligned, [{minor_version, 1}])).
external_size_1(Term, Size0, Limit) when Size0 < Limit ->
case erlang:external_size(Term) of
@@ -608,10 +608,10 @@ bad_binary_to_term(Config) when is_list(Config) ->
ok.
bad_bin_to_term(BadBin) ->
- {'EXIT',{badarg,_}} = (catch binary_to_term(BadBin)).
+ {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin)).
bad_bin_to_term(BadBin,Opts) ->
- {'EXIT',{badarg,_}} = (catch binary_to_term(BadBin,Opts)).
+ {'EXIT',{badarg,_}} = (catch binary_to_term_stress(BadBin,Opts)).
safe_binary_to_term2(doc) -> "Test safety options for binary_to_term/2";
safe_binary_to_term2(Config) when is_list(Config) ->
@@ -622,7 +622,7 @@ safe_binary_to_term2(Config) when is_list(Config) ->
BadRef = <<131,114,0,3,BadHostAtom/binary,0,<<0,0,0,255>>/binary,
Empty/binary,Empty/binary>>,
?line bad_bin_to_term(BadRef, [safe]), % good ref, with a bad atom
- ?line fullsweep_after = binary_to_term(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom
+ ?line fullsweep_after = binary_to_term_stress(<<131,100,0,15,"fullsweep_after">>, [safe]), % should be a good atom
BadExtFun = <<131,113,100,0,4,98,108,117,101,100,0,4,109,111,111,110,97,3>>,
?line bad_bin_to_term(BadExtFun, [safe]),
ok.
@@ -679,14 +679,14 @@ corrupter0(Term) ->
corrupter(Bin, Pos) when Pos >= 0 ->
?line {ShorterBin, Rest} = split_binary(Bin, Pos),
- ?line catch binary_to_term(ShorterBin), %% emulator shouldn't crash
+ ?line catch binary_to_term_stress(ShorterBin), %% emulator shouldn't crash
?line MovedBin = list_to_binary([ShorterBin]),
- ?line catch binary_to_term(MovedBin), %% emulator shouldn't crash
+ ?line catch binary_to_term_stress(MovedBin), %% emulator shouldn't crash
%% Bit faults, shouldn't crash
<<Byte,Tail/binary>> = Rest,
Fun = fun(M) -> FaultyByte = Byte bxor M,
- catch binary_to_term(<<ShorterBin/binary,
+ catch binary_to_term_stress(<<ShorterBin/binary,
FaultyByte, Tail/binary>>) end,
?line lists:foreach(Fun,[1,2,4,8,16,32,64,128,255]),
?line corrupter(Bin, Pos-1);
@@ -700,7 +700,7 @@ more_bad_terms(Config) when is_list(Config) ->
?line ok = io:format("File: ~s\n", [BadFile]),
?line case file:read_file(BadFile) of
{ok,Bin} ->
- ?line {'EXIT',{badarg,_}} = (catch binary_to_term(Bin)),
+ ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin)),
ok;
Other ->
?line ?t:fail(Other)
@@ -709,7 +709,7 @@ more_bad_terms(Config) when is_list(Config) ->
otp_5484(Config) when is_list(Config) ->
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
<<131,
104,2, %Tuple, 2 elements
103, %Pid
@@ -722,7 +722,7 @@ otp_5484(Config) when is_list(Config) ->
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
<<131,
104,2, %Tuple, 2 elements
103, %Pid
@@ -734,13 +734,13 @@ otp_5484(Config) when is_list(Config) ->
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
%% A old-type fun in a list containing a bad creator pid.
<<131,108,0,0,0,1,117,0,0,0,0,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,255,255,0,25,255,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72,106>>)),
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
%% A new-type fun in a list containing a bad creator pid.
%%
<<131,
@@ -752,7 +752,7 @@ otp_5484(Config) when is_list(Config) ->
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
%% A new-type fun in a list containing a bad module.
<<131,
108,0,0,0,1, %List, 1 element
@@ -763,7 +763,7 @@ otp_5484(Config) when is_list(Config) ->
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
%% A new-type fun in a list containing a bad index.
<<131,
108,0,0,0,1, %List, 1 element
@@ -775,7 +775,7 @@ otp_5484(Config) when is_list(Config) ->
?line {'EXIT',_} =
(catch
- binary_to_term(
+ binary_to_term_stress(
%% A new-type fun in a list containing a bad unique value.
<<131,
108,0,0,0,1, %List, 1 element
@@ -788,46 +788,46 @@ otp_5484(Config) when is_list(Config) ->
%% An absurdly large atom.
?line {'EXIT',_} =
- (catch binary_to_term(iolist_to_binary([<<131,100,65000:16>>|
+ (catch binary_to_term_stress(iolist_to_binary([<<131,100,65000:16>>|
lists:duplicate(65000, 42)]))),
%% Longer than 255 characters.
?line {'EXIT',_} =
- (catch binary_to_term(iolist_to_binary([<<131,100,256:16>>|
+ (catch binary_to_term_stress(iolist_to_binary([<<131,100,256:16>>|
lists:duplicate(256, 42)]))),
%% OTP-7218. Thanks to Matthew Dempsky. Also make sure that we
%% cover the other error cases for external funs (EXPORT_EXT).
?line {'EXIT',_} =
- (catch binary_to_term(
+ (catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
97,13, %Integer: 13
97,13, %Integer: 13
97,13>>)), %Integer: 13
?line {'EXIT',_} =
- (catch binary_to_term(
+ (catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
97,13, %Integer: 13
97,13>>)), %Integer: 13
?line {'EXIT',_} =
- (catch binary_to_term(
+ (catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
100,0,1,64, %Atom: '@'
106>>)), %NIL
?line {'EXIT',_} =
- (catch binary_to_term(
+ (catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
100,0,1,64, %Atom: '@'
98,255,255,255,255>>)), %Integer: -1
?line {'EXIT',_} =
- (catch binary_to_term(
+ (catch binary_to_term_stress(
<<131,
113, %EXPORT_EXP
100,0,1,64, %Atom: '@'
@@ -835,7 +835,7 @@ otp_5484(Config) when is_list(Config) ->
113,97,13,97,13,97,13>>)), %fun 13:13/13
%% Bad funs.
- ?line {'EXIT',_} = (catch binary_to_term(fake_fun(0, lists:seq(0, 256)))),
+ ?line {'EXIT',_} = (catch binary_to_term_stress(fake_fun(0, lists:seq(0, 256)))),
ok.
fake_fun(Arity, Env0) ->
@@ -869,7 +869,7 @@ try_bad_lengths(B) ->
try_bad_lengths(B, L) when L > 16#FFFFFFF0 ->
Bin = <<B/binary,L:32>>,
io:format("~p\n", [Bin]),
- {'EXIT',_} = (catch binary_to_term(Bin)),
+ {'EXIT',_} = (catch binary_to_term_stress(Bin)),
try_bad_lengths(B, L-1);
try_bad_lengths(_, _) -> ok.
@@ -923,7 +923,7 @@ otp_6817_try_bin(Bin) ->
%% If the bug is present, the heap pointer will moved when the invalid term
%% is found and we will have a linked list passing through the limbo area
%% between the heap top and the stack pointer.
- catch binary_to_term(Bin),
+ catch binary_to_term_stress(Bin),
%% If the bug is present, we will overwrite the pointers in the limbo area.
Filler = erlang:make_tuple(1024, 16#3FA),
@@ -935,7 +935,7 @@ otp_6817_try_bin(Bin) ->
otp_8117(doc) -> "Some bugs in binary_to_term when 32-bit integers are negative.";
otp_8117(suite) -> [];
otp_8117(Config) when is_list(Config) ->
- [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',list,tuple],
+ [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',named_fun,list,tuple],
N <- lists:seq(0,31)],
ok.
@@ -944,6 +944,11 @@ otp_8117_do('fun',Neg) ->
FunBin = term_to_binary(fun() -> ok end),
?line <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
?line bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
+otp_8117_do(named_fun,Neg) ->
+ % Named fun with negative num_free
+ FunBin = term_to_binary(fun F() -> F end),
+ ?line <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin,
+ ?line bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>);
otp_8117_do(list,Neg) ->
%% List with negative length
?line bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>);
@@ -1233,24 +1238,35 @@ bit_sized_binary_sizes(Config) when is_list(Config) ->
bsbs_1(A) ->
BinSize = 32+A,
io:format("A: ~p BinSize: ~p", [A,BinSize]),
- Bin = binary_to_term(<<131,$M,5:32,A,0,0,0,0,0>>),
+ Bin = binary_to_term_stress(<<131,$M,5:32,A,0,0,0,0,0>>),
BinSize = bit_size(Bin).
+%% lists:foldl(_,_,lists:seq(_,_)) with less heap consumption
+lists_foldl_seq(Fun, Acc0, N, To) when N =< To ->
+ Acc1 = Fun(N, Acc0),
+ lists_foldl_seq(Fun, Acc1, N+1, To);
+
+lists_foldl_seq(_, Acc, _, _) ->
+ Acc.
+
deep(Config) when is_list(Config) ->
- ?line deep_roundtrip(lists:foldl(fun(E, A) ->
- [E,A]
- end, [], lists:seq(1, 1000000))),
- ?line deep_roundtrip(lists:foldl(fun(E, A) ->
- {E,A}
- end, [], lists:seq(1, 1000000))),
- ?line deep_roundtrip(lists:foldl(fun(E, A) ->
- fun() -> {E,A} end
- end, [], lists:seq(1, 1000000))),
+ deep_roundtrip(lists_foldl_seq(fun(E, A) ->
+ [E,A]
+ end, [], 1, 1000000)),
+ erlang:garbage_collect(),
+ deep_roundtrip(lists_foldl_seq(fun(E, A) ->
+ {E,A}
+ end, [], 1, 1000000)),
+ erlang:garbage_collect(),
+ deep_roundtrip(lists_foldl_seq(fun(E, A) ->
+ fun() -> {E,A} end
+ end, [], 1, 1000000)),
+ erlang:garbage_collect(),
ok.
deep_roundtrip(T) ->
B = term_to_binary(T),
- T = binary_to_term(B).
+ T = binary_to_term_stress(B).
obsolete_funs(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -1285,29 +1301,29 @@ obsolete_fun(Fun) ->
Tuple = no_fun_roundtrip(Fun).
no_fun_roundtrip(Term) ->
- binary_to_term(erts_debug:get_internal_state({term_to_binary_no_funs,Term})).
+ binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_no_funs,Term})).
%% Test non-standard encodings never generated by term_to_binary/1
%% but recognized by binary_to_term/1.
robustness(Config) when is_list(Config) ->
- ?line [] = binary_to_term(<<131,107,0,0>>), %Empty string.
- ?line [] = binary_to_term(<<131,108,0,0,0,0,106>>), %Zero-length list.
+ ?line [] = binary_to_term_stress(<<131,107,0,0>>), %Empty string.
+ ?line [] = binary_to_term_stress(<<131,108,0,0,0,0,106>>), %Zero-length list.
%% {[],a} where [] is a zero-length list.
- ?line {[],a} = binary_to_term(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>),
+ ?line {[],a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>),
%% {42,a} where 42 is a zero-length list with 42 in the tail.
- ?line {42,a} = binary_to_term(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>),
+ ?line {42,a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>),
%% {{x,y},a} where {x,y} is a zero-length list with {x,y} in the tail.
- ?line {{x,y},a} = binary_to_term(<<131,104,2,108,0,0,0,0,
+ ?line {{x,y},a} = binary_to_term_stress(<<131,104,2,108,0,0,0,0,
104,2,100,0,1,120,100,0,1,
121,100,0,1,97>>),
%% Bignums fitting in 32 bits.
- ?line 16#7FFFFFFF = binary_to_term(<<131,98,127,255,255,255>>),
- ?line -1 = binary_to_term(<<131,98,255,255,255,255>>),
+ ?line 16#7FFFFFFF = binary_to_term_stress(<<131,98,127,255,255,255>>),
+ ?line -1 = binary_to_term_stress(<<131,98,255,255,255,255>>),
ok.
@@ -1325,40 +1341,48 @@ run_otp_8180(Name) ->
?line {ok,Bins} = file:consult(Name),
[begin
io:format("~p\n", [Bin]),
- ?line {'EXIT',{badarg,_}} = (catch binary_to_term(Bin))
+ ?line {'EXIT',{badarg,_}} = (catch binary_to_term_stress(Bin))
end || Bin <- Bins],
ok.
-%% Test that exit and GC during term_to_binary trap does not crash.
-ttb_trap(Config) when is_list(Config)->
- case erlang:system_info(wordsize) of
- N when N < 8 ->
- {skipped, "Only on 64bit machines"};
- _ ->
- do_ttb_trap(5)
- end.
+%% Test that exit and GC during trapping term_to_binary and binary_to_term
+%% does not crash.
+trapping(Config) when is_list(Config)->
+ do_trapping(5, term_to_binary,
+ fun() -> [lists:duplicate(2000000,2000000)] end),
+ do_trapping(5, binary_to_term,
+ fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end).
-do_ttb_trap(0) ->
+do_trapping(0, _, _) ->
ok;
-do_ttb_trap(N) ->
- Pid = spawn(?MODULE,ttb_loop,[1000,self()]),
+do_trapping(N, Bif, ArgFun) ->
+ io:format("N=~p: Do ~p ~s gc.\n", [N, Bif, case N rem 2 of 0 -> "with"; 1 -> "without" end]),
+ Pid = spawn(?MODULE,trapping_loop,[Bif, ArgFun, 1000, self()]),
receive ok -> ok end,
receive after 100 -> ok end,
- erlang:garbage_collect(Pid),
- receive after 100 -> ok end,
+ Ref = make_ref(),
+ case N rem 2 of
+ 0 -> erlang:garbage_collect(Pid, [{async,Ref}]),
+ receive after 100 -> ok end;
+ 1 -> void
+ end,
exit(Pid,kill),
+ case N rem 2 of
+ 0 -> receive {garbage_collect, Ref, _} -> ok end;
+ 1 -> void
+ end,
receive after 1 -> ok end,
- do_ttb_trap(N-1).
+ do_trapping(N-1, Bif, ArgFun).
-ttb_loop(N,Pid) ->
- Term = lists:duplicate(2000000,2000000),
+trapping_loop(Bif, ArgFun, N, Pid) ->
+ Args = ArgFun(),
Pid ! ok,
- ttb_loop2(N,Term).
-ttb_loop2(0,_T) ->
+ trapping_loop2(Bif,Args,N).
+trapping_loop2(_,_,0) ->
ok;
-ttb_loop2(N,T) ->
- apply(erlang,term_to_binary,[T]),
- ttb_loop2(N-1,T).
+trapping_loop2(Bif,Args,N) ->
+ apply(erlang,Bif,Args),
+ trapping_loop2(Bif, Args, N-1).
%% Utilities.
@@ -1394,3 +1418,52 @@ unaligned_sub_bin(Bin0, Offs) ->
Bin.
id(I) -> I.
+
+
+%% Stress binary_to_term with different initial reductions
+binary_to_term_stress(Bin) ->
+ binary_to_term_stress(Bin, no_opts).
+
+binary_to_term_stress(Bin, Opts) ->
+ Reds = get_reds(),
+ T = b2t(erlang:system_info(context_reductions),
+ Bin, Opts, catch_binary_to_term(Bin, Opts)),
+ set_reds(Reds),
+ T = case Opts of
+ no_opts -> binary_to_term(Bin);
+ _ -> binary_to_term(Bin,Opts)
+ end.
+
+catch_binary_to_term(Bin, no_opts) ->
+ try binary_to_term(Bin)
+ catch
+ error:badarg -> binary_to_term_throws_badarg
+ end;
+catch_binary_to_term(Bin, Opts) ->
+ try binary_to_term(Bin, Opts)
+ catch
+ error:badarg -> binary_to_term_throws_badarg
+ end.
+
+b2t(0, _Bin, _Opts, Term) ->
+ Term;
+b2t(Reds, Bin, Opts, Term) ->
+ set_reds(Reds),
+ Term = catch_binary_to_term(Bin,Opts),
+ b2t(Reds div 3, Bin, Opts, Term).
+
+set_reds(Reds) ->
+ try erts_debug:set_internal_state(reds_left, Reds)
+ catch
+ error:undef ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ set_reds(Reds)
+ end.
+
+get_reds() ->
+ try erts_debug:get_internal_state(reds_left)
+ catch
+ error:undef ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ get_reds()
+ end.
diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl
index 1cfe015ea6..428f1242ab 100644
--- a/erts/emulator/test/code_parallel_load_SUITE.erl
+++ b/erts/emulator/test/code_parallel_load_SUITE.erl
@@ -159,22 +159,34 @@ setup_checkers(_,0) -> [];
setup_checkers(T,N) -> [spawn_link(fun() -> ?model:check(T) end) | setup_checkers(T, N-1)].
check_and_purge_processes_code(Pids, M) ->
- check_and_purge_processes_code(Pids, M, []).
-check_and_purge_processes_code([], M, []) ->
+ Tag = make_ref(),
+ N = request_cpc(Pids, M, Tag),
+ ok = handle_cpc_responses(N, Tag, M),
erlang:purge_module(M),
+ ok.
+
+request_cpc(Pid, M, Tag) when is_pid(Pid) ->
+ erlang:check_process_code(Pid, M, [{async, {Tag, Pid}}]),
+ 1;
+request_cpc(Pids, M, Tag) when is_list(Pids) ->
+ request_cpc(Pids, M, Tag, 0).
+
+request_cpc([], _M, _Tag, N) ->
+ N;
+request_cpc([Pid|Pids], M, Tag, N) ->
+ request_cpc(Pids, M, Tag, N + request_cpc(Pid, M, Tag)).
+
+handle_cpc_responses(0, _Tag, _Module) ->
ok;
-check_and_purge_processes_code([], M, Pending) ->
- io:format("Processes ~w are still executing old code - retrying.~n", [Pending]),
- check_and_purge_processes_code(Pending, M, []);
-check_and_purge_processes_code([Pid|Pids], M, Pending) ->
- case erlang:check_process_code(Pid, M) of
- false ->
- check_and_purge_processes_code(Pids, M, Pending);
- true ->
- check_and_purge_processes_code(Pids, M, [Pid|Pending])
+handle_cpc_responses(N, Tag, Module) ->
+ receive
+ {check_process_code, {Tag, _Pid}, false} ->
+ handle_cpc_responses(N-1, Tag, Module);
+ {check_process_code, {Tag, Pid}, true} ->
+ 1 = request_cpc(Pid, Module, Tag),
+ handle_cpc_responses(N, Tag, Module)
end.
-
generate(Module, Attributes, FunStrings) ->
FunForms = function_forms(FunStrings),
Forms = [
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 7087542899..06211406b4 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -2075,6 +2075,21 @@ thr_msg_blast(Config) when is_list(Config) ->
Res
end.
+-define(IN_RANGE(LoW_, VaLuE_, HiGh_),
+ case in_range(LoW_, VaLuE_, HiGh_) of
+ true -> ok;
+ false ->
+ case erlang:system_info(lock_checking) of
+ true ->
+ ?t:format("~p:~p: Ignore bad sched count due to "
+ "lock checking~n",
+ [?MODULE,?LINE]);
+ false ->
+ ?t:fail({unexpected_sched_counts, VaLuE_})
+ end
+ end).
+
+
consume_timeslice(Config) when is_list(Config) ->
%%
%% Verify that erl_drv_consume_timeslice() works.
@@ -2131,15 +2146,8 @@ consume_timeslice(Config) when is_list(Config) ->
Proc1 ! Go,
wait_command_msgs(Port, 10),
[{Port, Sprt1}, {Proc1, Sproc1}] = count_pp_sched_stop([Port, Proc1]),
- case Sprt1 of
- 10 ->
- true = in_range(5, Sproc1-10, 7);
- _ ->
- case erlang:system_info(lock_checking) of
- true -> ?t:format("Ignore bad sched count due to lock checking", []);
- false -> ?t:fail({unexpected_sched_counts, Sprt1, Sproc1})
- end
- end,
+ ?IN_RANGE(10, Sprt1, 10),
+ ?IN_RANGE(5, Sproc1-10, 7),
"disabled" = port_control(Port, $D, ""),
Proc2 = spawn_link(fun () ->
@@ -2160,15 +2168,8 @@ consume_timeslice(Config) when is_list(Config) ->
Proc2 ! Go,
wait_command_msgs(Port, 10),
[{Port, Sprt2}, {Proc2, Sproc2}] = count_pp_sched_stop([Port, Proc2]),
- case Sprt2 of
- 10 ->
- true = in_range(1, Sproc2-10, 2);
- _ ->
- case erlang:system_info(lock_checking) of
- true -> ?t:format("Ignore bad sched count due to lock checking", []);
- false -> ?t:fail({unexpected_sched_counts, Sprt2, Sproc2})
- end
- end,
+ ?IN_RANGE(10, Sprt2, 10),
+ ?IN_RANGE(1, Sproc2-10, 2),
"enabled" = port_control(Port, $E, ""),
Proc3 = spawn_link(fun () ->
@@ -2188,15 +2189,8 @@ consume_timeslice(Config) when is_list(Config) ->
Proc3 ! Go,
wait_command_msgs(Port, 10),
[{Port, Sprt3}, {Proc3, Sproc3}] = count_pp_sched_stop([Port, Proc3]),
- case Sprt3 of
- 10 ->
- true = in_range(5, Sproc3-10, 7);
- _ ->
- case erlang:system_info(lock_checking) of
- true -> ?t:format("Ignore bad sched count due to lock checking", []);
- false -> ?t:fail({unexpected_sched_counts, Sprt3, Sproc3})
- end
- end,
+ ?IN_RANGE(10, Sprt3, 10),
+ ?IN_RANGE(5, Sproc3-10, 7),
"disabled" = port_control(Port, $D, ""),
Proc4 = spawn_link(fun () ->
@@ -2216,15 +2210,8 @@ consume_timeslice(Config) when is_list(Config) ->
Proc4 ! Go,
wait_command_msgs(Port, 10),
[{Port, Sprt4}, {Proc4, Sproc4}] = count_pp_sched_stop([Port, Proc4]),
- case Sprt4 of
- 10 ->
- true = in_range(1, Sproc4-10, 2);
- _ ->
- case erlang:system_info(lock_checking) of
- true -> ?t:format("Ignore bad sched count due to lock checking", []);
- false -> ?t:fail({unexpected_sched_counts, Sprt4, Sproc4})
- end
- end,
+ ?IN_RANGE(10, Sprt4, 10),
+ ?IN_RANGE(1, Sproc4-10, 2),
SOnl = erlang:system_info(schedulers_online),
%% If only one scheduler use port with parallelism set to true,
@@ -2272,8 +2259,8 @@ consume_timeslice(Config) when is_list(Config) ->
wait_procs_exit([W5, Proc5]),
wait_command_msgs(Port2, 10),
[{Port2, Sprt5}, {Proc5, Sproc5}] = count_pp_sched_stop([Port2, Proc5]),
- true = in_range(2, Sproc5, 3),
- true = in_range(7, Sprt5, 20),
+ ?IN_RANGE(2, Sproc5, 3),
+ ?IN_RANGE(6, Sprt5, 20),
count_pp_sched_start(),
"disabled" = port_control(Port2, $D, ""),
@@ -2307,8 +2294,8 @@ consume_timeslice(Config) when is_list(Config) ->
wait_procs_exit([W6, Proc6]),
wait_command_msgs(Port2, 10),
[{Port2, Sprt6}, {Proc6, Sproc6}] = count_pp_sched_stop([Port2, Proc6]),
- true = in_range(2, Sproc6, 3),
- true = in_range(3, Sprt6, 6),
+ ?IN_RANGE(2, Sproc6, 3),
+ ?IN_RANGE(2, Sprt6, 6),
process_flag(scheduler, 0),
@@ -2316,6 +2303,7 @@ consume_timeslice(Config) when is_list(Config) ->
receive {Port2, closed} -> ok end,
ok.
+
wait_command_msgs(_, 0) ->
ok;
wait_command_msgs(Port, N) ->
diff --git a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
index 9d8bbac231..e2b338f801 100644
--- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
@@ -277,10 +277,6 @@ static void stop(ErlDrvData drv_data)
case IOQ_EXIT_TIMEOUT_ASYNC:
driver_cancel_timer(ddp->port);
break;
- case IOQ_EXIT_READY_ASYNC:
- if (ddp->outstanding_async_task)
- driver_async_cancel(ddp->async_task);
- break;
default:
break;
}
diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
index 88df73f696..7c144d20cf 100644
--- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
@@ -227,6 +227,4 @@ static void output(ErlDrvData drv_data,
ad[4]->term_data.msg = driver_mk_atom("end_of_jobs");
for (i = 0; i < sizeof(id)/sizeof(id[0]); i++)
id[i] = driver_async(data->port, &key, async_invoke, ad[i], driver_free);
- if (id[2] > 0)
- driver_async_cancel(id[2]);
}
diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c
index c22a415c59..e44c7dbd5e 100644
--- a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c
@@ -19,14 +19,14 @@
/*
* Author: Rickard Green
*
- * Description: Driver that fakes driver version 2.0 and tests
+ * Description: Driver that fakes driver version 3.0 and tests
* driver_system_info().
*
*/
#include "sys_info_drv_impl.h"
-#define SYS_INFO_DRV_MAJOR_VSN 2
+#define SYS_INFO_DRV_MAJOR_VSN 3
#define SYS_INFO_DRV_MINOR_VSN 0
#define SYS_INFO_DRV_NAME_STR "sys_info_base_drv"
#define SYS_INFO_DRV_NAME sys_info_base_drv
diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c
index 815d96cc97..63c69f751c 100644
--- a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c
@@ -19,14 +19,14 @@
/*
* Author: Rickard Green
*
- * Description: Driver that fakes driver version 2.0 and tests
+ * Description: Driver that fakes driver version 3.0 and tests
* driver_system_info().
*
*/
#include "sys_info_drv_impl.h"
-#define SYS_INFO_DRV_MAJOR_VSN 2
+#define SYS_INFO_DRV_MAJOR_VSN 3
#define SYS_INFO_DRV_MINOR_VSN 0
#define SYS_INFO_DRV_NAME_STR "sys_info_prev_drv"
#define SYS_INFO_DRV_NAME sys_info_prev_drv
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 109cec25cb..09a7a87a9a 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -589,6 +589,13 @@ line_numbers(Config) when is_list(Config) ->
[{file,ModFile},{line,_}]}|_]}} =
(catch build_binary2(8, bad_binary)),
+ <<"abc",357:16>> = build_binary3(<<"abc">>),
+ {'EXIT',{badarg,[{?MODULE,build_binary3,1,
+ [{file,"bit_syntax.erl"},{line,72511}]},
+ {?MODULE,line_numbers,1,
+ [{file,ModFile},{line,_}]}|_]}} =
+ (catch build_binary3(no_binary)),
+
{'EXIT',{function_clause,
[{?MODULE,do_call_abs,[y,y],
[{file,"gc_bif.erl"},{line,18}]},
@@ -691,6 +698,10 @@ build_binary2(Size, Bin) -> %Line 72505
id(0), %Line 72506
<<7:Size,Bin/binary>>. %Line 72507
+build_binary3(Bin) -> %Line 72509
+ id(0), %Line 72510
+ <<Bin/binary,357:16>>. %Line 72511
+
-file("gc_bif.erl", 17).
do_call_abs(x, Arg) -> %Line 18
abs(Arg). %Line 19
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index 36ba4e0f48..8ad5f290ed 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -262,6 +262,16 @@ equality(Config) when is_list(Config) ->
?line false = eq(FF2, FF4),
?line false = eq(FF3, FF4),
+ %% EEP37
+ H1 = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end,
+ H2 = fun Pow(N, M) when M > 0 -> N * Pow(N, M - 1); Pow(_, 0) -> 1 end,
+ H1_copy = copy_term(H1),
+
+ true = eq(H1, H1),
+ true = eq(H1, H1_copy),
+ true = eq(H2, H2),
+ false = eq(H1, H2),
+
ok.
eq(X, X) -> true;
diff --git a/erts/emulator/test/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl
index 738c9c8b16..647bb45049 100644
--- a/erts/emulator/test/hash_SUITE.erl
+++ b/erts/emulator/test/hash_SUITE.erl
@@ -1,4 +1,3 @@
-%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
new file mode 100644
index 0000000000..8cc5621181
--- /dev/null
+++ b/erts/emulator/test/map_SUITE.erl
@@ -0,0 +1,1064 @@
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. 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(map_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2
+ ]).
+
+-export([
+ t_build_and_match_literals/1,
+ t_update_literals/1,t_match_and_update_literals/1,
+ t_update_map_expressions/1,
+ t_update_assoc/1,t_update_exact/1,
+ t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1,
+ t_guard_receive/1, t_guard_fun/1,
+ t_list_comprehension/1,
+ t_map_sort_literals/1,
+ %t_size/1,
+ t_map_size/1,
+
+ %% Specific Map BIFs
+ t_bif_map_get/1,
+ t_bif_map_find/1,
+ t_bif_map_is_key/1,
+ t_bif_map_keys/1,
+ t_bif_map_merge/1,
+ t_bif_map_new/1,
+ t_bif_map_put/1,
+ t_bif_map_remove/1,
+ t_bif_map_update/1,
+ t_bif_map_values/1,
+ t_bif_map_to_list/1,
+ t_bif_map_from_list/1,
+
+ %% erlang
+ t_erlang_hash/1,
+ t_map_encode_decode/1,
+
+ %% maps module not bifs
+ t_maps_fold/1,
+ t_maps_map/1,
+ t_maps_size/1,
+ t_maps_without/1,
+
+ %% misc
+ t_pdict/1,
+ t_ets/1,
+ t_dets/1,
+ t_tracing/1
+ ]).
+
+-include_lib("stdlib/include/ms_transform.hrl").
+
+suite() -> [].
+
+all() -> [
+ t_build_and_match_literals,
+ t_update_literals, t_match_and_update_literals,
+ t_update_map_expressions,
+ t_update_assoc,t_update_exact,
+ t_guard_bifs, t_guard_sequence, t_guard_update,
+ t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_sort_literals,
+
+ %% Specific Map BIFs
+ t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
+ t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
+ t_bif_map_put,
+ t_bif_map_remove, t_bif_map_update,
+ t_bif_map_values,
+ t_bif_map_to_list, t_bif_map_from_list,
+
+ %% erlang
+ t_erlang_hash, t_map_encode_decode,
+ t_map_size,
+
+ %% maps module
+ t_maps_fold, t_maps_map,
+ t_maps_size, t_maps_without,
+
+
+ %% Other functions
+ t_pdict,
+ t_ets,
+ t_tracing
+ ].
+
+groups() -> [].
+
+init_per_suite(Config) -> Config.
+end_per_suite(_Config) -> ok.
+
+init_per_group(_GroupName, Config) -> Config.
+end_per_group(_GroupName, Config) -> Config.
+
+%% tests
+
+t_build_and_match_literals(Config) when is_list(Config) ->
+ #{} = id(#{}),
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% error case
+ %V = 32,
+ %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ ok.
+
+
+%% Tests size(Map).
+%% not implemented, perhaps it shouldn't be either
+
+%t_size(Config) when is_list(Config) ->
+% 0 = size(#{}),
+% 1 = size(#{a=>1}),
+% 1 = size(#{a=>#{a=>1}}),
+% 2 = size(#{a=>1, b=>2}),
+% 3 = size(#{a=>1, b=>2, b=>"3"}),
+% ok.
+
+t_map_size(Config) when is_list(Config) ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 1),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "c" => 2}, 2),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+% test map updates without matching
+t_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
+
+% test map updates with matching
+t_match_and_update_literals(Config) when is_list(Config) ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+
+t_update_map_expressions(Config) when is_list(Config) ->
+ M = maps:new(),
+ #{ a := 1 } = M#{a => 1},
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+
+t_update_assoc(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+
+ ok.
+
+t_update_exact(Config) when is_list(Config) ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ true = M2 =/= M0#{3=>right,3.0:=new},
+ #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new},
+
+ M3 = id(#{ 1 => val}),
+ #{1 := update2,1.0 := new_val4} = M3#{
+ 1.0 => new_val1, 1 := update, 1=> update3,
+ 1 := update2, 1.0 := new_val2, 1.0 => new_val3,
+ 1.0 => new_val4 },
+
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+
+ ok.
+
+t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ ok.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+t_guard_sequence(Config) when is_list(Config) ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+
+t_guard_update(Config) when is_list(Config) ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(_, _) -> error.
+
+t_guard_receive(Config) when is_list(Config) ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <<B1/binary, B2/binary>>,
+
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
+
+
+t_list_comprehension(Config) when is_list(Config) ->
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
+
+t_guard_fun(Config) when is_list(Config) ->
+ F1 = fun
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
+ ok.
+
+
+t_map_sort_literals(Config) when is_list(Config) ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = #{ a => 1 } < id(#{ b => 1}),
+ false = #{ b => 1 } < id(#{ a => 1}),
+ true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}),
+ true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
+ true = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
+ false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
+ false = #{ 1 => 1 } < id(#{ 1.0 => 1}),
+ false = #{ 1.0 => 1 } < id(#{ 1 => 1}),
+
+ %% value order
+ true = #{ a => 1 } < id(#{ a => 2}),
+ false = #{ a => 2 } < id(#{ a => 1}),
+ false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}),
+ true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}),
+
+ true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+%% BIFs
+t_bif_map_get(Config) when is_list(Config) ->
+
+ 1 = maps:get(a, #{ a=> 1}),
+ 2 = maps:get(b, #{ a=> 1, b => 2}),
+ "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}),
+ "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])),
+ {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})),
+ {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})),
+ ok.
+
+t_bif_map_find(Config) when is_list(Config) ->
+
+ {ok, 1} = maps:find(a, #{ a=> 1}),
+ {ok, 2} = maps:find(b, #{ a=> 1, b => 2}),
+ {ok, "int"} = maps:find(1, #{ 1 => "int"}),
+ {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}),
+
+ {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
+ {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),
+
+ M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
+ {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }),
+
+ %% error case
+ error = maps:find(a,#{}),
+ error = maps:find(a,#{b=>1, c=>2}),
+ error = maps:find(1.0, #{ 1 => "int"}),
+ error = maps:find(1, #{ 1.0 => "float"}),
+ error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key
+
+
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))),
+ {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))),
+ ok.
+
+
+t_bif_map_is_key(Config) when is_list(Config) ->
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(int, M1),
+ true = maps:is_key(<<"key">>, M1),
+ true = maps:is_key(4, M1),
+
+ false = maps:is_key(5, M1),
+ false = maps:is_key(<<"key2">>, M1),
+ false = maps:is_key("h", M1),
+ false = maps:is_key("hello", M1),
+ false = maps:is_key(atom, M1),
+ false = maps:is_key(any, id(#{})),
+
+ false = maps:is_key("hi", maps:remove("hi", M1)),
+ true = maps:is_key("hi", M1),
+ true = maps:is_key(1, maps:put(1, "number", M1)),
+ false = maps:is_key(1.0, maps:put(1, "number", M1)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))),
+ {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))),
+ ok.
+
+t_bif_map_keys(Config) when is_list(Config) ->
+ [] = maps:keys(#{}),
+
+ [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ [4,int,"hi",<<"key">>] = maps:keys(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])),
+ {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)),
+ ok.
+
+t_bif_map_new(Config) when is_list(Config) ->
+ #{} = maps:new(),
+ 0 = erlang:map_size(maps:new()),
+ ok.
+
+t_bif_map_merge(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:merge(#{},#{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}),
+
+ M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer },
+
+ #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0),
+
+ #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3,
+ {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))),
+ {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )),
+
+ ok.
+
+
+t_bif_map_put(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}),
+
+ ["hi"] = maps:keys(M1),
+ ["hello"] = maps:values(M1),
+
+ M2 = #{ int := 3 } = maps:put(int, 3, M1),
+
+ [int,"hi"] = maps:keys(M2),
+ [3,"hello"] = maps:values(M2),
+
+ M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),
+
+ [int,"hi",<<"key">>] = maps:keys(M3),
+ [3,"hello",<<"value">>] = maps:values(M3),
+
+ M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),
+
+ [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4),
+ [wat,3,"hello",<<"value">>] = maps:values(M4),
+
+ M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5),
+ [number,wat,3,"hello",<<"value">>] = maps:values(M5),
+
+ M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),
+
+ [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6),
+ [number,wat,3,"hello",<<"other value">>] = maps:values(M6),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])),
+ {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)),
+ ok.
+
+t_bif_map_remove(Config) when is_list(Config) ->
+ 0 = erlang:map_size(maps:remove(some_key, #{})),
+
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ M1 = maps:remove("hi", M0),
+ [4,18446744073709551629,int,<<"key">>] = maps:keys(M1),
+ [number,wat,3,<<"value">>] = maps:values(M1),
+
+ M2 = maps:remove(int, M1),
+ [4,18446744073709551629,<<"key">>] = maps:keys(M2),
+ [number,wat,<<"value">>] = maps:values(M2),
+
+ M3 = maps:remove(<<"key">>, M2),
+ [4,18446744073709551629] = maps:keys(M3),
+ [number,wat] = maps:values(M3),
+
+ M4 = maps:remove(18446744073709551629, M3),
+ [4] = maps:keys(M4),
+ [number] = maps:values(M4),
+
+ M5 = maps:remove(4, M4),
+ [] = maps:keys(M5),
+ [] = maps:values(M5),
+
+ M0 = maps:remove(5,M0),
+ M0 = maps:remove("hi there",M0),
+
+ #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])),
+ {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)),
+ ok.
+
+t_bif_map_update(Config) when is_list(Config) ->
+ M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+
+ #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0),
+
+ #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>,
+ 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0),
+
+ #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
+ 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)),
+ {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)),
+
+ ok.
+
+
+
+t_bif_map_values(Config) when is_list(Config) ->
+
+ [] = maps:values(#{}),
+
+ [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}),
+ [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}),
+
+ % values in key order: [4,int,"hi",<<"key">>]
+ M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
+ M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
+ [number,3,"hello2",<<"value2">>] = maps:values(M2),
+ [number,3,"hello",<<"value">>] = maps:values(M1),
+
+ %% error case
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])),
+ {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)),
+ ok.
+
+t_erlang_hash(Config) when is_list(Config) ->
+
+ ok = t_bif_erlang_phash2(),
+ ok = t_bif_erlang_phash(),
+ ok = t_bif_erlang_hash(),
+
+ ok.
+
+t_bif_erlang_phash2() ->
+
+ 39679005 = erlang:phash2(#{}),
+ 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }),
+ 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }),
+ 14363616 = erlang:phash2(#{ 1 => a }),
+ 51612236 = erlang:phash2(#{ a => 1 }),
+
+ 37468437 = erlang:phash2(#{{} => <<>>}),
+ 44049159 = erlang:phash2(#{<<>> => {}}),
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 118679416 = erlang:phash2(M0),
+ 51612236 = erlang:phash2(M1),
+ 118679416 = erlang:phash2(M2),
+ ok.
+
+t_bif_erlang_phash() ->
+ Sz = 1 bsl 32,
+ 268440612 = erlang:phash(#{},Sz),
+ 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 1394238263 = erlang:phash(#{ 1 => a },Sz),
+ 4066388227 = erlang:phash(#{ a => 1 },Sz),
+
+ 1578050717 = erlang:phash(#{{} => <<>>},Sz),
+ 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 3590546636 = erlang:phash(M0,Sz),
+ 4066388227 = erlang:phash(M1,Sz),
+ 3590546636 = erlang:phash(M2,Sz),
+ ok.
+
+t_bif_erlang_hash() ->
+ Sz = 1 bsl 27 - 1,
+ 5158 = erlang:hash(#{},Sz),
+ 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz),
+ 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz),
+ 126071654 = erlang:hash(#{ 1 => a },Sz),
+ 126426236 = erlang:hash(#{ a => 1 },Sz),
+
+ 101655720 = erlang:hash(#{{} => <<>>},Sz),
+ 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken
+
+ M0 = #{ a => 1, "key" => <<"value">> },
+ M1 = maps:remove("key",M0),
+ M2 = M1#{ "key" => <<"value">> },
+
+ 38260486 = erlang:hash(M0,Sz),
+ 126426236 = erlang:hash(M1,Sz),
+ 38260486 = erlang:hash(M2,Sz),
+ ok.
+
+
+t_map_encode_decode(Config) when is_list(Config) ->
+ <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}),
+ Pairs = [
+ {a,b},{"key","values"},{<<"key">>,<<"value">>},
+ {1,b},{[atom,1],{<<"wat">>,1,2,3}},
+ {aa,"values"},
+ {1 bsl 64 + (1 bsl 50 - 1), sc1},
+ {99, sc2},
+ {1 bsl 65 + (1 bsl 51 - 1), sc3},
+ {88, sc4},
+ {1 bsl 66 + (1 bsl 52 - 1), sc5},
+ {77, sc6},
+ {1 bsl 67 + (1 bsl 53 - 1), sc3},
+ {75, sc6}, {-10,sc8},
+ {<<>>, sc9}, {3.14158, sc10},
+ {[3.14158], sc11}, {more_atoms, sc12},
+ {{more_tuples}, sc13}, {self(), sc14},
+ {{},{}},{[],[]}
+ ],
+ ok = map_encode_decode_and_match(Pairs,[],#{}),
+
+ %% check sorting
+
+ %% literally #{ b=>2, a=>1 } in the internal order
+ #{ a:=1, b:=2 } =
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>),
+
+
+ %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
+ #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
+ 107,0,2,104,105, % "hi" :: list()
+ 100,0,1,97, % a :: atom()
+ 100,0,1,98, % b :: atom()
+ 107,0,5,118,97,108,117,101, % "value" :: list()
+ 97,33, % 33 :: integer()
+ 97,55 % 55 :: integer()
+ >>),
+
+
+ %% error cases
+ %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>>
+ %% which is: #{ a=>1, b=>1 }
+
+ %% uniqueness violation
+ %% literally #{ a=>1, "hi"=>"value", a=>2 }
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)),
+
+ %% bad size (too large)
+ {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
+ erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)),
+
+ %% bad size (too small) .. should fail just truncate it .. weird.
+ %% possibly change external format so truncated will be #{a:=1}
+ #{ a:=b } =
+ erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),
+
+ ok.
+
+map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
+ M1 = maps:put(K,V,M0),
+ B0 = erlang:term_to_binary(M1),
+ Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
+ %% sort Ks and Vs according to term spec, then match it
+ ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]),
+ %% decode and match it
+ M1 = erlang:binary_to_term(B0),
+ map_encode_decode_and_match(Pairs,Ls,M1);
+map_encode_decode_and_match([],_,_) -> ok.
+
+match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) ->
+ match_encoded_map(Encoded,Items);
+match_encoded_map(_,_,_) -> no_match_size.
+
+match_encoded_map(<<>>,[]) -> ok;
+match_encoded_map(Bin,[<<131,Item/binary>>|Items]) ->
+ Size = erlang:byte_size(Item),
+ <<EncodedTerm:Size/binary, Bin1/binary>> = Bin,
+ EncodedTerm = Item, %% Asssert
+ match_encoded_map(Bin1,Items).
+
+
+t_bif_map_to_list(Config) when is_list(Config) ->
+ [] = maps:to_list(#{}),
+ [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}),
+ [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}),
+ [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}),
+ [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}),
+
+ [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{
+ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
+ <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))),
+ ok.
+
+
+t_bif_map_from_list(Config) when is_list(Config) ->
+ #{} = maps:from_list([]),
+ A = maps:from_list([]),
+ 0 = erlang:map_size(A),
+
+ #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]),
+ #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]),
+ #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]),
+
+ #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]),
+
+ #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} =
+ maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),
+
+ #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} =
+ maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
+ {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),
+
+ %% error cases
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))),
+ {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
+ ok.
+
+%% Maps module, not BIFs
+t_maps_fold(_Config) ->
+ Vs = lists:seq(1,100),
+ M = maps:from_list([{{k,I},{v,I}}||I<-Vs]),
+
+ %% fold
+ 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M),
+
+ ok.
+
+t_maps_map(_Config) ->
+ Vs = lists:seq(1,100),
+ M1 = maps:from_list([{I,I}||I<-Vs]),
+ M2 = maps:from_list([{I,{token,I}}||I<-Vs]),
+
+ M2 = maps:map(fun(_K,V) -> {token,V} end, M1),
+ ok.
+
+t_maps_size(_Config) ->
+ Vs = lists:seq(1,100),
+ lists:foldl(fun(I,M) ->
+ M1 = maps:put(I,I,M),
+ I = maps:size(M1),
+ M1
+ end, #{}, Vs),
+ ok.
+
+
+t_maps_without(_Config) ->
+ Ki = [11,22,33,44,55,66,77,88,99],
+ M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
+ M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]),
+ M1 = maps:without([{k,I}||I <- Ki],M0),
+ ok.
+
+
+%% MISC
+t_pdict(_Config) ->
+
+ put(#{ a => b, b => a},#{ c => d}),
+ put(get(#{ a => b, b => a}),1),
+ 1 = get(#{ c => d}),
+ #{ c := d } = get(#{ a => b, b => a}).
+
+t_ets(_Config) ->
+
+ Tid = ets:new(map_table,[]),
+
+ [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)],
+
+
+ [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }),
+
+ %% Test equal
+ [3,4] = lists:sort(
+ ets:select(Tid,[{{'$1','$2'},
+ [{'or',{'==','$1',#{ 3 => -3 }},
+ {'==','$1',#{ 4 => -4 }}}],
+ ['$2']}])),
+ %% Test match
+ [30,50] = lists:sort(
+ ets:select(Tid,
+ [{{#{ 30 => -30}, '$1'},[],['$1']},
+ {{#{ 50 => -50}, '$1'},[],['$1']}]
+ )),
+
+ ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}),
+
+ %% Test equal with map of different size
+ [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]),
+
+ %% Test match with map of different size
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),
+
+ %%% Test match with don't care value
+ %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),
+
+ %% Test is_map bif
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{not_a_map,2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+ ets:insert(Tid,{{nope,a,tuple},2}),
+ 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
+
+ %% Test map_size bif
+ [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}],
+ [{map_size,'$1'}]}]),
+
+ true = ets:delete(Tid,#{50 => -50}),
+ [] = ets:lookup(Tid,#{50 => -50}),
+
+ ets:delete(Tid),
+ ok.
+
+t_dets(_Config) ->
+ ok.
+
+t_tracing(_Config) ->
+
+ dbg:stop_clear(),
+ {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}),
+ dbg:p(self(),c),
+
+ %% Test basic map call
+ {ok,_} = dbg:tpl(?MODULE,id,x),
+ id(#{ a => b }),
+ {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
+ {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test equals in argument list
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}],
+ [{return_trace}]}]),
+ id(#{ a => b }),
+ id(#{ b => c }),
+ {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
+ {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %% Test match in head
+ {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]),
+ id(#{ a => b }),
+ id(#{ b => c }),
+ {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ % Test map guard bifs
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]),
+ id(#{ a => b }),
+ id({1,2}),
+ id({#{ a => b},2}),
+ {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]),
+ id(#{ a => b }),
+ id({1,2}),
+ id({#{ a => b},2}),
+ id({#{ a => b, b => c},atom}),
+ {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer),
+ dbg:ctpl(),
+
+ %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end),
+ %dbg:tpl(?MODULE,id,MS),
+ %id(#{ a => b }),
+ %id(#{ b => c }),
+ %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
+ %dbg:ctpl(),
+
+ %% Check to extra messages
+ timeout = getmsg(Tracer),
+
+ dbg:stop_clear(),
+ ok.
+
+getmsg(_Tracer) ->
+ receive V -> V after 100 -> timeout end.
+
+trace_collector(Msg,Parent) ->
+ io:format("~p~n",[Msg]),
+ Parent ! Msg,
+ Parent.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index bcc46d78ba..330bef7104 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. 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
@@ -25,6 +25,7 @@
trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1,
ms_trace2/1, ms_trace3/1, boxed_and_small/1,
destructive_in_test_bif/1, guard_exceptions/1,
+ empty_list/1,
unary_plus/1, unary_minus/1, moving_labels/1]).
-export([fpe/1]).
-export([otp_9422/1]).
@@ -60,6 +61,7 @@ all() ->
guard_exceptions, unary_plus, unary_minus, fpe,
moving_labels,
faulty_seq_trace,
+ empty_list,
otp_9422];
true -> [not_run]
end.
@@ -897,6 +899,11 @@ fpe(Config) when is_list(Config) ->
_ -> ok
end.
+empty_list(Config) when is_list(Config) ->
+ Val=[{'$1',[], [{message,'$1'},{message,{caller}},{return_trace}]}],
+ %% Did crash debug VM in faulty assert:
+ erlang:match_spec_test([],Val,trace).
+
moving_labels(Config) when is_list(Config) ->
%% Force an andalso/orelse construction to be moved by placing it
%% in a tuple followed by a constant term. Labels should still
diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
index e65d99e968..04412280c0 100644
--- a/erts/emulator/test/mtx_SUITE_data/Makefile.src
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -17,8 +17,8 @@
# %CopyrightEnd%
#
-include @erts_lib_include_internal_generated@@[email protected]
-include @erts_lib_include_internal_generated@@DS@erts_internal.mk
+include @erts_lib_make_ethread@
+include @erts_lib_make_internal@
NIF_LIBS = mtx_SUITE@dll@
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 9a70e8646a..bcc1f9e5af 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. 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
@@ -29,14 +29,15 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,
end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1,
- types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
+ types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
+ maps/1,
api_macros/1,
from_array/1, iolist_as_binary/1, resource/1, resource_binary/1,
resource_takeover/1,
threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1,
is_checks/1,
get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
- otp_9668/1, consume_timeslice/1
+ otp_9668/1, consume_timeslice/1, dirty_nif/1
]).
-export([many_args_100/100]).
@@ -58,12 +59,12 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, reload, upgrade, heap_frag, types, many_args,
- binaries, get_string, get_atom, api_macros, from_array,
+ binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
resource_takeover, threading, send, send2, send3,
send_threaded, neg, is_checks, get_length, make_atom,
make_string,reverse_list_test,
- otp_9668, consume_timeslice
+ otp_9668, consume_timeslice, dirty_nif
].
groups() ->
@@ -435,6 +436,54 @@ get_atom(Config) when is_list(Config) ->
?line {0, <<>>} = atom_to_bin('',0),
ok.
+maps(doc) -> ["Test NIF maps handling."];
+maps(suite) -> [];
+maps(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ Pairs = [{adam, "bert"}] ++
+ [{I,I}||I <- lists:seq(1,10)] ++
+ [{a,value},{"a","value"},{<<"a">>,<<"value">>}],
+ ok = ensure_lib_loaded(Config, 1),
+ M = maps_from_list_nif(Pairs),
+ R = {RIs,Is} = sorted_list_from_maps_nif(M),
+ io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]),
+ Is = lists:sort(Pairs),
+ Is = lists:reverse(RIs),
+
+ #{} = maps_from_list_nif([]),
+ {[],[]} = sorted_list_from_maps_nif(#{}),
+
+ 1 = is_map_nif(M),
+ 0 = is_map_nif("no map"),
+
+ Msz = map_size(M),
+ {1,Msz} = get_map_size_nif(M),
+ {1,0} = get_map_size_nif(#{}),
+ {0,-123} = get_map_size_nif({#{}}),
+
+ #{} = M0 = make_new_map_nif(),
+
+ {1, #{key := value}=M1} = make_map_put_nif(M0, key, value),
+ {1, #{key := value, "key2" := "value2"}=M2} = make_map_put_nif(M1, "key2", "value2"),
+ {1, #{key := "value", "key2" := "value2"}=M3} = make_map_put_nif(M2, key, "value"),
+ {0, undefined} = make_map_put_nif(666, key, value),
+
+ {1, "value2"} = get_map_value_nif(M3,"key2"),
+ {0, undefined} = get_map_value_nif(M3,"key3"),
+ {0, undefined} = get_map_value_nif(false,key),
+
+ {0, undefined} = make_map_update_nif(M0, key, value),
+ {0, undefined} = make_map_update_nif(M1, "key2", "value2"),
+ {1, #{key := "value", "key2" := "value2"}} = make_map_update_nif(M2, key, "value"),
+ {0, undefined} = make_map_update_nif(666, key, value),
+
+ {1, #{}} = make_map_remove_nif(M1, key),
+ {1, M1} = make_map_remove_nif(M2, "key2"),
+ {1, M2} = make_map_remove_nif(M2, "key3"),
+ {0, undefined} = make_map_remove_nif(self(), key),
+
+ ok.
+
api_macros(doc) -> ["Test macros enif_make_list<N> and enif_make_tuple<N>"];
api_macros(suite) -> [];
api_macros(Config) when is_list(Config) ->
@@ -1343,6 +1392,20 @@ consume_timeslice(Config) when is_list(Config) ->
ok.
+dirty_nif(Config) when is_list(Config) ->
+ try erlang:system_info(dirty_cpu_schedulers) of
+ N when is_integer(N) ->
+ ensure_lib_loaded(Config),
+ Val1 = 42,
+ Val2 = "Erlang",
+ Val3 = list_to_binary([Val2, 0]),
+ {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3),
+ ok
+ catch
+ error:badarg ->
+ {skipped,"No dirty scheduler support"}
+ end.
+
next_msg(Pid) ->
receive
M -> M
@@ -1472,6 +1535,19 @@ echo_int(_) -> ?nif_stub.
type_sizes() -> ?nif_stub.
otp_9668_nif(_) -> ?nif_stub.
consume_timeslice_nif(_,_) -> ?nif_stub.
+call_dirty_nif(_,_,_) -> ?nif_stub.
+
+%% maps
+is_map_nif(_) -> ?nif_stub.
+get_map_size_nif(_) -> ?nif_stub.
+make_new_map_nif() -> ?nif_stub.
+make_map_put_nif(_,_,_) -> ?nif_stub.
+get_map_value_nif(_,_) -> ?nif_stub.
+make_map_update_nif(_,_,_) -> ?nif_stub.
+make_map_remove_nif(_,_) -> ?nif_stub.
+maps_from_list_nif(_) -> ?nif_stub.
+sorted_list_from_maps_nif(_) -> ?nif_stub.
+
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 0c4a9f7e5c..b550d1f16d 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2014. 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
@@ -1494,6 +1494,174 @@ static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NI
}
}
+#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
+static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int n;
+ char s[10];
+ ErlNifBinary b;
+ ERL_NIF_TERM result;
+ if (enif_have_dirty_schedulers()) {
+ assert(enif_is_on_dirty_scheduler(env));
+ }
+ assert(argc == 3);
+ enif_get_int(env, argv[0], &n);
+ enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1);
+ enif_inspect_binary(env, argv[2], &b);
+ result = enif_make_tuple3(env,
+ enif_make_int(env, n),
+ enif_make_string(env, s, ERL_NIF_LATIN1),
+ enif_make_binary(env, &b));
+ return enif_schedule_dirty_nif_finalizer(env, result, enif_dirty_nif_finalizer);
+}
+
+static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int n;
+ char s[10];
+ ErlNifBinary b;
+ assert(!enif_is_on_dirty_scheduler(env));
+ if (argc != 3)
+ return enif_make_badarg(env);
+ if (enif_have_dirty_schedulers()) {
+ if (enif_get_int(env, argv[0], &n) &&
+ enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) &&
+ enif_inspect_binary(env, argv[2], &b))
+ return enif_schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_nif, argc, argv);
+ else
+ return enif_make_badarg(env);
+ } else {
+ return dirty_nif(env, argc, argv);
+ }
+}
+#endif
+
+static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_int(env, enif_is_map(env,argv[0]));
+}
+static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ size_t size = (size_t)-123;
+ int ret = enif_get_map_size(env, argv[0], &size);
+ return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size));
+}
+static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_new_map(env);
+}
+static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
+ int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out);
+ return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
+}
+static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM value = enif_make_atom(env, "undefined");
+ int ret = enif_get_map_value(env, argv[0], argv[1], &value);
+ return enif_make_tuple2(env, enif_make_int(env,ret), value);
+
+}
+static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
+ int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out);
+ return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
+}
+static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
+ int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out);
+ return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
+}
+
+/* maps */
+static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM cell = argv[0];
+ ERL_NIF_TERM map = enif_make_new_map(env);
+ ERL_NIF_TERM tuple;
+ const ERL_NIF_TERM *pair;
+ int arity = -1;
+
+ if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env);
+
+ /* assume sorted keys */
+
+ while (!enif_is_empty_list(env,cell)) {
+ if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env);
+ if (enif_get_tuple(env,tuple,&arity,&pair)) {
+ enif_make_map_put(env, map, pair[0], pair[1], &map);
+ }
+ }
+
+ return map;
+}
+
+static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
+
+ ERL_NIF_TERM map = argv[0];
+ ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
+ ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
+ ERL_NIF_TERM key, value, k2, v2;
+ ErlNifMapIterator iter_f;
+ ErlNifMapIterator iter_b;
+ int cnt, next_ret, prev_ret;
+
+ if (argc != 1 && !enif_is_map(env, map))
+ return enif_make_int(env, __LINE__);
+
+ if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD))
+ return enif_make_int(env, __LINE__);
+
+ cnt = 0;
+ while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) {
+ if (cnt && !next_ret)
+ return enif_make_int(env, __LINE__);
+ list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
+ next_ret = enif_map_iterator_next(env,&iter_f);
+ cnt++;
+ }
+ if (cnt && next_ret)
+ return enif_make_int(env, __LINE__);
+
+ if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL))
+ return enif_make_int(env, __LINE__);
+
+ cnt = 0;
+ while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) {
+ if (cnt && !prev_ret)
+ return enif_make_int(env, __LINE__);
+
+ /* Test that iter_f can step "backwards" */
+ if (!enif_map_iterator_prev(env,&iter_f)
+ || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2)
+ || k2 != key || v2 != value) {
+ return enif_make_int(env, __LINE__);
+ }
+
+ list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
+ prev_ret = enif_map_iterator_prev(env,&iter_b);
+ }
+
+ if (cnt) {
+ if (prev_ret || enif_map_iterator_prev(env,&iter_f))
+ return enif_make_int(env, __LINE__);
+
+ /* Test that iter_b can step "backwards" one step */
+ if (!enif_map_iterator_next(env, &iter_b)
+ || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2)
+ || k2 != key || v2 != value)
+ return enif_make_int(env, __LINE__);
+ }
+
+ enif_map_iterator_destroy(env, &iter_f);
+ enif_map_iterator_destroy(env, &iter_b);
+
+ return enif_make_tuple2(env, list_f, list_b);
+}
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -1543,7 +1711,19 @@ static ErlNifFunc nif_funcs[] =
{"echo_int", 1, echo_int},
{"type_sizes", 0, type_sizes},
{"otp_9668_nif", 1, otp_9668_nif},
- {"consume_timeslice_nif", 2, consume_timeslice_nif}
+ {"consume_timeslice_nif", 2, consume_timeslice_nif},
+#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
+ {"call_dirty_nif", 3, call_dirty_nif},
+#endif
+ {"is_map_nif", 1, is_map_nif},
+ {"get_map_size_nif", 1, get_map_size_nif},
+ {"make_new_map_nif", 0, make_new_map_nif},
+ {"make_map_put_nif", 3, make_map_put_nif},
+ {"get_map_value_nif", 2, get_map_value_nif},
+ {"make_map_update_nif", 3, make_map_update_nif},
+ {"make_map_remove_nif", 2, make_map_remove_nif},
+ {"maps_from_list_nif", 1, maps_from_list_nif},
+ {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index fcd4457c34..202a8b7537 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -949,6 +949,20 @@ cd(Config) when is_list(Config) ->
Other2 ->
test_server:fail({env, Other2})
end,
+ _ = open_port({spawn, Cmd},
+ [{cd, unicode:characters_to_binary(TestDir)},
+ {line, 256}]),
+ receive
+ {_, {data, {eol, String2}}} ->
+ case filename_equal(String2, TestDir) of
+ true ->
+ ok;
+ false ->
+ test_server:fail({cd, String2})
+ end;
+ Other3 ->
+ test_server:fail({env, Other3})
+ end,
test_server:timetrap_cancel(Dog),
ok.
@@ -1353,19 +1367,28 @@ spawn_executable(Config) when is_list(Config) ->
EchoArgs1 = filename:join([DataDir,"echo_args"]),
ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
[ExactFile1] = run_echo_args(DataDir,[]),
+ [ExactFile1] = run_echo_args(DataDir,[binary]),
["echo_args"] = run_echo_args(DataDir,["echo_args"]),
+ ["echo_args"] = run_echo_args(DataDir,[binary, "echo_args"]),
["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
+ ["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]),
[ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]),
[ExactFile1] = run_echo_args(DataDir,[default]),
+ [ExactFile1] = run_echo_args(DataDir,[binary, default]),
[ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[switch_order,ExactFile1,"hello world",
"dlrow olleh"]),
[ExactFile1,"hello world","dlrow olleh"] =
+ run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world",
+ "dlrow olleh"]),
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[default,"hello world","dlrow olleh"]),
[ExactFile1,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""),
+ [ExactFile1,"hello world","dlrow olleh"] =
+ run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")),
PrivDir = ?config(priv_dir, Config),
SpaceDir =filename:join([PrivDir,"With Spaces"]),
@@ -1380,6 +1403,8 @@ spawn_executable(Config) when is_list(Config) ->
["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]),
[ExactFile2,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]),
+ [ExactFile2,"hello world","dlrow olleh"] =
+ run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]),
[ExactFile2] = run_echo_args(SpaceDir,[default]),
[ExactFile2,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world",
@@ -1388,6 +1413,8 @@ spawn_executable(Config) when is_list(Config) ->
run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]),
[ExactFile2,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""),
+ [ExactFile2,"hello world","dlrow olleh"] =
+ run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")),
ExeExt =
case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
@@ -1415,9 +1442,12 @@ spawn_executable(Config) when is_list(Config) ->
[default,"hello world","dlrow olleh"]),
[ExactFile3,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""),
+ [ExactFile3,"hello world","dlrow olleh"] =
+ run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")),
{'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
[default,"hello world",
"dlrow olleh"])),
+
NonExec = "kronxfrt"++ExeExt,
file:write_file(filename:join([SpaceDir,NonExec]),
<<"Not an executable">>),
@@ -1518,25 +1548,40 @@ run_echo_args_2(FullnameAndArgs) ->
run_echo_args(Where,Args) ->
- run_echo_args(Where,"echo_args",Args).
+ run_echo_args(Where,"echo_args",Args).
run_echo_args(Where,Prog,Args) ->
- ArgvArg = case Args of
- [] ->
- [];
- [default|T] ->
- [{args,T}];
- [switch_order,H|T] ->
- [{args,T},{arg0,H}];
- [H|T] ->
- [{arg0,H},{args,T}]
+ {Binary, ArgvArg} = pack_argv(Args),
+ Command0 = filename:join([Where,Prog]),
+ Command = case Binary of
+ true -> unicode:characters_to_binary(Command0);
+ false -> Command0
end,
- Command = filename:join([Where,Prog]),
Port = open_port({spawn_executable,Command},ArgvArg++[eof]),
Data = collect_data(Port),
Port ! {self(), close},
receive {Port, closed} -> ok end,
parse_echo_args_output(Data).
-
+
+pack_argv([binary|Args]) ->
+ {true, pack_argv(Args, true)};
+pack_argv(Args) ->
+ {false, pack_argv(Args, false)}.
+
+pack_argv(Args, Binary) ->
+ case Args of
+ [] ->
+ [];
+ [default|T] ->
+ [{args,[make_bin(Arg,Binary) || Arg <- T]}];
+ [switch_order,H|T] ->
+ [{args,[make_bin(Arg,Binary) || Arg <- T]},{arg0,make_bin(H,Binary)}];
+ [H|T] ->
+ [{arg0,make_bin(H,Binary)},{args,[make_bin(Arg,Binary) || Arg <- T]}]
+ end.
+
+make_bin(Str, false) -> Str;
+make_bin(Str, true) -> unicode:characters_to_binary(Str).
+
collect_data(Port) ->
receive
{Port, {data, Data}} ->
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index e3aae17df4..bf31655066 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -51,7 +51,13 @@
processes_term_proc_list/1,
otp_7738_waiting/1, otp_7738_suspended/1,
otp_7738_resume/1,
- garb_other_running/1]).
+ garb_other_running/1,
+ no_priority_inversion/1,
+ no_priority_inversion2/1,
+ system_task_blast/1,
+ system_task_on_suspended/1,
+ gc_request_when_gc_disabled/1,
+ gc_request_blast_when_gc_disabled/1]).
-export([prio_server/2, prio_client/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -73,7 +79,8 @@ all() ->
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
spawn_opt_heap_size, otp_6237, {group, processes_bif},
- {group, otp_7738}, garb_other_running].
+ {group, otp_7738}, garb_other_running,
+ {group, system_task}].
groups() ->
[{t_exit_2, [],
@@ -87,7 +94,11 @@ groups() ->
processes_gc_trap, processes_term_proc_list]},
{otp_7738, [],
[otp_7738_waiting, otp_7738_suspended,
- otp_7738_resume]}].
+ otp_7738_resume]},
+ {system_task, [],
+ [no_priority_inversion, no_priority_inversion2,
+ system_task_blast, system_task_on_suspended,
+ gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}].
init_per_suite(Config) ->
A0 = case application:start(sasl) of
@@ -2214,6 +2225,208 @@ garb_other_running(Config) when is_list(Config) ->
receive {'DOWN', Mon, process, Pid, normal} -> ok end,
ok.
+no_priority_inversion(Config) when is_list(Config) ->
+ Prio = process_flag(priority, max),
+ HTLs = lists:map(fun (_) ->
+ spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, high}, monitor, link])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ receive after 500 -> ok end,
+ LTL = spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, low}, monitor, link]),
+ false = erlang:check_process_code(element(1, LTL), nonexisting_module),
+ true = erlang:garbage_collect(element(1, LTL)),
+ lists:foreach(fun ({P, _}) ->
+ unlink(P),
+ exit(P, kill)
+ end, [LTL | HTLs]),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, killed} ->
+ ok
+ end
+ end, [LTL | HTLs]),
+ process_flag(priority, Prio),
+ ok.
+
+no_priority_inversion2(Config) when is_list(Config) ->
+ Prio = process_flag(priority, max),
+ MTLs = lists:map(fun (_) ->
+ spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, max}, monitor, link])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ receive after 500 -> ok end,
+ {PL, ML} = spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, low}, monitor, link]),
+ RL = request_gc(PL, low),
+ RN = request_gc(PL, normal),
+ RH = request_gc(PL, high),
+ receive
+ {garbage_collect, _, _} ->
+ ?t:fail(unexpected_gc)
+ after 1000 ->
+ ok
+ end,
+ RM = request_gc(PL, max),
+ receive
+ {garbage_collect, RM, true} ->
+ ok
+ end,
+ lists:foreach(fun ({P, _}) ->
+ unlink(P),
+ exit(P, kill)
+ end, MTLs),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, killed} ->
+ ok
+ end
+ end, MTLs),
+ receive
+ {garbage_collect, RH, true} ->
+ ok
+ end,
+ receive
+ {garbage_collect, RN, true} ->
+ ok
+ end,
+ receive
+ {garbage_collect, RL, true} ->
+ ok
+ end,
+ unlink(PL),
+ exit(PL, kill),
+ receive
+ {'DOWN', ML, process, PL, killed} ->
+ ok
+ end,
+ process_flag(priority, Prio),
+ ok.
+
+request_gc(Pid, Prio) ->
+ Ref = make_ref(),
+ erts_internal:request_system_task(Pid, Prio, {garbage_collect, Ref}),
+ Ref.
+
+system_task_blast(Config) when is_list(Config) ->
+ Me = self(),
+ GCReq = fun () ->
+ RL = gc_req(Me, 100),
+ lists:foreach(fun (R) ->
+ receive
+ {garbage_collect, R, true} ->
+ ok
+ end
+ end, RL),
+ exit(it_worked)
+ end,
+ HTLs = lists:map(fun (_) -> spawn_monitor(GCReq) end, lists:seq(1, 1000)),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, it_worked} ->
+ ok
+ end
+ end, HTLs),
+ ok.
+
+gc_req(_Pid, 0) ->
+ [];
+gc_req(Pid, N) ->
+ R0 = request_gc(Pid, low),
+ R1 = request_gc(Pid, normal),
+ R2 = request_gc(Pid, high),
+ R3 = request_gc(Pid, max),
+ [R0, R1, R2, R3 | gc_req(Pid, N-1)].
+
+system_task_on_suspended(Config) when is_list(Config) ->
+ {P, M} = spawn_monitor(fun () ->
+ tok_loop()
+ end),
+ true = erlang:suspend_process(P),
+ {status, suspended} = process_info(P, status),
+ true = erlang:garbage_collect(P),
+ {status, suspended} = process_info(P, status),
+ true = erlang:resume_process(P),
+ false = ({status, suspended} == process_info(P, status)),
+ exit(P, kill),
+ receive
+ {'DOWN', M, process, P, killed} ->
+ ok
+ end.
+
+gc_request_when_gc_disabled(Config) when is_list(Config) ->
+ Master = self(),
+ AIS = erts_debug:set_internal_state(available_internal_state, true),
+ {P, M} = spawn_opt(fun () ->
+ true = erts_debug:set_internal_state(gc_state,
+ false),
+ Master ! {self(), gc_state, false},
+ receive after 1000 -> ok end,
+ Master ! {self(), gc_state, true},
+ false = erts_debug:set_internal_state(gc_state,
+ true),
+ receive after 100 -> ok end
+ end, [monitor, link]),
+ receive {P, gc_state, false} -> ok end,
+ ReqId = make_ref(),
+ async = garbage_collect(P, [{async, ReqId}]),
+ receive
+ {garbage_collect, ReqId, Result} ->
+ ?t:fail({unexpected_gc, Result});
+ {P, gc_state, true} ->
+ ok
+ end,
+ receive {garbage_collect, ReqId, true} -> ok end,
+ erts_debug:set_internal_state(available_internal_state, AIS),
+ receive {'DOWN', M, process, P, _Reason} -> ok end,
+ ok.
+
+gc_request_blast_when_gc_disabled(Config) when is_list(Config) ->
+ Master = self(),
+ AIS = erts_debug:set_internal_state(available_internal_state, true),
+ {P, M} = spawn_opt(fun () ->
+ true = erts_debug:set_internal_state(gc_state,
+ false),
+ Master ! {self(), gc_state, false},
+ receive after 1000 -> ok end,
+ false = erts_debug:set_internal_state(gc_state,
+ true),
+ receive after 100 -> ok end
+ end, [monitor, link]),
+ receive {P, gc_state, false} -> ok end,
+ PMs = lists:map(fun (N) ->
+ Prio = case N rem 4 of
+ 0 -> max;
+ 1 -> high;
+ 2 -> normal;
+ 3 -> low
+ end,
+ spawn_opt(fun () ->
+ erlang:garbage_collect(P)
+ end, [monitor, link, {priority, Prio}])
+ end, lists:seq(1, 10000)),
+ lists:foreach(fun ({Proc, Mon}) ->
+ receive
+ {'DOWN', Mon, process, Proc, normal} ->
+ ok
+ end
+ end,
+ PMs),
+ erts_debug:set_internal_state(available_internal_state, AIS),
+ receive {'DOWN', M, process, P, _Reason} -> ok end,
+ ok.
+
+
%% Internal functions
wait_until(Fun) ->
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 81539faa09..6a43e2b0e7 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -1495,7 +1495,7 @@ mcall(Node, Funs) ->
end, Refs).
erl_rel_flag_var() ->
- "ERL_"++erlang:system_info(otp_release)++"_FLAGS".
+ "ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS".
clear_erl_rel_flags() ->
EnvVar = erl_rel_flag_var(),
diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl
index b631f55a03..8e1f8df43a 100644
--- a/erts/emulator/test/send_term_SUITE.erl
+++ b/erts/emulator/test/send_term_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
@@ -62,7 +62,19 @@ basic(Config) when is_list(Config) ->
?line [] = term(P, 0),
?line Self = self(),
- ?line {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self} = term(P, 1),
+ {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1),
+
+ Map41 = maps:from_list([{blurf, 42},
+ {[], [-42,{}|"abc"++P]},
+ {"kalle", 3.1416},
+ {Self, #{}}]),
+ Map41 = term(P, 41),
+
+ Map42 = maps:from_list([{42, []},
+ {[-42,{}|"abc"++P], "kalle"},
+ {3.1416, Self},
+ {#{}, blurf}]),
+ Map42 = term(P, 42),
?line Deep = lists:seq(0, 199),
?line Deep = term(P, 2),
?line {B1,B2} = term(P, 3),
@@ -125,7 +137,8 @@ basic(Config) when is_list(Config) ->
{-1, 36}, % ERL_DRV_INT64
{-4711, 37}, % ERL_DRV_INT64
{-20233590931456, 38}, % ERL_DRV_INT64
- {-9223372036854775808, 39}], % ERL_DRV_INT64
+ {-9223372036854775808, 39},
+ {#{}, 40}], % ERL_DRV_MAP
?line {Terms, Ops} = lists:unzip(Singles),
?line Terms = term(P,Ops),
diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
index f8613487b0..381a4f20d5 100644
--- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
+++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c
@@ -104,7 +104,7 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
double f = 3.1416;
msg[0] = ERL_DRV_ATOM;
- msg[1] = driver_mk_atom("blurf"),
+ msg[1] = driver_mk_atom("blurf");
msg[2] = ERL_DRV_INT;
msg[3] = (ErlDrvTermData) 42;
msg[4] = ERL_DRV_NIL;
@@ -126,9 +126,11 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
msg[20] = (ErlDrvTermData) &f;
msg[21] = ERL_DRV_PID;
msg[22] = driver_connected(erlang_port);
- msg[23] = ERL_DRV_TUPLE;
- msg[24] = (ErlDrvTermData) 7;
- msg += 25;
+ msg[23] = ERL_DRV_MAP;
+ msg[24] = (ErlDrvTermData) 0;
+ msg[25] = ERL_DRV_TUPLE;
+ msg[26] = (ErlDrvTermData) 8;
+ msg += 27;
}
break;
@@ -481,6 +483,52 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
break;
}
+ case 40: {
+ msg[0] = ERL_DRV_MAP;
+ msg[1] = (ErlDrvTermData) 0;
+ msg += 2;
+ break;
+ }
+
+ case 41: /* Most term types inside a map */
+ case 42: {
+ double f = 3.1416;
+
+ if (buf[i] == 41) {
+ *msg++ = ERL_DRV_ATOM;
+ *msg++ = driver_mk_atom("blurf");
+ }
+ *msg++ = ERL_DRV_INT;
+ *msg++ = (ErlDrvTermData)42;
+ *msg++ = ERL_DRV_NIL;
+ *msg++ = ERL_DRV_INT;
+ *msg++ = (ErlDrvTermData)-42;
+ *msg++ = ERL_DRV_TUPLE;
+ *msg++ = (ErlDrvTermData)0;
+ *msg++ = ERL_DRV_PORT;
+ *msg++ = driver_mk_port(erlang_port);
+ *msg++ = ERL_DRV_STRING_CONS;
+ *msg++ = (ErlDrvTermData)"abc";
+ *msg++ = (ErlDrvTermData)3;
+ *msg++ = ERL_DRV_LIST;
+ *msg++ = (ErlDrvTermData)3;
+ *msg++ = ERL_DRV_STRING;
+ *msg++ = (ErlDrvTermData)"kalle";
+ *msg++ = (ErlDrvTermData)5;
+ *msg++ = ERL_DRV_FLOAT;
+ *msg++ = (ErlDrvTermData)&f;
+ *msg++ = ERL_DRV_PID;
+ *msg++ = driver_connected(erlang_port);
+ *msg++ = ERL_DRV_MAP;
+ *msg++ = (ErlDrvTermData)0;
+ if (buf[i] == 42) {
+ *msg++ = ERL_DRV_ATOM;
+ *msg++ = driver_mk_atom("blurf");
+ }
+ *msg++ = ERL_DRV_MAP;
+ *msg++ = (ErlDrvTermData)4;
+ break;
+ }
case 127: /* Error cases */
{
@@ -662,6 +710,22 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count)
FAIL_TERM(msg, 2);
}
+ msg[0] = ERL_DRV_MAP;
+ msg[1] = (ErlDrvTermData) 0;
+ FAIL_TERM(msg, 1);
+
+ /* map with duplicate key */
+ msg[0] = ERL_DRV_ATOM;
+ msg[1] = driver_mk_atom("key");
+ msg[2] = ERL_DRV_NIL;
+ msg[3] = ERL_DRV_ATOM;
+ msg[4] = driver_mk_atom("key");
+ msg[5] = ERL_DRV_INT;
+ msg[6] = (ErlDrvTermData) -4711;
+ msg[7] = ERL_DRV_MAP;
+ msg[8] = 2;
+ FAIL_TERM(msg, 9);
+
/* Signal end of test case */
msg[0] = ERL_DRV_NIL;
erl_drv_output_term(driver_mk_port(erlang_port), msg, 1);