1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 2004-2013. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %% This module tests the ordsets, sets, and gb_sets modules.
   21: %%
   22: 
   23: -module(sets_SUITE).
   24: 
   25: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   26: 	 init_per_group/2,end_per_group/2,
   27: 	 init_per_testcase/2,end_per_testcase/2,
   28: 	 create/1,add_element/1,del_element/1,
   29: 	 subtract/1,intersection/1,union/1,is_subset/1,
   30: 	 is_set/1,fold/1,filter/1,
   31: 	 take_smallest/1,take_largest/1]).
   32: 
   33: -include_lib("test_server/include/test_server.hrl").
   34: 
   35: -import(lists, [foldl/3,reverse/1]).
   36: 
   37: init_per_testcase(_Case, Config) ->
   38:     Dog = ?t:timetrap(?t:minutes(5)),
   39:     [{watchdog,Dog}|Config].
   40: 
   41: end_per_testcase(_Case, Config) ->
   42:     Dog = ?config(watchdog, Config),
   43:     test_server:timetrap_cancel(Dog),
   44:     ok.
   45: 
   46: suite() -> [{ct_hooks,[ts_install_cth]}].
   47: 
   48: all() -> 
   49:     [create, add_element, del_element, subtract,
   50:      intersection, union, is_subset, is_set, fold, filter,
   51:      take_smallest, take_largest].
   52: 
   53: groups() -> 
   54:     [].
   55: 
   56: init_per_suite(Config) ->
   57:     Config.
   58: 
   59: end_per_suite(_Config) ->
   60:     ok.
   61: 
   62: init_per_group(_GroupName, Config) ->
   63:     Config.
   64: 
   65: end_per_group(_GroupName, Config) ->
   66:     Config.
   67: 
   68: 
   69: create(Config) when is_list(Config) ->
   70:     test_all(fun create_1/1).
   71: 
   72: create_1(M) ->
   73:     S0 = M(empty, []),
   74:     [] = M(to_list, S0),
   75:     0 = M(size, S0),
   76:     true = M(is_empty, S0),
   77:     E = make_ref(),
   78:     One = M(singleton, E),
   79:     1 = M(size, One),
   80:     false = M(is_empty, One),
   81:     [E] = M(to_list, One),
   82:     S0.
   83: 
   84: add_element(Config) when is_list(Config) ->
   85:     test_all([{0,132},{253,258},{510,514}], fun add_element_1/2).
   86: 
   87: add_element_1(List, M) ->
   88:     S = M(from_list, List),
   89:     SortedSet = lists:usort(List),
   90:     SortedSet = lists:sort(M(to_list, S)),
   91: 
   92:     %% Make sure that we get the same result by inserting
   93:     %% elements one at the time.
   94:     S2 = foldl(fun(El, Set) -> M(add_element, {El,Set}) end,
   95: 	       M(empty, []), List),
   96:     true = M(equal, {S,S2}),
   97: 
   98:     %% Insert elements, randomly delete inserted elements,
   99:     %% and re-inserted all deleted elements at the end.
  100:     S3 = add_element_del(List, M, M(empty, []), [], []),
  101:     true = M(equal, {S2,S3}),
  102:     true = M(equal, {S,S3}),
  103:     S.
  104: 
  105: add_element_del([H|T], M, S, Del, []) ->
  106:     add_element_del(T, M, M(add_element, {H,S}), Del, [H]);
  107: add_element_del([H|T], M, S0, Del, Inserted) ->
  108:     S1 = M(add_element, {H,S0}),
  109:     case random:uniform(3) of
  110: 	1 ->
  111: 	    OldEl = lists:nth(random:uniform(length(Inserted)), Inserted),
  112: 	    S = M(del_element, {OldEl,S1}),
  113: 	    add_element_del(T, M, S, [OldEl|Del], [H|Inserted]);
  114: 	_ ->
  115: 	    add_element_del(T, M, S1, Del, [H|Inserted])
  116:     end;
  117: add_element_del([], M, S, Del, _) ->
  118:     M(union, {S,M(from_list, Del)}).
  119: 
  120: del_element(Config) when is_list(Config) ->
  121:     test_all([{0,132},{253,258},{510,514},{1022,1026}], fun del_element_1/2).
  122: 
  123: del_element_1(List, M) ->    
  124:     S0 = M(from_list, List),
  125:     Empty = foldl(fun(El, Set) -> M(del_element, {El,Set}) end, S0, List),
  126:     Empty = M(empty, []),
  127:     true = M(is_empty, Empty),
  128:     S1 = foldl(fun(El, Set) ->
  129: 		       M(add_element, {El,Set})
  130: 	       end, S0, reverse(List)),
  131:     true = M(equal, {S0,S1}),
  132:     S1.
  133: 
  134: subtract(Config) when is_list(Config) ->
  135:     test_all(fun subtract_empty/1),
  136: 
  137:     %% Note: No empty set.
  138:     test_all([{2,69},{126,130},{253,258},511,512,{1023,1030}], fun subtract_1/2).
  139: 
  140: subtract_empty(M) ->
  141:     Empty = M(empty, []),
  142:     true = M(is_empty, M(subtract, {Empty,Empty})),
  143:     M(subtract, {Empty,Empty}).
  144: 
  145: subtract_1(List, M) ->
  146:     S0 = M(from_list, List),
  147:     Empty = M(empty, []),
  148: 
  149:     %% Trivial cases.
  150:     true = M(is_empty, M(subtract, {Empty,S0})),
  151:     true = M(equal, {S0,M(subtract, {S0,Empty})}),
  152: 
  153:     %% Not so trivial.
  154:     subtract_check(List, mutate_some(remove_some(List, 0.4)), M),
  155:     subtract_check(List, rnd_list(length(List) div 2 + 5), M),
  156:     subtract_check(List, rnd_list(length(List) div 7 + 9), M),
  157:     subtract_check(List, mutate_some(List), M).
  158: 
  159: subtract_check(A, B, M) ->
  160:     one_subtract_check(B, A, M),
  161:     one_subtract_check(A, B, M).
  162: 
  163: one_subtract_check(A, B, M) ->
  164:     ASorted = lists:usort(A),
  165:     BSorted = lists:usort(B),
  166:     ASet = M(from_list, A),
  167:     BSet = M(from_list, B),
  168:     DiffSet = M(subtract, {ASet,BSet}),
  169:     Diff = ASorted -- BSorted,
  170:     true = M(equal, {DiffSet,M(from_list, Diff)}),
  171:     Diff = lists:sort(M(to_list, DiffSet)),
  172:     DiffSet.
  173: 
  174: intersection(Config) when is_list(Config) ->
  175:     %% Note: No empty set.
  176:     test_all([{1,65},{126,130},{253,259},{499,513},{1023,1025}], fun intersection_1/2).
  177: 
  178: intersection_1(List, M) ->
  179:     S0 = M(from_list, List),
  180: 
  181:     %% Intersection with self.
  182:     true = M(equal, {S0,M(intersection, {S0,S0})}),
  183:     true = M(equal, {S0,M(intersection, [S0,S0])}),
  184:     true = M(equal, {S0,M(intersection, [S0,S0,S0])}),
  185:     true = M(equal, {S0,M(intersection, [S0])}),
  186: 
  187:     %% Intersection with empty.
  188:     Empty = M(empty, []),
  189:     true = M(equal, {Empty,M(intersection, {S0,Empty})}),
  190:     true = M(equal, {Empty,M(intersection, [S0,Empty,S0,Empty])}),
  191: 
  192:     %% The intersection of no sets is undefined.
  193:     {'EXIT',_} = (catch M(intersection, [])),
  194: 
  195:     %% Disjoint sets.
  196:     Disjoint = [{El} || El <- List],
  197:     DisjointSet = M(from_list, Disjoint),
  198:     true = M(is_empty, M(intersection, {S0,DisjointSet})),
  199: 
  200:     %% Disjoint, different sizes.
  201:     [begin
  202: 	 SomeRemoved = M(from_list, remove_some(Disjoint, HowMuch)),
  203: 	 true = M(is_empty, M(intersection, {S0,SomeRemoved})),
  204: 	 MoreRemoved = M(from_list, remove_some(List, HowMuch)),
  205: 	 true = M(is_empty, M(intersection, {MoreRemoved,DisjointSet}))
  206:      end || HowMuch <- [0.3,0.5,0.7,0.9]],
  207: 
  208:     %% Partial overlap (one or more elements in result set).
  209:     %% The sets have almost the same size. (Almost because a duplicated
  210:     %% element in the original list could be mutated and not mutated
  211:     %% at the same time.)
  212:     PartialOverlap = mutate_some(List, []),
  213:     IntersectionSet = check_intersection(List, PartialOverlap, M),
  214:     false = M(is_empty, IntersectionSet),
  215: 
  216:     %% Partial overlap, different set sizes. (Intersection possibly empty.)
  217:     check_intersection(List, remove_some(PartialOverlap, 0.1), M),
  218:     check_intersection(List, remove_some(PartialOverlap, 0.3), M),
  219:     check_intersection(List, remove_some(PartialOverlap, 0.5), M),
  220:     check_intersection(List, remove_some(PartialOverlap, 0.7), M),
  221:     check_intersection(List, remove_some(PartialOverlap, 0.9), M),
  222: 
  223:     IntersectionSet.
  224: 
  225: check_intersection(Orig, Mutated, M) ->
  226:     OrigSet = M(from_list, Orig),
  227:     MutatedSet = M(from_list, Mutated),
  228:     Intersection = [El || El <- Mutated, not is_tuple(El)],
  229:     SortedIntersection = lists:usort(Intersection),
  230:     IntersectionSet = M(intersection, {OrigSet,MutatedSet}),
  231:     true = M(equal, {IntersectionSet,M(from_list, SortedIntersection)}),
  232:     SortedIntersection = lists:sort(M(to_list, IntersectionSet)),
  233: 
  234:     IntersectionSet.
  235: 
  236: 
  237: union(Config) when is_list(Config) ->
  238:     %% Note: No empty set.
  239:     test_all([{1,71},{125,129},{254,259},{510,513},{1023,1025}], fun union_1/2).
  240: 
  241: union_1(List, M) ->
  242:     S = M(from_list, List),
  243: 
  244:     %% Union with self and empty.
  245:     Empty = M(empty, []),
  246:     true = M(equal, {S,M(union, {S,S})}),
  247:     true = M(equal, {S,M(union, [S,S])}),
  248:     true = M(equal, {S,M(union, [S,S,Empty])}),
  249:     true = M(equal, {S,M(union, [S,Empty,S])}),
  250:     true = M(equal, {S,M(union, {S,Empty})}),
  251:     true = M(equal, {S,M(union, [S])}),
  252:     true = M(is_empty, M(union, [])),
  253: 
  254:     %% Partial overlap.
  255:     check_union(List, remove_some(mutate_some(List), 0.9), M),
  256:     check_union(List, remove_some(mutate_some(List), 0.7), M),
  257:     check_union(List, remove_some(mutate_some(List), 0.5), M),
  258:     check_union(List, remove_some(mutate_some(List), 0.3), M),
  259:     check_union(List, remove_some(mutate_some(List), 0.1), M),
  260: 
  261:     check_union(List, mutate_some(remove_some(List, 0.9)), M),
  262:     check_union(List, mutate_some(remove_some(List, 0.7)), M),
  263:     check_union(List, mutate_some(remove_some(List, 0.5)), M),
  264:     check_union(List, mutate_some(remove_some(List, 0.3)), M),
  265:     check_union(List, mutate_some(remove_some(List, 0.1)), M).
  266: 
  267: check_union(Orig, Other, M) ->
  268:     OrigSet = M(from_list, Orig),
  269:     OtherSet = M(from_list, Other),
  270:     Union = Orig++Other,
  271:     SortedUnion = lists:usort(Union),
  272:     UnionSet = M(union, {OrigSet,OtherSet}),
  273:     SortedUnion = lists:sort(M(to_list, UnionSet)),
  274:     M(equal, {UnionSet,M(from_list, Union)}),
  275:     UnionSet.
  276: 
  277: is_subset(Config) when is_list(Config) ->
  278:     test_all([{1,132},{253,270},{299,311}], fun is_subset_1/2).
  279: 
  280: is_subset_1(List, M) ->
  281:     S = M(from_list, List),
  282:     Empty = M(empty, []),
  283: 
  284:     %% Subset of empty and self.
  285:     true = M(is_subset, {Empty,Empty}),
  286:     true = M(is_subset, {Empty,S}),
  287:     false = M(is_subset, {S,Empty}),
  288:     true = M(is_subset, {S,S}),
  289: 
  290:     %% Other cases.
  291:     Res = [false = M(is_subset, {M(singleton, make_ref()),S}),
  292: 	   true = M(is_subset, {M(singleton, hd(List)),S}),
  293: 	   true = check_subset(remove_some(List, 0.1), List, M),
  294: 	   true = check_subset(remove_some(List, 0.5), List, M),
  295: 	   true = check_subset(remove_some(List, 0.9), List, M),
  296: 	   check_subset(mutate_some(List), List, M),
  297: 	   check_subset(rnd_list(length(List) div 2 + 5), List, M),
  298: 	   subtract_check(List, rnd_list(length(List) div 7 + 9), M)
  299: 	  ],
  300:     res_to_set(Res, M, 0, []).
  301: 
  302: check_subset(X, Y, M) ->
  303:     check_one_subset(Y, X, M),
  304:     check_one_subset(X, Y, M).
  305: 
  306: check_one_subset(X, Y, M) ->
  307:     XSet = M(from_list, X),
  308:     YSet = M(from_list, Y),
  309:     SortedX = lists:usort(X),
  310:     SortedY = lists:usort(Y),
  311:     IsSubSet = length(SortedY--SortedX) =:= length(SortedY) - length(SortedX),
  312:     IsSubSet = M(is_subset, {XSet,YSet}),
  313:     IsSubSet.
  314: 
  315: %% Encode all test results as a set to return.
  316: res_to_set([true|T], M, I, Acc) ->
  317:     res_to_set(T, M, I+1, [I|Acc]);
  318: res_to_set([_|T], M, I, Acc) ->
  319:     res_to_set(T, M, I+1, Acc);
  320: res_to_set([], M, _, Acc) -> M(from_list, Acc).
  321: 
  322: is_set(Config) when is_list(Config) ->
  323:     %% is_set/1 is tested in the other test cases when its argument
  324:     %% is a set. Here test some arguments that makes it return false.
  325: 
  326:     false = gb_sets:is_set([a,b]),
  327:     false = gb_sets:is_set({a,very,bad,tuple}),
  328: 
  329:     false = sets:is_set([a,b]),
  330:     false = sets:is_set({a,very,bad,tuple}),
  331: 
  332:     false = ordsets:is_set([b,a]),
  333:     false = ordsets:is_set({bad,tuple}),
  334: 
  335:     %% Now test values that are known to be bad for all set representations.
  336:     test_all(fun is_set_1/1).
  337: 
  338: is_set_1(M) ->
  339:     false = M(is_set, self()),
  340:     false = M(is_set, blurf),
  341:     false = M(is_set, make_ref()),
  342:     false = M(is_set, <<1,2,3>>),
  343:     false = M(is_set, 42),
  344:     false = M(is_set, math:pi()),
  345:     false = M(is_set, {}),
  346:     M(empty, []).
  347: 
  348: fold(Config) when is_list(Config) ->
  349:     test_all([{0,71},{125,129},{254,259},{510,513},{1023,1025},{9999,10001}],
  350: 	     fun fold_1/2).
  351: 
  352: fold_1(List, M) ->
  353:     S = M(from_list, List),
  354:     L = M(fold, {fun(E, A) -> [E|A] end,[],S}),
  355:     true = lists:sort(L) =:= lists:usort(List),
  356:     M(empty, []).
  357: 
  358: filter(Config) when is_list(Config) ->
  359:     test_all([{0,69},{126,130},{254,259},{510,513},{1023,1025},{7999,8000}],
  360: 	     fun filter_1/2).
  361: 
  362: filter_1(List, M) ->
  363:     S = M(from_list, List),
  364:     IsNumber = fun(X) -> is_number(X) end,
  365:     M(equal, {M(from_list, lists:filter(IsNumber, List)),
  366: 	      M(filter, {IsNumber,S})}),
  367:     M(filter, {fun(X) -> is_atom(X) end,S}).
  368: 
  369: %%%
  370: %%% Test specifics for gb_sets.
  371: %%%
  372: 
  373: take_smallest(Config) when is_list(Config) ->
  374:     test_all([{1,71},{125,129},{254,259},{510,513},{1023,1025}],
  375: 	     fun take_smallest_1/2).
  376: 
  377: take_smallest_1(List, M) ->
  378:     case M(module, []) of
  379: 	gb_sets -> take_smallest_2(List, M);
  380: 	_ -> ok
  381:     end,
  382:     M(empty, []).
  383: 
  384: take_smallest_2(List0, M) ->
  385:     List = lists:usort(List0),
  386:     S = M(from_list, List0),
  387:     take_smallest_3(S, List, M).
  388: 
  389: take_smallest_3(S0, List0, M) ->
  390:     case M(is_empty, S0) of
  391: 	true -> ok;
  392: 	false ->
  393: 	    Smallest = hd(List0),
  394: 	    Smallest = gb_sets:smallest(S0),
  395: 	    {Smallest,S} = gb_sets:take_smallest(S0),
  396: 	    List = tl(List0),
  397: 	    true = gb_sets:to_list(S) =:= List,
  398: 	    take_smallest_3(S, List, M)
  399:     end.
  400: 
  401: take_largest(Config) when is_list(Config) ->
  402:     test_all([{1,71},{125,129},{254,259},{510,513},{1023,1025}],
  403: 	     fun take_largest_1/2).
  404: 
  405: take_largest_1(List, M) ->
  406:     case M(module, []) of
  407: 	gb_sets -> take_largest_2(List, M);
  408: 	_ -> ok
  409:     end,
  410:     M(empty, []).
  411: 
  412: take_largest_2(List0, M) ->
  413:     List = reverse(lists:usort(List0)),
  414:     S = M(from_list, List0),
  415:     take_largest_3(S, List, M).
  416: 
  417: take_largest_3(S0, List0, M) ->
  418:     case M(is_empty, S0) of
  419: 	true -> ok;
  420: 	false ->
  421: 	    Largest = hd(List0),
  422: 	    Largest = gb_sets:largest(S0),
  423: 	    {Largest,S} = gb_sets:take_largest(S0),
  424: 	    List = tl(List0),
  425: 	    true = gb_sets:to_list(S) =:= reverse(List),
  426: 	    take_largest_3(S, List, M)
  427:     end.
  428: 
  429: %%%
  430: %%% Helper functions.
  431: %%%
  432: 
  433: sets_mods() ->
  434:     Ordsets = sets_test_lib:new(ordsets, fun(X, Y) -> X == Y end),
  435:     Sets = sets_test_lib:new(sets, fun(X, Y) ->
  436: 					   lists:sort(sets:to_list(X)) == 
  437: 					       lists:sort(sets:to_list(Y)) end),
  438:     Gb = sets_test_lib:new(gb_sets, fun(X, Y) ->
  439: 					    gb_sets:to_list(X) == 
  440: 						gb_sets:to_list(Y) end),
  441:     [Ordsets,Sets,Gb].
  442: 
  443: test_all(Tester) ->
  444:     Res = [begin
  445: 	       random:seed(1, 2, 42),
  446: 	       S = Tester(M),
  447: 	       {M(size, S),lists:sort(M(to_list, S))}
  448: 	   end || M <- sets_mods()],
  449:     all_same(Res).
  450: 
  451: test_all([{Low,High}|T], Tester) ->
  452:     test_all(lists:seq(Low, High)++T, Tester);
  453: test_all([Sz|T], Tester) when is_integer(Sz) ->
  454:     List = rnd_list(Sz),
  455:     Res = [begin
  456: 		     random:seed(19, 2, Sz),
  457: 		     S = Tester(List, M),
  458: 		     {M(size, S),lists:sort(M(to_list, S))}
  459: 		 end || M <- sets_mods()],
  460:     all_same(Res),
  461:     test_all(T, Tester);
  462: test_all([], _) -> ok.
  463: 
  464: 
  465: all_same([H|T]) ->
  466:     all_same_1(T, H).
  467: 
  468: all_same_1([H|T], H) ->
  469:     all_same_1(T, H);
  470: all_same_1([], _) -> ok.
  471: 
  472: rnd_list(Sz) ->
  473:     rnd_list_1(Sz, []).
  474:     
  475: atomic_rnd_term() ->
  476:     case random:uniform(3) of
  477: 	1 -> list_to_atom(integer_to_list($\s+random:uniform(94))++"rnd");
  478: 	2 -> random:uniform();
  479: 	3 -> random:uniform(50)-37
  480:     end.
  481: 
  482: rnd_list_1(0, Acc) -> Acc;
  483: rnd_list_1(N, Acc) -> rnd_list_1(N-1, [atomic_rnd_term()|Acc]).
  484: 
  485: mutate_some(List) ->
  486:     mutate_some(List, []).
  487:     
  488: mutate_some([X,Y,Z|T], Acc) ->
  489:     %% Intentionally change order. (Order should not matter.)
  490:     mutate_some(T, [{X},Z,Y|Acc]);
  491: mutate_some([H|T], Acc) ->
  492:     mutate_some(T, [H|Acc]);
  493: mutate_some([], Acc) ->
  494:     %% Intentionally not reversing.
  495:     Acc.
  496: 
  497: %% Removes at least one element.
  498: remove_some(List0, P) ->
  499:     case remove_some(List0, P, []) of
  500: 	List when length(List0) =:= length(List) ->
  501: 	    tl(List);
  502: 	List ->
  503: 	    List
  504:     end.
  505: 
  506: remove_some([H|T], P, Acc) ->
  507:     case random:uniform() of
  508: 	F when F < P ->				%Remove.
  509: 	    remove_some(T, P, Acc);
  510: 	_ ->
  511: 	    remove_some(T, P, [H|Acc])
  512:     end;
  513: remove_some([], _, Acc) ->
  514:     %% Intentionally no reverse. Order should not matter.
  515:     Acc.