From 126ef4f4830624eec10518875a851223d4f7054f Mon Sep 17 00:00:00 2001 From: Maria Scott Date: Wed, 15 Feb 2023 17:15:17 +0100 Subject: Improve the lists:merge family of functions The merge functions no longer accept non-list arguments for merging. Before, non-list arguments were accepted and returned if they were the first argument and the second (and third, where applicable, ie in merge3/3 and umerge3/3) arguments were empty lists. For merge/1 and umerge/1, non-list arguments were accepted if they were the only element in a list of otherwise empty lists. The merge functions now also return the second (or third, where applicable) list immediately and unchanged if the other lists are empty. Before, this was only done for the first argument, if the second (and third, where applicable) list was empty. In all other cases, the merging process would be performed, unnecessarily. The same applies for the undocumented reverse-merge functions like rmerge/2,3, rumerge/2,3 etc. Co-authored-by: Jan Uhlig --- lib/stdlib/test/lists_SUITE.erl | 242 ++++++++++++++++++++++++++ lib/stdlib/test/lists_property_test_SUITE.erl | 50 ++++-- lib/stdlib/test/property_test/lists_prop.erl | 131 ++++++++++++++ 3 files changed, 413 insertions(+), 10 deletions(-) (limited to 'lib/stdlib/test') diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl index b5cbcd98c7..792a94702a 100644 --- a/lib/stdlib/test/lists_SUITE.erl +++ b/lib/stdlib/test/lists_SUITE.erl @@ -441,6 +441,8 @@ keyreplace(Config) when is_list(Config) -> merge(Config) when is_list(Config) -> + Singleton = id([a, b, c]), + %% merge list of lists [] = lists:merge([]), [] = lists:merge([[]]), @@ -463,6 +465,33 @@ merge(Config) when is_list(Config) -> Seq = lists:seq(1,100), true = Seq == lists:merge(lists:map(fun(E) -> [E] end, Seq)), + true = erts_debug:same(Singleton, lists:merge([Singleton])), + true = erts_debug:same(Singleton, lists:merge([Singleton, []])), + true = erts_debug:same(Singleton, lists:merge([[], Singleton])), + true = erts_debug:same(Singleton, lists:merge([Singleton, [], []])), + true = erts_debug:same(Singleton, lists:merge([[], Singleton, []])), + true = erts_debug:same(Singleton, lists:merge([[], [], Singleton])), + + {'EXIT', _} = (catch lists:merge([a])), + {'EXIT', _} = (catch lists:merge([a, b])), + {'EXIT', _} = (catch lists:merge([a, []])), + {'EXIT', _} = (catch lists:merge([[], b])), + {'EXIT', _} = (catch lists:merge([a, [1, 2, 3]])), + {'EXIT', _} = (catch lists:merge([[1, 2, 3], b])), + {'EXIT', _} = (catch lists:merge([a, b, c])), + {'EXIT', _} = (catch lists:merge([a, b, []])), + {'EXIT', _} = (catch lists:merge([a, [], c])), + {'EXIT', _} = (catch lists:merge([a, [], []])), + {'EXIT', _} = (catch lists:merge([[], b, c])), + {'EXIT', _} = (catch lists:merge([[], b, []])), + {'EXIT', _} = (catch lists:merge([[], [], c])), + {'EXIT', _} = (catch lists:merge([a, b, [1, 2, 3]])), + {'EXIT', _} = (catch lists:merge([a, [1, 2, 3], c])), + {'EXIT', _} = (catch lists:merge([a, [1, 2, 3], [4, 5, 6]])), + {'EXIT', _} = (catch lists:merge([[1, 2, 3], b, c])), + {'EXIT', _} = (catch lists:merge([[1, 2, 3], b, [4, 5, 6]])), + {'EXIT', _} = (catch lists:merge([[1, 2, 3], [4, 5, 6], c])), + Two = [1,2], Six = [1,2,3,4,5,6], @@ -482,6 +511,15 @@ merge(Config) when is_list(Config) -> [1,2,3,4,5,7] = lists:merge([2,4], [1,3,5,7]), [1,2,3,4,5,6,7] = lists:merge([2,4,6], [1,3,5,7]), + true = erts_debug:same(Singleton, lists:merge([], Singleton)), + true = erts_debug:same(Singleton, lists:merge(Singleton, [])), + + {'EXIT', _} = (catch lists:merge(a, b)), + {'EXIT', _} = (catch lists:merge(a, [])), + {'EXIT', _} = (catch lists:merge([], b)), + {'EXIT', _} = (catch lists:merge(a, [1, 2, 3])), + {'EXIT', _} = (catch lists:merge([1, 2, 3], b)), + %% 3-way merge [] = lists:merge3([], [], []), Two = lists:merge3([], [], Two), @@ -498,11 +536,28 @@ merge(Config) when is_list(Config) -> Nine = lists:merge3([7,8,9],[4,5,6],[1,2,3]), Nine = lists:merge3([4,5,6],[7,8,9],[1,2,3]), + true = erts_debug:same(Singleton, lists:merge3([], [], Singleton)), + true = erts_debug:same(Singleton, lists:merge3([], Singleton, [])), + true = erts_debug:same(Singleton, lists:merge3(Singleton, [], [])), + + {'EXIT', _} = (catch lists:merge3(a, b, c)), + {'EXIT', _} = (catch lists:merge3(a, b, [])), + {'EXIT', _} = (catch lists:merge3(a, [], c)), + {'EXIT', _} = (catch lists:merge3(a, [], [])), + {'EXIT', _} = (catch lists:merge3([], b, [])), + {'EXIT', _} = (catch lists:merge3([], [], c)), + {'EXIT', _} = (catch lists:merge3(a, b, [1, 2, 3])), + {'EXIT', _} = (catch lists:merge3(a, [1, 2, 3], c)), + {'EXIT', _} = (catch lists:merge3(a, [1, 2, 3], [4, 5, 6])), + {'EXIT', _} = (catch lists:merge3([1, 2, 3], b, [4, 5, 6])), + {'EXIT', _} = (catch lists:merge3([1, 2, 3], [4, 5, 6], c)), + ok. %% reverse merge functions rmerge(Config) when is_list(Config) -> + Singleton = id([a, b, c]), Two = [2,1], Six = [6,5,4,3,2,1], @@ -522,6 +577,15 @@ rmerge(Config) when is_list(Config) -> [7,5,4,3,2,1] = lists:rmerge([4,2], [7,5,3,1]), [7,6,5,4,3,2,1] = lists:rmerge([6,4,2], [7,5,3,1]), + true = erts_debug:same(Singleton, lists:rmerge([], Singleton)), + true = erts_debug:same(Singleton, lists:rmerge(Singleton, [])), + + {'EXIT', _} = (catch lists:rmerge(a, b)), + {'EXIT', _} = (catch lists:rmerge(a, [])), + {'EXIT', _} = (catch lists:rmerge([], b)), + {'EXIT', _} = (catch lists:rmerge(a, [1, 2, 3])), + {'EXIT', _} = (catch lists:rmerge([1, 2, 3], b)), + Nine = [9,8,7,6,5,4,3,2,1], %% 3-way reversed merge @@ -540,6 +604,22 @@ rmerge(Config) when is_list(Config) -> Nine = lists:rmerge3([9,8,7],[6,5,4],[3,2,1]), Nine = lists:rmerge3([6,5,4],[9,8,7],[3,2,1]), + true = erts_debug:same(Singleton, lists:rmerge3([], [], Singleton)), + true = erts_debug:same(Singleton, lists:rmerge3([], Singleton, [])), + true = erts_debug:same(Singleton, lists:rmerge3(Singleton, [], [])), + + {'EXIT', _} = (catch lists:rmerge3(a, b, c)), + {'EXIT', _} = (catch lists:rmerge3(a, b, [])), + {'EXIT', _} = (catch lists:rmerge3(a, [], c)), + {'EXIT', _} = (catch lists:rmerge3(a, [], [])), + {'EXIT', _} = (catch lists:rmerge3([], b, [])), + {'EXIT', _} = (catch lists:rmerge3([], [], c)), + {'EXIT', _} = (catch lists:rmerge3(a, b, [1, 2, 3])), + {'EXIT', _} = (catch lists:rmerge3(a, [1, 2, 3], c)), + {'EXIT', _} = (catch lists:rmerge3(a, [1, 2, 3], [4, 5, 6])), + {'EXIT', _} = (catch lists:rmerge3([1, 2, 3], b, [4, 5, 6])), + {'EXIT', _} = (catch lists:rmerge3([1, 2, 3], [4, 5, 6], c)), + ok. sort_1(Config) when is_list(Config) -> @@ -640,6 +720,8 @@ usort_1(Conf) when is_list(Conf) -> ok. umerge(Conf) when is_list(Conf) -> + Singleton = id([a, b, c]), + %% merge list of lists [] = lists:umerge([]), [] = lists:umerge([[]]), @@ -663,6 +745,33 @@ umerge(Conf) when is_list(Conf) -> Seq = lists:seq(1,100), true = Seq == lists:umerge(lists:map(fun(E) -> [E] end, Seq)), + true = erts_debug:same(Singleton, lists:umerge([Singleton])), + true = erts_debug:same(Singleton, lists:umerge([Singleton, []])), + true = erts_debug:same(Singleton, lists:umerge([[], Singleton])), + true = erts_debug:same(Singleton, lists:umerge([Singleton, [], []])), + true = erts_debug:same(Singleton, lists:umerge([[], Singleton, []])), + true = erts_debug:same(Singleton, lists:umerge([[], [], Singleton])), + + {'EXIT', _} = (catch lists:umerge([a])), + {'EXIT', _} = (catch lists:umerge([a, b])), + {'EXIT', _} = (catch lists:umerge([a, []])), + {'EXIT', _} = (catch lists:umerge([[], b])), + {'EXIT', _} = (catch lists:umerge([a, [1, 2, 3]])), + {'EXIT', _} = (catch lists:umerge([[1, 2, 3], b])), + {'EXIT', _} = (catch lists:umerge([a, b, c])), + {'EXIT', _} = (catch lists:umerge([a, b, []])), + {'EXIT', _} = (catch lists:umerge([a, [], c])), + {'EXIT', _} = (catch lists:umerge([a, [], []])), + {'EXIT', _} = (catch lists:umerge([[], b, c])), + {'EXIT', _} = (catch lists:umerge([[], b, []])), + {'EXIT', _} = (catch lists:umerge([[], [], c])), + {'EXIT', _} = (catch lists:umerge([a, b, [1, 2, 3]])), + {'EXIT', _} = (catch lists:umerge([a, [1, 2, 3], c])), + {'EXIT', _} = (catch lists:umerge([a, [1, 2, 3], [4, 5, 6]])), + {'EXIT', _} = (catch lists:umerge([[1, 2, 3], b, c])), + {'EXIT', _} = (catch lists:umerge([[1, 2, 3], b, [4, 5, 6]])), + {'EXIT', _} = (catch lists:umerge([[1, 2, 3], [4, 5, 6], c])), + Two = [1,2], Six = [1,2,3,4,5,6], @@ -689,6 +798,15 @@ umerge(Conf) when is_list(Conf) -> [1,2,3,4,5,7] = lists:umerge([2,4], [1,2,3,4,5,7]), [1,2,3,4,5,6,7] = lists:umerge([2,4,6], [1,2,3,4,5,6,7]), + true = erts_debug:same(Singleton, lists:umerge([], Singleton)), + true = erts_debug:same(Singleton, lists:umerge(Singleton, [])), + + {'EXIT', _} = (catch lists:umerge(a, b)), + {'EXIT', _} = (catch lists:umerge(a, [])), + {'EXIT', _} = (catch lists:umerge([], b)), + {'EXIT', _} = (catch lists:umerge(a, [1, 2, 3])), + {'EXIT', _} = (catch lists:umerge([1, 2, 3], b)), + %% 3-way unique merge [] = lists:umerge3([], [], []), Two = lists:umerge3([], [], Two), @@ -710,9 +828,27 @@ umerge(Conf) when is_list(Conf) -> [1,2,3] = lists:umerge3([1,2,3],[2,3],[1,2,3]), [1,2,3,4] = lists:umerge3([2,3,4],[3,4],[1,2,3]), + true = erts_debug:same(Singleton, lists:umerge3([], [], Singleton)), + true = erts_debug:same(Singleton, lists:umerge3([], Singleton, [])), + true = erts_debug:same(Singleton, lists:umerge3(Singleton, [], [])), + + {'EXIT', _} = (catch lists:umerge3(a, b, c)), + {'EXIT', _} = (catch lists:umerge3(a, b, [])), + {'EXIT', _} = (catch lists:umerge3(a, [], c)), + {'EXIT', _} = (catch lists:umerge3(a, [], [])), + {'EXIT', _} = (catch lists:umerge3([], b, [])), + {'EXIT', _} = (catch lists:umerge3([], [], c)), + {'EXIT', _} = (catch lists:umerge3(a, b, [1, 2, 3])), + {'EXIT', _} = (catch lists:umerge3(a, [1, 2, 3], c)), + {'EXIT', _} = (catch lists:umerge3(a, [1, 2, 3], [4, 5, 6])), + {'EXIT', _} = (catch lists:umerge3([1, 2, 3], b, [4, 5, 6])), + {'EXIT', _} = (catch lists:umerge3([1, 2, 3], [4, 5, 6], c)), + ok. rumerge(Conf) when is_list(Conf) -> + Singleton = id([a, b, c]), + Two = [2,1], Six = [6,5,4,3,2,1], @@ -739,6 +875,15 @@ rumerge(Conf) when is_list(Conf) -> [7,5,4,3,2,1] = lists:rumerge([4,2], [7,5,4,3,2,1]), [7,6,5,4,3,2,1] = lists:rumerge([6,4,2], [7,6,5,4,3,2,1]), + true = erts_debug:same(Singleton, lists:rumerge([], Singleton)), + true = erts_debug:same(Singleton, lists:rumerge(Singleton, [])), + + {'EXIT', _} = (catch lists:rumerge(a, b)), + {'EXIT', _} = (catch lists:rumerge(a, [])), + {'EXIT', _} = (catch lists:rumerge([], b)), + {'EXIT', _} = (catch lists:rumerge(a, [1, 2, 3])), + {'EXIT', _} = (catch lists:rumerge([1, 2, 3], b)), + Nine = [9,8,7,6,5,4,3,2,1], %% 3-way reversed unique merge @@ -767,6 +912,23 @@ rumerge(Conf) when is_list(Conf) -> true = lists:umerge(L1, L2) == lists:reverse(lists:rumerge(lists:reverse(L1), lists:reverse(L2))), + + true = erts_debug:same(Singleton, lists:rumerge3([], [], Singleton)), + true = erts_debug:same(Singleton, lists:rumerge3([], Singleton, [])), + true = erts_debug:same(Singleton, lists:rumerge3(Singleton, [], [])), + + {'EXIT', _} = (catch lists:rumerge3(a, b, c)), + {'EXIT', _} = (catch lists:rumerge3(a, b, [])), + {'EXIT', _} = (catch lists:rumerge3(a, [], c)), + {'EXIT', _} = (catch lists:rumerge3(a, [], [])), + {'EXIT', _} = (catch lists:rumerge3([], b, [])), + {'EXIT', _} = (catch lists:rumerge3([], [], c)), + {'EXIT', _} = (catch lists:rumerge3(a, b, [1, 2, 3])), + {'EXIT', _} = (catch lists:rumerge3(a, [1, 2, 3], c)), + {'EXIT', _} = (catch lists:rumerge3(a, [1, 2, 3], [4, 5, 6])), + {'EXIT', _} = (catch lists:rumerge3([1, 2, 3], b, [4, 5, 6])), + {'EXIT', _} = (catch lists:rumerge3([1, 2, 3], [4, 5, 6], c)), + ok. %% usort/1 on big randomized lists. @@ -823,6 +985,7 @@ ucheck_stability(L) -> %% Key merge two lists. keymerge(Config) when is_list(Config) -> + Singleton = id([{1, a}, {2, b}, {3, c}]), Two = [{1,a},{2,b}], Six = [{1,a},{2,b},{3,c},{4,d},{5,e},{6,f}], @@ -851,11 +1014,21 @@ keymerge(Config) when is_list(Config) -> [{b,2},{c,11},{c,12},{c,21},{c,22},{e,5}] = lists:keymerge(1,[{c,11},{c,12},{e,5}], [{b,2},{c,21},{c,22}]), + true = erts_debug:same(Singleton, lists:keymerge(1, Singleton, [])), + true = erts_debug:same(Singleton, lists:keymerge(1, [], Singleton)), + + {'EXIT', _} = (catch lists:keymerge(1, a, b)), + {'EXIT', _} = (catch lists:keymerge(1, a, [])), + {'EXIT', _} = (catch lists:keymerge(1, [], b)), + {'EXIT', _} = (catch lists:keymerge(1, a, [{1, a}, {2, b}, {3, c}])), + {'EXIT', _} = (catch lists:keymerge(1, [{1, a}, {2, b}, {3, c}], b)), + ok. %% Reverse key merge two lists. rkeymerge(Config) when is_list(Config) -> + Singleton = id([{1, a}, {2, b}, {3, c}]), Two = [{2,b},{1,a}], Six = [{6,f},{5,e},{4,d},{3,c},{2,b},{1,a}], @@ -888,6 +1061,15 @@ rkeymerge(Config) when is_list(Config) -> lists:reverse(lists:rkeymerge(1,lists:reverse(L1), lists:reverse(L2))), + true = erts_debug:same(Singleton, lists:rkeymerge(1, Singleton, [])), + true = erts_debug:same(Singleton, lists:rkeymerge(1, [], Singleton)), + + {'EXIT', _} = (catch lists:rkeymerge(1, a, b)), + {'EXIT', _} = (catch lists:rkeymerge(1, a, [])), + {'EXIT', _} = (catch lists:rkeymerge(1, [], b)), + {'EXIT', _} = (catch lists:rkeymerge(1, a, [{1, a}, {2, b}, {3, c}])), + {'EXIT', _} = (catch lists:rkeymerge(1, [{1, a}, {2, b}, {3, c}], b)), + ok. keysort_1(Config) when is_list(Config) -> @@ -1006,6 +1188,7 @@ keycompare(I, J, A, B) when element(I, A) == element(I, B), %% Merge two lists while removing duplicates. ukeymerge(Conf) when is_list(Conf) -> + Singleton = id([{1, a}, {2, b}, {3, c}]), Two = [{1,a},{2,b}], Six = [{1,a},{2,b},{3,c},{4,d},{5,e},{6,f}], @@ -1055,11 +1238,21 @@ ukeymerge(Conf) when is_list(Conf) -> L2 = [{b,1},{b,3},{b,5},{b,7}], L1 = lists:ukeymerge(2, L1, L2), + true = erts_debug:same(Singleton, lists:ukeymerge(1, Singleton, [])), + true = erts_debug:same(Singleton, lists:ukeymerge(1, [], Singleton)), + + {'EXIT', _} = (catch lists:ukeymerge(1, a, b)), + {'EXIT', _} = (catch lists:ukeymerge(1, a, [])), + {'EXIT', _} = (catch lists:ukeymerge(1, [], b)), + {'EXIT', _} = (catch lists:ukeymerge(1, a, [{1, a}, {2, b}, {3, c}])), + {'EXIT', _} = (catch lists:ukeymerge(1, [{1, a}, {2, b}, {3, c}], b)), + ok. %% Reverse merge two lists while removing duplicates. rukeymerge(Conf) when is_list(Conf) -> + Singleton = id([{1, a}, {2, b}, {3, c}]), Two = [{2,b},{1,a}], Six = [{6,f},{5,e},{4,d},{3,c},{2,b},{1,a}], @@ -1109,6 +1302,15 @@ rukeymerge(Conf) when is_list(Conf) -> lists:reverse(lists:rukeymerge(2, lists:reverse(L1), lists:reverse(L2))), + true = erts_debug:same(Singleton, lists:rukeymerge(1, Singleton, [])), + true = erts_debug:same(Singleton, lists:rukeymerge(1, [], Singleton)), + + {'EXIT', _} = (catch lists:rukeymerge(1, a, b)), + {'EXIT', _} = (catch lists:rukeymerge(1, a, [])), + {'EXIT', _} = (catch lists:rukeymerge(1, [], b)), + {'EXIT', _} = (catch lists:rukeymerge(1, a, [{1, a}, {2, b}, {3, c}])), + {'EXIT', _} = (catch lists:rukeymerge(1, [{1, a}, {2, b}, {3, c}], b)), + ok. ukeysort_1(Config) when is_list(Config) -> @@ -1286,6 +1488,7 @@ ukeycompare(I, J, A, B) when A =/= B, %% Merge two lists using a fun. funmerge(Config) when is_list(Config) -> + Singleton = id([a, b, c]), Two = [1,2], Six = [1,2,3,4,5,6], F = fun(X, Y) -> X =< Y end, @@ -1310,11 +1513,21 @@ funmerge(Config) when is_list(Config) -> [{b,2},{c,11},{c,12},{c,21},{c,22},{e,5}] = lists:merge(F2,[{c,11},{c,12},{e,5}], [{b,2},{c,21},{c,22}]), + true = erts_debug:same(Singleton, lists:merge(F, Singleton, [])), + true = erts_debug:same(Singleton, lists:merge(F, [], Singleton)), + + {'EXIT', _} = (catch lists:merge(F, a, b)), + {'EXIT', _} = (catch lists:merge(F, a, [])), + {'EXIT', _} = (catch lists:merge(F, [], b)), + {'EXIT', _} = (catch lists:merge(F, a, [1, 2, 3])), + {'EXIT', _} = (catch lists:merge(F, [1, 2, 3], b)), + ok. %% Reverse merge two lists using a fun. rfunmerge(Config) when is_list(Config) -> + Singleton = id([a, b, c]), Two = [2,1], Six = [6,5,4,3,2,1], F = fun(X, Y) -> X =< Y end, @@ -1342,6 +1555,15 @@ rfunmerge(Config) when is_list(Config) -> lists:merge(F2, L1, L2) == lists:reverse(lists:rmerge(F2,lists:reverse(L1), lists:reverse(L2))), + true = erts_debug:same(Singleton, lists:rmerge(F, Singleton, [])), + true = erts_debug:same(Singleton, lists:rmerge(F, [], Singleton)), + + {'EXIT', _} = (catch lists:rmerge(F, a, b)), + {'EXIT', _} = (catch lists:rmerge(F, a, [])), + {'EXIT', _} = (catch lists:rmerge(F, [], b)), + {'EXIT', _} = (catch lists:rmerge(F, a, [1, 2, 3])), + {'EXIT', _} = (catch lists:rmerge(F, [1, 2, 3], b)), + ok. @@ -1411,6 +1633,7 @@ funsort_check(I, Input, Expected) -> %% Merge two lists while removing duplicates using a fun. ufunmerge(Conf) when is_list(Conf) -> + Singleton = id([a, b, c]), Two = [1,2], Six = [1,2,3,4,5,6], F = fun(X, Y) -> X =< Y end, @@ -1445,10 +1668,20 @@ ufunmerge(Conf) when is_list(Conf) -> [{b,2},{e,5},{c,11},{c,12},{c,21},{c,22}] = lists:umerge(F2, [{e,5},{c,11},{c,12}], [{b,2},{c,21},{c,22}]), + true = erts_debug:same(Singleton, lists:umerge(F, Singleton, [])), + true = erts_debug:same(Singleton, lists:umerge(F, [], Singleton)), + + {'EXIT', _} = (catch lists:umerge(F, a, b)), + {'EXIT', _} = (catch lists:umerge(F, a, [])), + {'EXIT', _} = (catch lists:umerge(F, [], b)), + {'EXIT', _} = (catch lists:umerge(F, a, [1, 2, 3])), + {'EXIT', _} = (catch lists:umerge(F, [1, 2, 3], b)), + ok. %% Reverse merge two lists while removing duplicates using a fun. rufunmerge(Conf) when is_list(Conf) -> + Singleton = id([a, b, c]), Two = [2,1], Six = [6,5,4,3,2,1], F = fun(X, Y) -> X =< Y end, @@ -1488,6 +1721,15 @@ rufunmerge(Conf) when is_list(Conf) -> lists:umerge(F2, L3, L4) == lists:reverse(lists:rumerge(F2,lists:reverse(L3), lists:reverse(L4))), + true = erts_debug:same(Singleton, lists:rumerge(F, Singleton, [])), + true = erts_debug:same(Singleton, lists:rumerge(F, [], Singleton)), + + {'EXIT', _} = (catch lists:rumerge(F, a, b)), + {'EXIT', _} = (catch lists:rumerge(F, a, [])), + {'EXIT', _} = (catch lists:rumerge(F, [], b)), + {'EXIT', _} = (catch lists:rumerge(F, a, [1, 2, 3])), + {'EXIT', _} = (catch lists:rumerge(F, [1, 2, 3], b)), + ok. ufunsort_1(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/lists_property_test_SUITE.erl b/lib/stdlib/test/lists_property_test_SUITE.erl index 617fed6af8..4b366d730a 100644 --- a/lib/stdlib/test/lists_property_test_SUITE.erl +++ b/lib/stdlib/test/lists_property_test_SUITE.erl @@ -49,7 +49,7 @@ all() -> keyfind_case, keyfind_absent_case, keymap_case, keymember_case, keymember_absent_case, - keymerge_case, + keymerge_case, keymerge_invalid_case, keyreplace_case, keyreplace_absent_case, keysearch_case, keysearch_absent_case, keysort_case, @@ -61,10 +61,10 @@ all() -> mapfoldr_case, max_case, member_case, member_absent_case, - merge_1_case, - merge_2_case, - merge_3_case, - merge3_case, + merge_1_case, merge_1_invalid_case, + merge_2_case, merge_2_invalid_case, + merge_3_case, merge_3_invalid_case, + merge3_case, merge3_invalid_case, min_case, nth_case, nth_outofrange_case, nthtail_case, nthtail_outofrange_case, @@ -85,12 +85,12 @@ all() -> suffix_case, sum_case, takewhile_case, - ukeymerge_case, + ukeymerge_case, ukeymerge_invalid_case, ukeysort_case, - umerge_1_case, - umerge_2_case, - umerge_3_case, - umerge3_case, + umerge_1_case, umerge_1_invalid_case, + umerge_2_case, umerge_2_invalid_case, + umerge_3_case, umerge_3_invalid_case, + umerge3_case, umerge3_invalid_case, uniq_1_case, uniq_2_case, unzip_case, @@ -213,6 +213,9 @@ keymember_absent_case(Config) -> keymerge_case(Config) -> do_proptest(prop_keymerge, Config). +keymerge_invalid_case(Config) -> + do_proptest(prop_keymerge_invalid, Config). + keyreplace_case(Config) -> do_proptest(prop_keyreplace, Config). @@ -264,15 +267,27 @@ member_absent_case(Config) -> merge_1_case(Config) -> do_proptest(prop_merge_1, Config). +merge_1_invalid_case(Config) -> + do_proptest(prop_merge_1_invalid, Config). + merge_2_case(Config) -> do_proptest(prop_merge_2, Config). +merge_2_invalid_case(Config) -> + do_proptest(prop_merge_2_invalid, Config). + merge_3_case(Config) -> do_proptest(prop_merge_3, Config). +merge_3_invalid_case(Config) -> + do_proptest(prop_merge_3_invalid, Config). + merge3_case(Config) -> do_proptest(prop_merge3, Config). +merge3_invalid_case(Config) -> + do_proptest(prop_merge3_invalid, Config). + min_case(Config) -> do_proptest(prop_min, Config). @@ -348,21 +363,36 @@ takewhile_case(Config) -> ukeymerge_case(Config) -> do_proptest(prop_ukeymerge, Config). +ukeymerge_invalid_case(Config) -> + do_proptest(prop_ukeymerge_invalid, Config). + ukeysort_case(Config) -> do_proptest(prop_ukeysort, Config). umerge_1_case(Config) -> do_proptest(prop_umerge_1, Config). +umerge_1_invalid_case(Config) -> + do_proptest(prop_umerge_1_invalid, Config). + umerge_2_case(Config) -> do_proptest(prop_umerge_2, Config). +umerge_2_invalid_case(Config) -> + do_proptest(prop_umerge_2_invalid, Config). + umerge_3_case(Config) -> do_proptest(prop_umerge_3, Config). +umerge_3_invalid_case(Config) -> + do_proptest(prop_umerge_3_invalid, Config). + umerge3_case(Config) -> do_proptest(prop_umerge3, Config). +umerge3_invalid_case(Config) -> + do_proptest(prop_umerge3_invalid, Config). + uniq_1_case(Config) -> do_proptest(prop_uniq_1, Config). diff --git a/lib/stdlib/test/property_test/lists_prop.erl b/lib/stdlib/test/property_test/lists_prop.erl index 4d5809b262..00b518ff83 100644 --- a/lib/stdlib/test/property_test/lists_prop.erl +++ b/lib/stdlib/test/property_test/lists_prop.erl @@ -513,6 +513,23 @@ prop_keymerge() -> ) ). +prop_keymerge_invalid() -> + ?FORALL( + {N, InList, X, Y}, + ?LET( + N, + range(1, 5), + ?LET( + {L, X, Y}, + {list(gen_tuple(N, N+3)), non_list(), non_list()}, + {N, L, X, Y} + ) + ), + expect_error(fun lists:keymerge/3, [N, InList, Y]) andalso + expect_error(fun lists:keymerge/3, [N, X, InList]) andalso + expect_error(fun lists:keymerge/3, [N, X, Y]) + ). + %% keyreplace/4 prop_keyreplace() -> ?FORALL( @@ -745,6 +762,17 @@ prop_merge_1() -> check_merged(fun erlang:'=<'/2, InLists, lists:merge(InLists)) ). +prop_merge_1_invalid() -> + ?FORALL( + InLists, + ?LET( + {L1, X, L2}, + {list(oneof([non_list(), gen_list()])), non_list(), list(oneof([non_list(), gen_list()]))}, + L1 ++ [X|L2] + ), + expect_error(fun lists:merge/1, [InLists]) + ). + %% merge/2 prop_merge_2() -> ?FORALL( @@ -757,6 +785,15 @@ prop_merge_2() -> check_merged(fun erlang:'=<'/2, [InList1, InList2], lists:merge(InList1, InList2)) ). +prop_merge_2_invalid() -> + ?FORALL( + {InList, X, Y}, + {gen_list(), non_list(), non_list()}, + expect_error(fun lists:merge/2, [InList, X]) andalso + expect_error(fun lists:merge/2, [X, InList]) andalso + expect_error(fun lists:merge/2, [X, Y]) + ). + %% merge/3 prop_merge_3() -> ?FORALL( @@ -769,6 +806,15 @@ prop_merge_3() -> check_merged(SortFn, [InList1, InList2], lists:merge(SortFn, InList1, InList2)) ). +prop_merge_3_invalid() -> + ?FORALL( + {SortFn, InList, X, Y}, + {gen_ordering_fun(), gen_list(), non_list(), non_list()}, + expect_error(fun lists:merge/3, [SortFn, InList, Y]) andalso + expect_error(fun lists:merge/3, [SortFn, X, InList]) andalso + expect_error(fun lists:merge/3, [SortFn, X, Y]) + ). + %% merge3/3 prop_merge3() -> ?FORALL( @@ -781,6 +827,18 @@ prop_merge3() -> check_merged(fun erlang:'=<'/2, [InList1, InList2, InList3], lists:merge3(InList1, InList2, InList3)) ). +prop_merge3_invalid() -> + ?FORALL( + {InList, X, Y, Z}, + {gen_list(), non_list(), non_list(), non_list()}, + expect_error(fun lists:merge/3, [InList, InList, Z]) andalso + expect_error(fun lists:merge/3, [InList, Y, InList]) andalso + expect_error(fun lists:merge/3, [InList, Y, Z]) andalso + expect_error(fun lists:merge/3, [X, InList, Z]) andalso + expect_error(fun lists:merge/3, [X, Y, InList]) andalso + expect_error(fun lists:merge/3, [X, Y, Z]) + ). + %% min/1 prop_min() -> ?FORALL( @@ -1132,6 +1190,23 @@ prop_ukeymerge() -> ) ). +prop_ukeymerge_invalid() -> + ?FORALL( + {N, InList, X, Y}, + ?LET( + N, + range(1, 5), + ?LET( + {L, X, Y}, + {list(gen_tuple(N, N+3)), non_list(), non_list()}, + {N, L, X, Y} + ) + ), + expect_error(fun lists:ukeymerge/3, [N, InList, Y]) andalso + expect_error(fun lists:ukeymerge/3, [N, X, InList]) andalso + expect_error(fun lists:ukeymerge/3, [N, X, Y]) + ). + %% ukeysort/2 prop_ukeysort() -> ?FORALL( @@ -1156,6 +1231,17 @@ prop_umerge_1() -> check_umerged(InLists, lists:umerge(InLists)) ). +prop_umerge_1_invalid() -> + ?FORALL( + InList, + ?LET( + {L1, X, L2}, + {list(oneof([non_list(), gen_list()])), non_list(), list(oneof([non_list(), gen_list()]))}, + L1 ++ [X|L2] + ), + expect_error(fun lists:umerge/1, [InList]) + ). + %% umerge/2 prop_umerge_2() -> ?FORALL( @@ -1168,6 +1254,15 @@ prop_umerge_2() -> check_umerged([InList1, InList2], lists:umerge(InList1, InList2)) ). +prop_umerge_2_invalid() -> + ?FORALL( + {InList, X, Y}, + {gen_list(), non_list(), non_list()}, + expect_error(fun lists:umerge/2, [InList, Y]) andalso + expect_error(fun lists:umerge/2, [X, InList]) andalso + expect_error(fun lists:umerge/2, [X, Y]) + ). + %% umerge/3 prop_umerge_3() -> ?FORALL( @@ -1180,6 +1275,15 @@ prop_umerge_3() -> check_umerged(SortFn, [InList1, InList2], lists:umerge(SortFn, InList1, InList2)) ). +prop_umerge_3_invalid() -> + ?FORALL( + {SortFn, InList, X, Y}, + {gen_ordering_fun(), gen_list(), non_list(), non_list()}, + expect_error(fun lists:umerge/3, [SortFn, InList, Y]) andalso + expect_error(fun lists:umerge/3, [SortFn, X, InList]) andalso + expect_error(fun lists:umerge/3, [SortFn, X, Y]) + ). + %% umerge3/3 prop_umerge3() -> ?FORALL( @@ -1192,6 +1296,19 @@ prop_umerge3() -> check_umerged([InList1, InList2, InList3], lists:umerge3(InList1, InList2, InList3)) ). +prop_umerge3_invalid() -> + ?FORALL( + {InList, X, Y, Z}, + {gen_list(), non_list(), non_list(), non_list()}, + expect_error(fun lists:umerge3/3, [InList, InList, Z]) andalso + expect_error(fun lists:umerge3/3, [InList, Y, InList]) andalso + expect_error(fun lists:umerge3/3, [InList, Y, Z]) andalso + expect_error(fun lists:umerge3/3, [X, InList, InList]) andalso + expect_error(fun lists:umerge3/3, [X, InList, Z]) andalso + expect_error(fun lists:umerge3/3, [X, Y, InList]) andalso + expect_error(fun lists:umerge3/3, [X, Y, Z]) + ). + %% uniq/1 prop_uniq_1() -> ?FORALL( @@ -1530,6 +1647,9 @@ prop_zipwith3_5() -> %%% Generators %%% %%%%%%%%%%%%%%%%%% +non_list() -> + ?SUCHTHAT(NonList, gen_any(), not is_list(NonList)). + %% Generator for lists of the given type, folding the given function %% over values on the top level as they are generated. The first generated %% value serves as the initial accumulator. @@ -1739,6 +1859,17 @@ gen_ordering_fun() -> %%% Helpers %%% %%%%%%%%%%%%%%% +%% -------------------------------------------------------------------- +expect_error(Fn, Args) when is_function(Fn, length(Args))-> + try + erlang:apply(Fn, Args) + of + _ -> false + catch + error:_ -> true; + _:_ -> false + end. + %% -------------------------------------------------------------------- check_appended([], []) -> true; -- cgit v1.2.1