summaryrefslogtreecommitdiff
path: root/lib/stdlib/test
diff options
context:
space:
mode:
authorMaria Scott <maria-12648430@hnc-agency.org>2023-02-15 17:15:17 +0100
committerMaria Scott <maria-12648430@hnc-agency.org>2023-02-23 12:57:14 +0100
commit126ef4f4830624eec10518875a851223d4f7054f (patch)
tree8acf02d1837ef7c5b0c80ceaee95772986e8b4ab /lib/stdlib/test
parentb31b8ad1225caed2944f6f1eb4a9d98fc7af9709 (diff)
downloaderlang-126ef4f4830624eec10518875a851223d4f7054f.tar.gz
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 <juhlig@hnc-agency.org>
Diffstat (limited to 'lib/stdlib/test')
-rw-r--r--lib/stdlib/test/lists_SUITE.erl242
-rw-r--r--lib/stdlib/test/lists_property_test_SUITE.erl50
-rw-r--r--lib/stdlib/test/property_test/lists_prop.erl131
3 files changed, 413 insertions, 10 deletions
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.
@@ -1740,6 +1860,17 @@ gen_ordering_fun() ->
%%%%%%%%%%%%%%%
%% --------------------------------------------------------------------
+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;
check_appended([[]|Ls], AL) ->