1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2005-2012. 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: -module(erl_expand_records_SUITE). 21: 22: %-define(debug, true). 23: 24: -ifdef(debug). 25: -define(line, put(line, ?LINE), ). 26: -define(config(X,Y), foo). 27: -define(privdir, "erl_expand_records_SUITE_priv"). 28: -define(t, test_server). 29: -else. 30: -include_lib("test_server/include/test_server.hrl"). 31: -define(privdir, ?config(priv_dir, Config)). 32: -endif. 33: 34: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 35: init_per_group/2,end_per_group/2, 36: init_per_testcase/2, end_per_testcase/2]). 37: 38: -export([attributes/1, expr/1, guard/1, 39: init/1, pattern/1, strict/1, update/1, 40: otp_5915/1, otp_7931/1, otp_5990/1, 41: otp_7078/1, otp_7101/1]). 42: 43: % Default timetrap timeout (set in init_per_testcase). 44: -define(default_timeout, ?t:minutes(1)). 45: 46: init_per_testcase(_Case, Config) -> 47: ?line Dog = ?t:timetrap(?default_timeout), 48: [{watchdog, Dog} | Config]. 49: 50: end_per_testcase(_Case, _Config) -> 51: Dog = ?config(watchdog, _Config), 52: test_server:timetrap_cancel(Dog), 53: ok. 54: 55: suite() -> [{ct_hooks,[ts_install_cth]}]. 56: 57: all() -> 58: [attributes, expr, guard, init, 59: pattern, strict, update, {group, tickets}]. 60: 61: groups() -> 62: [{tickets, [], 63: [otp_5915, otp_7931, otp_5990, otp_7078, otp_7101]}]. 64: 65: init_per_suite(Config) -> 66: Config. 67: 68: end_per_suite(_Config) -> 69: ok. 70: 71: init_per_group(_GroupName, Config) -> 72: Config. 73: 74: end_per_group(_GroupName, Config) -> 75: Config. 76: 77: 78: attributes(doc) -> 79: "Import module and functions."; 80: attributes(suite) -> []; 81: attributes(Config) when is_list(Config) -> 82: Ts = [ 83: <<"-import(lists, [append/2, reverse/1]). 84: 85: -record(r, {a,b}). 86: 87: t() -> 88: [2,1] = reverse(append([1],[2])), 89: 3 = length([1,2,3]), 90: 3 = record_info(size, r), 91: [a, b] = record_info(fields, r), 92: [] = erl_expand_records_SUITE:attributes(suite), 93: ok. 94: ">> 95: ], 96: ?line run(Config, Ts), 97: ok. 98: 99: expr(doc) -> 100: "Some expressions."; 101: expr(suite) -> []; 102: expr(Config) when is_list(Config) -> 103: Ts = [ 104: <<" 105: -record(r, {a,b,c}). 106: 107: t() -> 108: [1,2] = [R#r.a || R <- [#r{a = 1}, #r{a = 2}, #r{a = 3}], 109: R#r.a < 3], 110: [1,2] = [R#r.a || R <- [#r{a = 1}, #r{a = 2}, #r{a = 3}], 111: begin R#r.a < 3 end], 112: [1,2,3] = [R#r.a || R <- [#r{a = 1}, #r{a = 2}, #r{a = 3}], 113: begin is_record(R, r) end], 114: [1,2,3] = [R#r.a || R <- [#r{a = 1}, #r{a = 2}, #r{a = 3}], 115: begin erlang:is_record(R, r) end], 116: ok. 117: ">>, 118: <<" 119: -record(r, {a,b,c}). 120: 121: f(X) -> X. 122: 123: t() -> 124: A = {$c, 1, 3.14, a, \"hi\", [], [a,b]}, 125: R = #r{a = element(6, A), b = #r.b}, 126: 3 = R#r.b, 127: <<1:8>> = <<(begin erlang:element(2, A) end):8>>, 128: self() ! {a, message, []}, 129: One = 1 = fun f/1(1), 130: 2 = fun(X) -> X end(One + One), 131: 3 = fun exprec_test:f/1(3), 132: 4 = exprec_test:f(4), 133: 5 = f(5), 134: L = receive 135: {a,message,L0} -> 136: L0 137: end, 138: case catch a:foo(bar) of 139: {'EXIT', _} -> ok 140: end, 141: _ = receive %Suppress warning. 142: noop -> 143: 1/(length(L) - 0) 144: after 0 -> 145: ok 146: end, 147: if 148: R#r.c =:= undefined -> 149: ok; 150: true -> 151: not_ok 152: end. 153: 154: is_record(_, _, _) -> 155: error(wrong_is_record). 156: ">> 157: ], 158: 159: %% The code above should run equally well with and without 160: %% strict record tests. 161: ?line run(Config, Ts, [no_strict_record_tests]), 162: ?line run(Config, Ts, [strict_record_tests]), 163: 164: ok. 165: 166: guard(doc) -> 167: "is_record in guards."; 168: guard(suite) -> []; 169: guard(Config) when is_list(Config) -> 170: File = filename("guard.erl", Config), 171: Beam = filename("guard.beam", Config), 172: Test = <<"-module(guard). 173: 174: -export([t/1]). 175: 176: -record(r, {a,b}). 177: 178: t(_) when is_record(3, r) -> 179: 1; 180: t(_) when is_record(a, r) -> 181: 2; 182: t(_) when is_record(3.14, r) -> 183: 3; 184: t(_) when is_record([], r) -> 185: 4; 186: t(_) when is_record([a], r) -> 187: 5; 188: t(_) when is_record($a, r) -> 189: 6; 190: t(_) when is_record(\"foo\", r) -> 191: 7; 192: t(_) when is_record(#r.a, r) -> 193: 8; 194: t(_) when is_record(<<\"foo\">>, r) -> % line 23 195: 9; 196: t(_) when is_record(1 + 2, r) -> 197: 10; 198: t(_) when is_record(+ 3, r) -> 199: 11; 200: t(_) -> 201: 12. 202: ">>, 203: 204: ?line ok = file:write_file(File, Test), 205: ?line {ok, guard, Ws} = compile:file(File, [return,{outdir,?privdir}]), 206: ?line Warnings = [L || {_File,WL} <- Ws, {L,_M,nomatch_guard} <- WL], 207: ?line [7,9,11,13,15,17,19,21,23,25,27] = Warnings, 208: 209: ?line ok = file:delete(File), 210: ?line ok = file:delete(Beam), 211: ok. 212: 213: init(doc) -> 214: "Wildcard initialisation."; 215: init(suite) -> []; 216: init(Config) when is_list(Config) -> 217: Ts = [ 218: <<" 219: -record(r, {a,b,c,d = foo}). 220: 221: t() -> 222: R = #r{_ = init, b = b}, 223: #r{c = init, b = b, a = init} = R, 224: case R of 225: #r{b = b, _ = init} -> ok; 226: _ -> not_ok 227: end. 228: ">> 229: ], 230: ?line run(Config, Ts), 231: ok. 232: 233: pattern(doc) -> 234: "Some patterns."; 235: pattern(suite) -> []; 236: pattern(Config) when is_list(Config) -> 237: Ts = [ 238: <<"-import(lists, [append/2, reverse/1]). 239: 240: -record(r, {a,b}). 241: 242: t() -> 243: 1 = t(#r{}), 244: 2 = t($a), 245: 3 = t(1000), 246: 4 = t({1000}), 247: 5 = t(3), 248: 6 = t(-3.14), 249: 7 = t({4.0}), 250: 8 = t(3.14), 251: 9 = t(\"str\"), 252: 10 = t([]), 253: 11 = t([a|b]), 254: 12 = t(\"string\"), 255: 13 = t({[]}), 256: 14 = t({a,b}), 257: 15 = t({{}}), 258: 16 = t({tuple,tupel}), 259: 17 = t(4), 260: 18 = t(10), 261: 19 = t({a}), 262: 20 = t(<<100:8,220:8>>), 263: 21 = t(#r{a = #r{}}), 264: 22 = t(2), 265: 23 = t(#r{a = #r{}, b = b}), 266: 24 = t(abc), 267: ok. 268: 269: t(abc) -> 270: 24; 271: t($a) -> 272: 2; 273: t(3) -> 274: 5; 275: t(3.14) -> 276: 8; 277: t(\"str\") -> 278: 9; 279: t([]) -> 280: 10; 281: t([a|b]) -> 282: 11; 283: t(L) when is_list(L) -> 284: 12; 285: t({L}) when list(L) -> 286: 13; 287: t({a,b}) -> 288: 14; 289: t({T}) when is_tuple(T) -> 290: 15; 291: t(+ 4) -> 292: 17; 293: t(3+7) -> 294: 18; 295: t(<<A:8, (100+120):8>>) when A =:= 100 -> 296: 20; 297: t(#r{a = #r{}, b = undefined}) -> 298: 21; 299: t(#r.a) -> 300: 22; 301: t(A) when is_record(A, r), record(element(2, A), r) -> 302: 23; 303: t(A) when is_record(A, r) -> 304: 1; 305: t(I) when is_integer(I) -> 306: 3; 307: t({I}) when integer(I) -> 308: 4; 309: t({F}) when float(F) -> 310: 7; 311: t({A} = B) when A < B -> 312: 19; 313: t(F) when is_float(F) -> 314: 6; 315: t(T) when tuple(T) -> 316: 16. 317: ">> 318: ], 319: ?line run(Config, Ts), 320: ok. 321: 322: strict(doc) -> 323: ""; 324: strict(suite) -> []; 325: strict(Config) when is_list(Config) -> 326: Ts1 = [ 327: <<"-record(r1, {a,b}). 328: -record(r2, {a,b}). 329: 330: t() -> 331: A = #r1{a = 1, b = 2}, 332: ok = try 333: {1, 2} = {A#r2.a, A#r2.b}, 334: not_ok 335: catch error:{badrecord,r2} -> ok 336: end, 337: try 338: case foo of 339: _ when A#r2.a =:= 1 -> not_ok 340: end 341: catch error:_ -> ok 342: end. 343: element(_, _) -> 344: error(wrong_element). 345: ">> 346: ], 347: ?line run(Config, Ts1, [strict_record_tests]), 348: 349: Ts2 = [ 350: <<"-record(r1, {a,b}). 351: -record(r2, {a,b}). 352: 353: t() -> 354: A = #r1{a = 1, b = 2}, 355: {1, 2} = {A#r2.a, A#r2.b}, 356: case foo of 357: _ when A#r2.a =:= 1 -> ok 358: end. 359: element(_, _) -> 360: error(wrong_element). 361: ">> 362: ], 363: ?line run(Config, Ts2, [no_strict_record_tests]), 364: ok. 365: 366: update(doc) -> 367: "Record updates."; 368: update(suite) -> []; 369: update(Config) when is_list(Config) -> 370: Ts = [ 371: <<"-record(r, {a,b,c,d,e,f}). 372: 373: t() -> 374: R0 = #r{}, 375: R1 = R0#r{a = #r.a, e = {x,y}}, 376: 2 = R1#r.a, 377: R2 = R1#r{}, 378: true = R1 =:= R2, 379: R3 = R2#r{c = fun(X) -> X end, 380: d = <<\"foo\">>, 381: e = [x,y,z], 382: f = {R0,R1}}, 383: R4 = R3#r{a = R3#r{b = #r{}}}, 384: true = erlang:is_record((R4#r.a)#r.b, r), 385: #r{a = R0, b = 3, c = 3.14, d = [], e = [[]], f = [{}]} = 386: R4#r{a = R0, b = 3, c = 3.14, d = [], e = [[]], f = [{}]}, 387: ok. 388: 389: %% Just playing around a bit... 390: t1() -> 391: ((#r{a = (#r{b = #r{}})#r{a = #r{}}})#r{b = #r{}})#r{c = #r{}}. 392: 393: t2() -> 394: R0 = #r{}, 395: #r{_ = R0#r{a = ok}}. 396: 397: %% Implicit calls to setelement/3 must go to the BIF, 398: %% not to this function. 399: setelement(_, _, _) -> 400: erlang:error(wrong_setelement_called). 401: ">> 402: ], 403: ?line run(Config, Ts), 404: ok. 405: 406: 407: otp_5915(doc) -> 408: "Strict record tests in guards."; 409: otp_5915(suite) -> []; 410: otp_5915(Config) when is_list(Config) -> 411: %% These tests are also run by the compiler's record_SUITE. 412: Ts = [ 413: <<"-record(r, {a = 4,b}). 414: -record(r1, {a,b}). 415: -record(r2, {a = #r1{},b,c=length([1,2,3])}). 416: -record(r3, {a = fun(_) -> #r1{} end(1), b}). 417: 418: t() -> 419: foo = fun(A) when A#r1.a > A#r1.b -> foo end(#r1{b = 2}), 420: 0 = fun(A) when A#r2.a -> 0 end(#r2{a = true}), 421: 1 = fun(A) when (#r1{a = A})#r1.a > 2 -> 1 end(3), 422: 2 = fun(N) when ((#r2{a = #r{a = 4}, b = length([a,b,c])})#r2.a)#r.a > N -> 423: 2 end(2), 424: 3 = fun(A) when (A#r2.a)#r1.a =:= 3 -> 3 end(#r2{a = #r1{a = 3}}), 425: ok = fun() -> 426: F = fun(A) when record(A#r.a, r1) -> 4; 427: (A) when record(A#r1.a, r1) -> 5 428: end, 429: 5 = F(#r1{a = #r1{}}), 430: 4 = F(#r{a = #r1{}}), 431: ok 432: end(), 433: 3 = fun(A) when record(A#r1.a, r), 434: (A#r1.a)#r.a > 3 -> 3 435: end(#r1{a = #r{a = 4}}), 436: 7 = fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}), 437: [#r1{a = 2,b = 1}] = 438: fun() -> 439: [A || A <- [#r1{a = 1, b = 3}, 440: #r2{a = 2,b = 1}, 441: #r1{a = 2, b = 1}], 442: A#r1.a > 443: A#r1.b] 444: end(), 445: {[_],b} = 446: fun(L) -> 447: %% A is checked only once: 448: R1 = [{A,B} || A <- L, A#r1.a, B <- L, A#r1.b], 449: A = #r2{a = true}, 450: %% A is checked again: 451: B = if A#r1.a -> a; true -> b end, 452: {R1,B} 453: end([#r1{a = true, b = true}]), 454: 455: p = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; 456: (_) -> p 457: end(#r1{a = 2}), 458: 459: o = fun(A) when (A#r1.a =:= 2) orelse (A#r2.a =:= 1) -> o; 460: (_) -> p 461: end(#r1{a = 2}), 462: 463: 3 = fun(A) when A#r1.a > 3, 464: record(A, r1) -> 3 465: end(#r1{a = 5}), 466: 467: ok = fun() -> 468: F = fun(A) when (A#r2.a =:= 1) orelse (A#r2.a) -> 2; 469: (A) when (A#r1.a =:= 1) orelse (A#r1.a) -> 1; 470: (A) when (A#r2.a =:= 2) andalso (A#r2.b) -> 3 471: end, 472: 1 = F(#r1{a = 1}), 473: 2 = F(#r2{a = true}), 474: 3 = F(#r2{a = 2, b = true}), 475: ok 476: end(), 477: 478: b = fun(A) when false or not (A#r.a =:= 1) -> a; 479: (_) -> b 480: end(#r1{a = 1}), 481: b = fun(A) when not (A#r.a =:= 1) or false -> a; 482: (_) -> b 483: end(#r1{a = 1}), 484: 485: ok = fun() -> 486: F = fun(A) when not (A#r.a =:= 1) -> yes; 487: (_) -> no 488: end, 489: no = F(#r1{a = 2}), 490: yes = F(#r{a = 2}), 491: no = F(#r{a = 1}), 492: ok 493: end(), 494: 495: a = fun(A) when record(A, r), 496: A#r.a =:= 1, 497: A#r.b =:= 2 ->a 498: end(#r{a = 1, b = 2}), 499: a = fun(A) when erlang:is_record(A, r), 500: A#r.a =:= 1, 501: A#r.b =:= 2 -> a 502: end(#r{a = 1, b = 2}), 503: a = fun(A) when is_record(A, r), 504: A#r.a =:= 1, 505: A#r.b =:= 2 -> a 506: end(#r{a = 1, b = 2}), 507: 508: nop = fun(A) when (is_record(A, r1) and (A#r1.a > 3)) or (A#r2.a < 1) -> 509: japp; 510: (_) -> 511: nop 512: end(#r2{a = 0}), 513: nop = fun(A) when (A#r1.a > 3) or (A#r2.a < 1) -> japp; 514: (_) -> 515: nop 516: end(#r2{a = 0}), 517: 518: ok = fun() -> 519: F = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; 520: (_) -> p 521: end, 522: p = F(#r2{a = 1}), 523: p = F(#r1{a = 2}), 524: ok 525: end(), 526: 527: ok = fun() -> 528: F = fun(A) when fail, A#r1.a; A#r1.a -> ab; 529: (_) -> bu 530: end, 531: ab = F(#r1{a = true}), 532: bu = F(#r2{a = true}), 533: ok 534: end(), 535: 536: both = fun(A) when A#r.a, A#r.b -> both 537: end(#r{a = true, b = true}), 538: 539: ok = fun() -> 540: F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) 541: or (B#r2.b) or (A#r1.b) -> true; 542: (_, _) -> false 543: end, 544: true = F(#r1{a = false, b = false}, #r2{a = false, b = true}), 545: false = F(#r1{a = true, b = true}, #r1{a = false, b = true}), 546: ok 547: end(), 548: 549: ok. 550: ">> 551: ], 552: ?line run(Config, Ts, [strict_record_tests]), 553: ok. 554: 555: otp_7931(doc) -> 556: "Test optimization of record accesses and is_record/3 tests in guards"; 557: otp_7931(suite) -> []; 558: otp_7931(Config) when is_list(Config) -> 559: Ts = [ 560: <<"-record(r, {a = 4,b}). 561: -record(r1, {a,b}). 562: -record(r2, {a = #r1{},b,c=length([1,2,3])}). 563: -record(r3, {a = fun(_) -> #r1{} end(1), b}). 564: 565: t() -> 566: ok = fun() -> 567: F = fun(F, [H,H|T]) when is_record(H, r) -> 568: [H|F(F, T)]; 569: (F, [H|T]) when is_record(H, r) -> 570: [H|F(F, T)]; 571: (_, []) -> [] 572: end, 573: [#r{a=4,b=7},#r{a=1,b=42}] = 574: F(F, [#r{a=4,b=7},#r{a=4,b=7},#r{a=1,b=42}]), 575: {'EXIT',_} = (catch F(F, [#r1{}])), 576: ok 577: end(), 578: 579: true = fun() -> 580: R = #r{}, 581: if is_record(R, r) -> true; true -> false end 582: end(), 583: 584: ok = fun() -> 585: F = fun(true, B) when B#r1.a -> ok; 586: (false, _) -> error 587: end, 588: ok = F(true, #r1{a=true}), 589: error = F(false, anything_goes), 590: {'EXIT',_} = (catch F(true, #r1{})), 591: {'EXIT',_} = (catch F(true, #r{})), 592: ok 593: end(), 594: 595: ok = fun() -> 596: F = fun([{a,R}=T]) when R#r.a =:= 42 -> 597: {ok,tuple_size(T)}; 598: ([{a,R}=T]) when R#r1.a =:= 7 -> 599: {ok,tuple_size(T)}; 600: (_) -> error 601: end, 602: {ok,2} = F([{a,#r{a=42}}]), 603: {ok,2} = F([{a,#r1{a=7}}]), 604: error = F([{a,#r1{}}]), 605: error = F({a,b,c}), 606: error = F([]), 607: ok 608: end(), 609: 610: ok = fun() -> 611: F = fun(X, Y, Z) when is_record(X, r1) andalso 612: (is_record(Y, r2) orelse 613: is_record(Z, r3)) -> true; 614: (_, _, _) -> false 615: end, 616: true = F(#r1{}, #r2{}, #r3{}), 617: true = F(#r1{}, #r2{}, blurf), 618: true = F(#r1{}, blurf, #r3{}), 619: false = F(#r1{}, blurf, blurf), 620: false = F(blurf, #r2{}, #r3{}), 621: false = F(blurf, #r2{}, blurf), 622: false = F(blurf, blurf, #r3{}), 623: false = F(blurf, blurf, blurf), 624: ok 625: end(), 626: 627: ok = fun() -> 628: F = fun(R=#r{a=42}) when R#r.b =:= 7 -> 629: {ok,R}; 630: (_) -> error 631: end, 632: {ok,#r{a=42,b=7}} = F(#r{a=42,b=7}), 633: error = F(#r{}), 634: error = F([a,b,c]), 635: ok 636: end(), 637: 638: ok. 639: ">> 640: ], 641: ?line run(Config, Ts, [strict_record_tests]), 642: ok. 643: 644: otp_5990(doc) -> 645: "OTP-5990. {erlang,is_record}."; 646: otp_5990(suite) -> []; 647: otp_5990(Config) when is_list(Config) -> 648: Ts = [ 649: <<" 650: -record(r, {a,b,c}). 651: 652: t() -> 653: [1,2,3] = [R#r.a || R <- [#r{a = 1}, #r{a = 2}, #r{a = 3}], 654: begin {erlang,is_record}(R, r) end], 655: [1,2,3] = [R#r.a || R <- [#r{a = 1}, #r{a = 2}, #r{a = 3}], 656: begin {erlang,is_record}(R, r) end], 657: ok. 658: ">>, 659: 660: <<" 661: -record('OrdSet', {orddata = {}, 662: ordtype = {}}). 663: 664: to_sets(S) when tuple(S#'OrdSet'.ordtype) -> 665: ok. 666: 667: lc(S) -> 668: [X || X <- [S], tuple(X#'OrdSet'.ordtype)]. 669: 670: t() -> 671: S = #'OrdSet'{}, 672: ok = to_sets(S), 673: [S] = lc(S), 674: ok. 675: ">> 676: ], 677: ?line run(Config, Ts, [strict_record_tests]), 678: ok. 679: 680: 681: otp_7078(doc) -> 682: "OTP-7078. Record update: missing test."; 683: otp_7078(suite) -> []; 684: otp_7078(Config) when is_list(Config) -> 685: Ts = [ 686: <<" 687: -record(r, {f}). 688: -record(r2, {}). 689: 690: t() -> 691: {'EXIT',_} = (catch (#r2{})#r{}), 692: {'EXIT',_} = (catch (#r2{})#r{f = 2}), 693: ok. 694: ">>, 695: 696: <<" 697: -record(r, {f}). 698: 699: maker(F) -> 700: put(a, get(a)+1), 701: #r{f = F}. 702: 703: t() -> 704: put(a, 0), 705: (maker(2))#r{}, 706: 1 = get(a), 707: ok. 708: ">> 709: 710: ], 711: ?line run(Config, Ts, [strict_record_tests]), 712: ok. 713: 714: -record(otp_7101, {a,b,c=[],d=[],e=[]}). 715: 716: otp_7101(doc) -> 717: "OTP-7101. Record update: more than one call to setelement/3."; 718: otp_7101(suite) -> []; 719: otp_7101(Config) when is_list(Config) -> 720: Rec = #otp_7101{}, 721: 722: %% Spawn a tracer process to count the number of setelement/3 calls. 723: %% The tracer will forward all trace messages to us. 724: Self = self(), 725: Tracer = spawn_link(fun() -> otp_7101_tracer(Self, 0) end), 726: ?line 1 = erlang:trace_pattern({erlang,setelement,3}, true), 727: ?line erlang:trace(self(), true, [{tracer,Tracer},call]), 728: 729: %% Update the record. 730: ?line #otp_7101{a=2,b=1,c=[],d=[],e=[]} = otp_7101_update1(Rec), 731: ?line #otp_7101{a=1,b=2,c=[],d=[],e=[]} = otp_7101_update2(Rec), 732: ?line #otp_7101{a=2,b=1,c=[],d=[],e=[]} = otp_7101_update3(Rec), 733: ?line #otp_7101{a=1,b=2,c=[],d=[],e=[]} = otp_7101_update4(Rec), 734: 735: %% Verify that setelement/3 was called the same number of times as 736: %% the number of record updates. 737: ?line Ref = erlang:trace_delivered(Self), 738: receive 739: {trace_delivered, Self, Ref} -> 740: Tracer ! done 741: end, 742: ?line 1 = erlang:trace_pattern({erlang,setelement,3}, false), 743: receive 744: 4 -> 745: ok; 746: Other -> 747: ?line ?t:fail({unexpected,Other}) 748: end. 749: 750: otp_7101_tracer(Parent, N) -> 751: receive 752: {trace,Parent,call,{erlang,setelement,[_,_,_]}} -> 753: otp_7101_tracer(Parent, N+1); 754: done -> 755: Parent ! N 756: end. 757: 758: otp_7101_update1(R) -> 759: R#otp_7101{b=1, 760: a=2}. 761: 762: otp_7101_update2(R) -> 763: R#otp_7101{a=1, 764: b=2}. 765: 766: otp_7101_update3(R) -> 767: R#otp_7101{b=1,a=2}. 768: 769: otp_7101_update4(R) -> 770: R#otp_7101{a=1,b=2}. 771: 772: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 773: 774: run(Config, Tests) -> 775: run(Config, Tests, []). 776: 777: run(Config, Tests, Opts) -> 778: F = fun(P) -> 779: {SourceFile, Mod} = compile_file_mod(Config), 780: _ = compile_file(Config, P, Opts), 781: AbsFile = filename:rootname(SourceFile, ".erl"), 782: code:purge(Mod), 783: code:load_abs(AbsFile, Mod), 784: %io:format("run~n"), 785: case catch Mod:t() of 786: {'EXIT', _Reason} = Error -> 787: ?t:format("failed, got ~p~n", [Error]), 788: fail(); 789: ok -> 790: ok 791: end 792: end, 793: lists:foreach(F, Tests). 794: 795: %% Compiles a test module and returns the list of errors and warnings. 796: 797: compile_file(Config, Test0, Opts0) -> 798: {File, _Mod} = compile_file_mod(Config), 799: Filename = 'exprec_test.erl', 800: Test = list_to_binary(["-module(exprec_test). " 801: "-compile(export_all). ", 802: Test0]), 803: File = filename(Filename, Config), 804: Opts = [export_all,return,{outdir,?privdir}|Opts0], 805: ok = file:write_file(File, Test), 806: {ok, _M, Ws} = compile:file(File, Opts), 807: warnings(File, Ws). 808: 809: compile_file_mod(Config) -> 810: {filename('exprec_test.erl', Config), exprec_test}. 811: 812: filename(Name, Config) when is_atom(Name) -> 813: filename(atom_to_list(Name), Config); 814: filename(Name, Config) -> 815: filename:join(?privdir, Name). 816: 817: warnings(File, Ws) -> 818: case lists:append([W || {F, W} <- Ws, F =:= File]) of 819: [] -> []; 820: L -> {warnings, L} 821: end. 822: 823: fail() -> 824: io:format("failed~n"), 825: ?t:fail().