[erlang-bugs] Maps equality failure / Maps merge failure
Björn-Egil Dahlberg
egil@REDACTED
Tue Mar 24 15:28:05 CET 2015
On 2015-03-24 15:16, Jesper Louis Andersen wrote:
> Hi OTP team (and other readers),
Hi Jesper!
Awesome! Thank you for reporting this and thank you for your quickcheck
model. =D
term_to_binary binary_to_term problem has already been spotted and a fix
is on it's way.
We will look into the others now.
// Björn-Egil
>
> Over the weekend, I've been building up a QuickCheck model of
> Erlang/OTP maps(). This is to be able to properly test the HAMT maps
> of the up-and-coming 18.0 release for correctness, but I'm testing on
> 17.4.1 as well. The following problem occurs for release 17.4.1. (for
> the curious, 18.0 currently segfaults on these tests):
>
> Erlang/OTP 17 [erts-6.3.1] [source] [64-bit] [smp:8:8] [ds:8:8:10]
> [async-threads:10] [kernel-poll:false]
>
> Eshell V6.3.1 (abort with ^G)
> 1> Map = #{0 => 0,2147483648 => 0}.
> #{0 => 0,2147483648 => 0}
> 2> Bin = term_to_binary(M).
> * 1: variable 'M' is unbound
> 3> Bin = term_to_binary(Map).
> <<131,116,0,0,0,2,97,0,97,0,110,4,0,0,0,0,128,97,0>>
> 4> Map2 = binary_to_term(Bin).
> #{2147483648 => 0,0 => 0}
> 5> Map =:= Map2.
> false
>
> (copied verbatim, including my error in 2>)
>
> The problem is that if I convert the map to a binary and then back
> again, the two maps are not equal. Furthermore, they print differently:
>
> 6> Map.
> #{0 => 0,2147483648 => 0}
> 7> Map2.
> #{2147483648 => 0,0 => 0}
> 8>
>
> The value 2147483648 seems important and it is 2^31. However, the
> problem also occurs at 2^31 + 1. Only testing with "small" integers
> poses no problem at all. I have yet to test against floating point
> numbers and this requires some additional attention to detail as they
> have fun equality rules :)
>
> A second problem occurs in merges, which may be related or may not be
> related. In the second problem, we are merging maps
>
> [#{-2147483649 => 0,
> 0 => 0,
> 97 => 0,
> false => 0,
> flower => 0,
> #Fun<eqc_gen.133.121384563> => 0,
> #Fun<eqc_gen.133.121384563> => 0,
> <<>> => 0},
> #{0 => 1}]
>
> where the #Fun's are generated functions of 0 and 2 parameters
> respectively. Lets see how to create such a map (one might note that
> direct creation through #{ ... } syntax would make the keys which are
> functions illegal, but I am an experiened Erlang programmer and have a
> way around such petty limitations :P)
>
> 9> F1 = fun(_, _) -> 0 end,
> 9> F2 = fun(_, _) -> 1 end.
>
> This defines two functions:
>
> 11> maps:from_list(
> 11> [{-2147483649, 0},
> 11> {0,0}, {97, 0}, {false, 0}, {flower, 0}, {F1, 0}, {F2, 0},
> {<<>>, 0}]).
> #{-2147483649 => 0,
> 0 => 0,
> 97 => 0,
> false => 0,
> flower => 0,
> #Fun<erl_eval.12.90072148> => 0,
> #Fun<erl_eval.12.90072148> => 0,
> <<>> => 0}
>
> Remarkably, this is well-defined, now lets merge:
>
> 12> maps:merge(v(11), #{0 => 1}).
> #{0 => 1,
> -2147483649 => 0,
> 0 => 0,
> 97 => 0,
> false => 0,
> flower => 0,
> #Fun<erl_eval.12.90072148> => 0,
> #Fun<erl_eval.12.90072148> => 0,
> <<>> => 0}
> 13>
>
> The resulting map now has to '0' keys! Rejoice!
>
> The model I am using is the following:
>
> https://github.com/jlouis/maps_eqc/tree/ac7748ce81781ae6c1ad7b4ed07ca5cd935c125d
>
> (maps_iso_eqc are simple stateless tests, whereas maps_eqc is a more
> complicated stateful test which also verifies properties about copying
> maps between processes and that maps are persistent data structures).
>
> To run:
>
> make:all([load]).
> eqc:module({testing_budget, 20}, maps_iso_eqc).
>
> Two test runs of mine:
>
> https://gist.github.com/jlouis/39de114e0a447af983a5
>
> Note the latter merge test which managed to shrink over 1200 times!
>
>
> --
> J.
>
>
> _______________________________________________
> erlang-bugs mailing list
> erlang-bugs@REDACTED
> http://erlang.org/mailman/listinfo/erlang-bugs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-bugs/attachments/20150324/36f2564d/attachment.htm>
More information about the erlang-bugs
mailing list