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.