1: %% -*- coding: utf-8 -*- 2: %% 3: %% %CopyrightBegin% 4: %% 5: %% Copyright Ericsson AB 2004-2013. All Rights Reserved. 6: %% 7: %% The contents of this file are subject to the Erlang Public License, 8: %% Version 1.1, (the "License"); you may not use this file except in 9: %% compliance with the License. You should have received a copy of the 10: %% Erlang Public License along with this software. If not, it can be 11: %% retrieved online at http://www.erlang.org/. 12: %% 13: %% Software distributed under the License is distributed on an "AS IS" 14: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 15: %% the License for the specific language governing rights and limitations 16: %% under the License. 17: %% 18: %% %CopyrightEnd% 19: %% 20: -module(shell_SUITE). 21: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 22: init_per_group/2,end_per_group/2]). 23: 24: -export([forget/1, records/1, known_bugs/1, otp_5226/1, otp_5327/1, 25: otp_5435/1, otp_5195/1, otp_5915/1, otp_5916/1, 26: bs_match_misc_SUITE/1, bs_match_int_SUITE/1, 27: bs_match_tail_SUITE/1, bs_match_bin_SUITE/1, 28: bs_construct_SUITE/1, 29: refman_bit_syntax/1, 30: progex_bit_syntax/1, progex_records/1, 31: progex_lc/1, progex_funs/1, 32: otp_5990/1, otp_6166/1, otp_6554/1, 33: otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]). 34: 35: -export([ start_restricted_from_shell/1, 36: start_restricted_on_command_line/1,restricted_local/1]). 37: 38: %% Internal export. 39: -export([otp_5435_2/0, prompt1/1, prompt2/1, prompt3/1, prompt4/1, 40: prompt5/1]). 41: 42: %% 43: %% Define to run outside of test server 44: %% 45: %% -define(STANDALONE,1). 46: 47: -ifdef(STANDALONE). 48: -define(config(A,B),config(A,B)). 49: -define(t,test_server). 50: -export([config/2]). 51: -define(line, noop, ). 52: config(priv_dir,_) -> 53: ".". 54: -else. 55: -include_lib("test_server/include/test_server.hrl"). 56: -export([init_per_testcase/2, end_per_testcase/2]). 57: % Default timetrap timeout (set in init_per_testcase). 58: -define(default_timeout, ?t:minutes(2)). 59: init_per_testcase(_Case, Config) -> 60: ?line Dog = ?t:timetrap(?default_timeout), 61: ?line OrigPath = code:get_path(), 62: ?line code:add_patha(?config(priv_dir,Config)), 63: [{orig_path,OrigPath}, {watchdog, Dog} | Config]. 64: 65: end_per_testcase(_Case, Config) -> 66: ?line Dog = ?config(watchdog, Config), 67: ?line test_server:timetrap_cancel(Dog), 68: ?line OrigPath = ?config(orig_path,Config), 69: ?line code:set_path(OrigPath), 70: ?line application:unset_env(stdlib, restricted_shell), 71: ?line (catch code:purge(user_default)), 72: ?line (catch code:delete(user_default)), 73: ok. 74: -endif. 75: 76: suite() -> [{ct_hooks,[ts_install_cth]}]. 77: 78: all() -> 79: [forget, records, known_bugs, otp_5226, otp_5327, 80: otp_5435, otp_5195, otp_5915, otp_5916, {group, bits}, 81: {group, refman}, {group, progex}, {group, tickets}, 82: {group, restricted}]. 83: 84: groups() -> 85: [{restricted, [], 86: [start_restricted_from_shell, 87: start_restricted_on_command_line, restricted_local]}, 88: {bits, [], 89: [bs_match_misc_SUITE, bs_match_tail_SUITE, 90: bs_match_bin_SUITE, bs_construct_SUITE]}, 91: {refman, [], [refman_bit_syntax]}, 92: {progex, [], 93: [progex_bit_syntax, progex_records, progex_lc, 94: progex_funs]}, 95: {tickets, [], 96: [otp_5990, otp_6166, otp_6554, otp_7184, 97: otp_7232, otp_8393, otp_10302]}]. 98: 99: init_per_suite(Config) -> 100: Config. 101: 102: end_per_suite(_Config) -> 103: ok. 104: 105: init_per_group(_GroupName, Config) -> 106: Config. 107: 108: end_per_group(_GroupName, Config) -> 109: Config. 110: 111: 112: -record(state, {bin, reply, leader, unic = latin1}). 113: 114: 115: start_restricted_from_shell(doc) -> 116: ["Test that a restricted shell can be started from the normal shell"]; 117: start_restricted_from_shell(suite) -> 118: []; 119: start_restricted_from_shell(Config) when is_list(Config) -> 120: ?line [{error,nofile}] = scan(<<"begin shell:start_restricted(" 121: "nonexisting_module) end.">>), 122: ?line Test = filename:join(?config(priv_dir, Config), 123: "test_restricted.erl"), 124: Contents = <<"-module(test_restricted). 125: -export([local_allowed/3, non_local_allowed/3]). 126: local_allowed(m,[],State) -> 127: {true,State}; 128: local_allowed(ugly,[],_State) -> 129: non_conforming_reply; 130: local_allowed(_,_,State) -> 131: {false,State}. 132: 133: non_local_allowed({shell,stop_restricted},[],State) -> 134: {true,State}; 135: non_local_allowed({erlang,'+'},[_],State) -> 136: {true,State}; 137: non_local_allowed({erlang,'-'},[_,_],_State) -> 138: non_conforming_reply; 139: non_local_allowed({h, d}, [Arg], S) -> 140: {{redirect, {erlang,hd}, [Arg]}, S}; 141: non_local_allowed(_,_,State) -> 142: {false,State}. 143: ">>, 144: ?line ok = compile_file(Config, Test, Contents, []), 145: ?line "exception exit: restricted shell starts now" = 146: comm_err(<<"begin shell:start_restricted(" 147: "test_restricted) end.">>), 148: ?line {ok, test_restricted} = 149: application:get_env(stdlib, restricted_shell), 150: ?line "Module" ++ _ = t(<<"begin m() end.">>), 151: ?line "exception exit: restricted shell does not allow c(foo)" = 152: comm_err(<<"begin c(foo) end.">>), 153: ?line "exception exit: restricted shell does not allow init:stop()" = 154: comm_err(<<"begin init:stop() end.">>), 155: ?line "exception exit: restricted shell does not allow init:stop()" = 156: comm_err(<<"begin F = fun() -> init:stop() end, F() end.">>), 157: ?line "exception error: an error occurred when evaluating an arithmetic expression" = 158: comm_err(<<"begin +a end.">>), 159: ?line "exception exit: restricted shell does not allow a + b" = 160: comm_err(<<"begin a+b end.">>), 161: ?line "exception exit: restricted shell does not allow - b" = 162: comm_err(<<"begin -b end.">>), 163: ?line "exception exit: restricted shell does not allow 1 + 2" = 164: comm_err(<<"begin if atom(1 + 2> 0) -> 1; true -> 2 end end.">>), 165: ?line "exception exit: restricted shell does not allow 1 + 2" = 166: comm_err(<<"begin if is_atom(1 + 2> 0) -> 1; true -> 2 end end.">>), 167: ?line "exception exit: restricted shell does not allow - 2" = 168: comm_err(<<"begin if - 2 -> 1; true -> 2 end end.">>), 169: ?line "exception exit: restricted shell does not allow - 2" = 170: comm_err(<<"begin if (- 2 > 0) andalso true -> 1; true -> 2 end end.">>), 171: ?line "exception exit: restricted shell does not allow - 2" = 172: comm_err(<<"begin if (- 2 > 0) orelse true -> 1; true -> 2 end end.">>), 173: ?line "exception exit: restricted shell does not allow 1 + 2" = 174: comm_err(<<"begin if 1 + 2 > 0 -> 1; true -> 2 end end.">>), 175: ?line "exception exit: restricted shell does not allow 1 + 2" = 176: comm_err(<<"begin if erlang:is_atom(1 + 2> 0) -> 1; true -> 2 end end.">>), 177: ?line "exception exit: restricted shell does not allow is_integer(1)" = 178: comm_err(<<"begin if is_integer(1) -> 1; true -> 2 end end.">>), 179: ?line "exception exit: restricted shell does not allow is_integer(1)" = 180: comm_err(<<"begin if integer(1) -> 1; true -> 2 end end.">>), 181: ?line "exception exit: " 182: "restricted shell module returned bad value non_conforming_reply" = 183: comm_err(<<"ugly().">>), 184: ?line [one] = scan(<<"h:d([one,two]).">>), 185: ?line "exception exit: " 186: "restricted shell module returned bad value non_conforming_reply" = 187: comm_err(<<"1 - 2.">>), 188: ?line "exception exit: restricted shell stopped"= 189: comm_err(<<"begin shell:stop_restricted() end.">>), 190: ?line undefined = 191: application:get_env(stdlib, restricted_shell), 192: ok. 193: 194: start_restricted_on_command_line(doc) -> 195: ["Check restricted shell when started from the command line"]; 196: start_restricted_on_command_line(suite) -> 197: []; 198: start_restricted_on_command_line(Config) when is_list(Config) -> 199: ?line {ok,Node} = start_node(shell_suite_helper_1, 200: "-pa "++?config(priv_dir,Config)++ 201: " -stdlib restricted_shell foo"), 202: ?line "Warning! Restricted shell module foo not found: nofile"++_ = 203: t({Node, <<"begin m() end.">>}), 204: ?line "exception exit: restricted shell does not allow m()" = 205: comm_err({Node, <<"begin m() end.">>}), 206: ?line [ok] = 207: (catch scan({Node, <<"begin q() end.">>})), 208: ?line test_server:stop_node(Node), 209: ?line Test = filename:join(?config(priv_dir, Config), 210: "test_restricted2.erl"), 211: Contents = <<"-module(test_restricted2). 212: -export([local_allowed/3, non_local_allowed/3]). 213: local_allowed(m,[],State) -> 214: {true,State}; 215: local_allowed(_,_,State) -> 216: {false,State}. 217: 218: non_local_allowed({shell,stop_restricted},[],State) -> 219: {true,State}; 220: non_local_allowed({erlang,node},[],State) -> 221: {true,State}; 222: non_local_allowed(_,_,State) -> 223: {false,State}. 224: ">>, 225: ?line ok = compile_file(Config, Test, Contents, []), 226: ?line {ok,Node2} = start_node(shell_suite_helper_2, 227: "-pa "++?config(priv_dir,Config)++ 228: " -stdlib restricted_shell test_restricted2"), 229: ?line "Module" ++ _ = t({Node2,<<"begin m() end.">>}), 230: ?line "exception exit: restricted shell does not allow c(foo)" = 231: comm_err({Node2,<<"begin c(foo) end.">>}), 232: ?line "exception exit: restricted shell does not allow init:stop()" = 233: comm_err({Node2,<<"begin init:stop() end.">>}), 234: ?line "exception exit: restricted shell does not allow init:stop()" = 235: comm_err({Node2,<<"begin F = fun() -> init:stop() end, F() end.">>}), 236: ?line [Node2] = 237: scan({Node2, <<"begin erlang:node() end.">>}), 238: ?line [Node2] = 239: scan({Node2, <<"begin node() end.">>}), 240: ?line "exception exit: restricted shell stopped"= 241: comm_err({Node2,<<"begin shell:stop_restricted() end.">>}), 242: ?line [ok] = 243: scan({Node2, <<"begin q() end.">>}), 244: ?line test_server:stop_node(Node2), 245: ok. 246: 247: restricted_local(suite) -> 248: []; 249: restricted_local(doc) -> 250: ["Tests calling local shell functions with spectacular arguments in restricted shell"]; 251: restricted_local(Config) when is_list(Config) -> 252: ?line [{error,nofile}] = scan(<<"begin shell:start_restricted(" 253: "nonexisting_module) end.">>), 254: ?line Test = filename:join(?config(priv_dir, Config), 255: "test_restricted_local.erl"), 256: Contents = <<"-module(test_restricted_local). 257: -export([local_allowed/3, non_local_allowed/3]). 258: local_allowed(m,[],State) -> 259: {true,State}; 260: local_allowed(banan,_,State) -> 261: {true,State}; 262: local_allowed(funkis,_,State) -> 263: {true,State}; 264: local_allowed(c,_,State) -> 265: {true,State}; 266: local_allowed(_,_,State) -> 267: {false,State}. 268: 269: non_local_allowed({shell,stop_restricted},[],State) -> 270: {true,State}; 271: non_local_allowed(_,_,State) -> 272: {false,State}. 273: ">>, 274: ?line ok = compile_file(Config, Test, Contents, []), 275: ?line Test2 = filename:join(?config(priv_dir, Config), 276: "user_default.erl"), 277: Contents2 = <<"-module(user_default). 278: -export([funkis/1,apple/1]). 279: funkis(F) when is_function(F) -> 280: funkis; 281: funkis(_) -> 282: nofunkis. 283: apple(_) -> 284: apple. 285: ">>, 286: ?line ok = compile_file(Config, Test2, Contents2, []), 287: ?line "exception exit: restricted shell starts now" = 288: comm_err(<<"begin shell:start_restricted(" 289: "test_restricted_local) end.">>), 290: ?line {ok, test_restricted_local} = 291: application:get_env(stdlib, restricted_shell), 292: ?line "exception exit: restricted shell does not allow foo(" ++ _ = 293: comm_err(<<"begin F=fun() -> hello end, foo(F) end.">>), 294: ?line "exception error: undefined shell command banan/1" = 295: comm_err(<<"begin F=fun() -> hello end, banan(F) end.">>), 296: ?line "{error,"++_ = t(<<"begin F=fun() -> hello end, c(F) end.">>), 297: ?line "exception exit: restricted shell does not allow l(" ++ _ = 298: comm_err(<<"begin F=fun() -> hello end, l(F) end.">>), 299: ?line "exception error: variable 'F' is unbound" = 300: comm_err(<<"begin F=fun() -> hello end, f(F), F end.">>), 301: ?line [funkis] = 302: scan(<<"begin F=fun() -> hello end, funkis(F) end.">>), 303: ?line "exception exit: restricted shell does not allow apple(" ++ _ = 304: comm_err(<<"begin F=fun() -> hello end, apple(F) end.">>), 305: ?line "exception exit: restricted shell stopped"= 306: comm_err(<<"begin shell:stop_restricted() end.">>), 307: ?line undefined = 308: application:get_env(stdlib, restricted_shell), 309: ?line (catch code:purge(user_default)), 310: ?line true = (catch code:delete(user_default)), 311: ok. 312: 313: 314: forget(doc) -> 315: ["f/0 and f/1"]; 316: forget(suite) -> 317: []; 318: forget(Config) when is_list(Config) -> 319: %% f/0 320: ?line [ok] = scan(<<"begin f() end.">>), 321: ?line "1: variable 'A' is unbound" = 322: comm_err(<<"A = 3, f(), A.">>), 323: ?line [ok] = scan(<<"A = 3, A = f(), A.">>), 324: 325: %% f/1 326: ?line [ok] = scan(<<"begin f(A) end.">>), 327: ?line "1: variable 'A' is unbound" = 328: comm_err(<<"A = 3, f(A), A.">>), 329: ?line [ok] = scan(<<"A = 3, A = f(A), A.">>), 330: ?line "exception error: no function clause matching call to f/1" = 331: comm_err(<<"f(a).">>), 332: ok. 333: 334: records(doc) -> 335: ["Test of the record support. OTP-5063."]; 336: records(suite) -> 337: []; 338: records(Config) when is_list(Config) -> 339: %% rd/2 340: ?line [{attribute,_,record,{bar,_}},ok] = 341: scan(<<"rd(foo,{bar}), 342: rd(bar,{foo = (#foo{})#foo.bar}), 343: rl(bar).">>), 344: ?line "variable 'R' is unbound" = % used to work (before OTP-5878, R11B) 345: exit_string(<<"rd(foo,{bar}), 346: R = #foo{}, 347: rd(bar,{foo = R#foo.bar}).">>), 348: ?line "exception error: no function clause matching call to rd/2" = 349: comm_err(<<"rd({foo},{bar}).">>), 350: ?line "bad record declaration" = exit_string(<<"A = bar, rd(foo,A).">>), 351: ?line [foo] = scan(<<"begin rd(foo,{bar}) end.">>), 352: ?line "1: record foo undefined" = 353: comm_err(<<"begin rd(foo,{bar}), #foo{} end.">>), 354: ?line ['f o o'] = scan(<<"rd('f o o', {bar}).">>), 355: ?line [foo] = scan(<<"rd(foo,{bar}), rd(foo,{foo = #foo{}}).">>), 356: 357: %% rf/0,1 358: ?line [_, {attribute,_,record,{foo,_}},ok] = 359: scan(<<"rf('_'). rd(foo,{bar}),rl().">>), 360: ?line "1: record foo undefined" = 361: comm_err(<<"rd(foo,{bar}), #foo{}, rf(foo), #foo{}.">>), 362: ?line [ok,{foo,undefined}] = 363: scan(<<"rd(foo,{bar}), A = #foo{}, rf(foo). A.">>), 364: ?line [_] = scan(<<"begin rf() end.">>), 365: ?line [ok] = scan(<<"begin rf(foo) end.">>), 366: 367: %% rp/1 368: ?line "#foo{bar = undefined}.\nok.\n" = 369: t(<<"rd(foo,{bar}), rp(#foo{}).">>), 370: ?line [{foo,3,4,3},ok] = scan(<<"rd(foo,{a = 3, b}), rp({foo,3,4,3}).">>), 371: ?line "#foo{a = 12}.\nok.\n" = t(<<"rd(foo,{a = 3}), rp({foo,12}).">>), 372: ?line [{[{foo}],12},ok] = scan(<<"rd(foo,{a = 3}), rp({[{foo}],12}).">>), 373: 374: %% rr/1,2,3 375: MS = ?MODULE_STRING, 376: RR1 = "rr(" ++ MS ++ "). #state{}.", 377: ?line "[state]\n" 378: "#state{bin = undefined,reply = undefined,leader = undefined,\n" 379: " unic = latin1}.\n" = 380: t(RR1), 381: RR2 = "rr(" ++ MS ++ ",[state]). #state{}.", 382: ?line "[state]\n" 383: "#state{bin = undefined,reply = undefined,leader = undefined,\n" 384: " unic = latin1}.\n" = 385: t(RR2), 386: RR3 = "rr(" ++ MS ++ ",'_'). #state{}.", 387: ?line "[state]\n" 388: "#state{bin = undefined,reply = undefined,leader = undefined,\n" 389: " unic = latin1}.\n" = 390: t(RR3), 391: RR4 = "rr(" ++ MS ++ ", '_', {d,test1}).", 392: ?line [[state]] = scan(RR4), 393: 394: Test = filename:join(?config(priv_dir, Config), "test.erl"), 395: Contents = <<"-module(test). 396: -record(state, {bin, reply, leader}). 397: 398: -ifdef(test1). 399: -record(test1, {f}). 400: -endif. 401: 402: -ifdef(test2). 403: -record(test2, {g}). 404: -endif.">>, 405: ?line ok = file:write_file(Test, Contents), 406: 407: RR5 = "rr(\"" ++ Test ++ "\", '_', {d,test1}), rl([test1,test2]).", 408: ?line [{attribute,1,record,{test1,_}},ok] = scan(RR5), 409: RR6 = "rr(\"" ++ Test ++ "\", '_', {d,test2}), rl([test1,test2]).", 410: ?line [{attribute,1,record,{test2,_}},ok] = scan(RR6), 411: RR7 = "rr(\"" ++ Test ++ 412: "\", '_', [{d,test1},{d,test2,17}]), rl([test1,test2]).", 413: ?line [{attribute,1,record,{test1,_}},{attribute,1,record,{test2,_}}, 414: ok] = scan(RR7), 415: ?line PreReply = scan(<<"rr(prim_file).">>), % preloaded... 416: ?line true = is_list(PreReply), 417: ?line Dir = filename:join(?config(priv_dir, Config), "*.erl"), 418: ?line RR8 = "rp(rr(\"" ++ Dir ++ "\")).", 419: ?line [_,ok] = scan(RR8), 420: file:delete(Test), 421: 422: RR1000 = "begin rr(" ++ MS ++ ") end.", 423: ?line [_] = scan(RR1000), 424: RR1001 = "begin rr(" ++ MS ++ ", state) end.", 425: ?line [_] = scan(RR1001), 426: RR1002 = "begin rr(" ++ MS ++ ", state,{i,'.'}) end.", 427: ?line [_] = scan(RR1002), 428: 429: ?line [{error,nofile}] = scan(<<"rr(not_a_module).">>), 430: ?line [{error,invalid_filename}] = scan(<<"rr({foo}).">>), 431: ?line [[]] = scan(<<"rr(\"not_a_file\").">>), 432: 433: %% using records 434: ?line [2] = scan(<<"rd(foo,{bar}), record_info(size, foo).">>), 435: ?line [true] = scan(<<"rd(foo,{bar}), is_record(#foo{}, foo).">>), 436: ?line [true] = scan(<<"rd(foo,{bar}), erlang:is_record(#foo{}, foo).">>), 437: ?line [true] = scan(<<"rd(foo,{bar}), 438: fun() when record(#foo{},foo) -> true end().">>), 439: ?line [2] = scan(<<"rd(foo,{bar}), #foo.bar.">>), 440: ?line "#foo{bar = 17}.\n" = 441: t(<<"rd(foo,{bar}), A = #foo{}, A#foo{bar = 17}.">>), 442: 443: %% test of is_record/2 in lc 444: ?line "[#foo{bar = 3}].\n" = 445: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 446: "is_record(X, foo)].">>), 447: ?line "[x,[],{a,b}].\n" = 448: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 449: "not is_record(X, foo)].">>), 450: ?line "[#foo{bar = 3}].\n" = 451: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 452: "begin is_record(X, foo) end].">>), 453: ?line "[x,[],{a,b}].\n" = 454: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 455: "begin not is_record(X, foo) end].">>), 456: 457: ?line "[#foo{bar = 3},x,[],{a,b}].\n" = 458: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 459: "is_record(X, foo) or not is_binary(X)].">>), 460: ?line "[#foo{bar = 3},x,[],{a,b}].\n" = 461: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 462: "not is_record(X, foo) or not is_binary(X)].">>), 463: ?line "[#foo{bar = 3}].\n" = 464: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 465: "is_record(X, foo) or is_reference(X)].">>), 466: ?line "[x,[],{a,b}].\n" = 467: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 468: "not is_record(X, foo) or is_reference(X)].">>), 469: 470: ?line "[#foo{bar = 3},x,[],{a,b}].\n" = 471: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 472: "begin is_record(X, foo) or not is_binary(X) end].">>), 473: ?line "[#foo{bar = 3},x,[],{a,b}].\n" = 474: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 475: "begin not is_record(X, foo) or not is_binary(X) end].">>), 476: ?line "[#foo{bar = 3}].\n" = 477: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 478: "begin is_record(X, foo) or is_reference(X) end].">>), 479: ?line "[x,[],{a,b}].\n" = 480: t(<<"rd(foo,{bar}), [X || X <- [#foo{bar=3},x,[],{a,b}]," 481: "begin not is_record(X, foo) or is_reference(X) end].">>), 482: 483: ?line [ok] = 484: scan(<<"rd(a,{}), is_record({a},a) andalso true, b().">>), 485: 486: %% nested record defs 487: ?line "#b{a = #a{}}.\n" = t(<<"rd(a,{}), rd(b, {a = #a{}}), #b{}.">>), 488: 489: ?line [ok,ok,ok] = scan(<<"rf('_'), rp(rp(rl(rf(rf(rf(rl())))))).">>), 490: 491: ok. 492: 493: known_bugs(doc) -> 494: ["Known bugs."]; 495: known_bugs(suite) -> 496: []; 497: known_bugs(Config) when is_list(Config) -> 498: %% erl_eval:merge_bindings/2 cannot handle _removal_ of bindings. 499: ?line [3] = scan(<<"A = 3, length(begin f(A), [3] end), A.">>), 500: ok. 501: 502: otp_5226(doc) -> 503: ["OTP-5226. Wildcards accepted when reading BEAM files using rr/1,2,3."]; 504: otp_5226(suite) -> 505: []; 506: otp_5226(Config) when is_list(Config) -> 507: Test1 = <<"-module(test1). 508: -record('_test1', {a,b}).">>, 509: Test2 = <<"-module(test2). 510: -record('_test2', {c,d}).">>, 511: ?line File1 = filename("test1.erl", Config), 512: ?line File2 = filename("test2.erl", Config), 513: ?line Beam = filename("*.beam", Config), 514: ?line ok = compile_file(Config, File1, Test1, [no_debug_info]), 515: ?line ok = compile_file(Config, File2, Test2, [no_debug_info]), 516: RR = "rr(\"" ++ Beam ++ "\").", 517: ?line [Recs] = scan(RR), 518: ?line true = lists:member('_test1', Recs), 519: ?line true = lists:member('_test2', Recs), 520: file:delete(filename("test1.beam", Config)), 521: file:delete(filename("test2.beam", Config)), 522: file:delete(File1), 523: file:delete(File2), 524: ok. 525: 526: otp_5327(doc) -> 527: ["OTP-5226. Test of eval_bits, mostly."]; 528: otp_5327(suite) -> 529: []; 530: otp_5327(Config) when is_list(Config) -> 531: ?line "exception error: bad argument" = 532: comm_err(<<"<<\"hej\":default>>.">>), 533: ?line <<"abc">> = 534: erl_parse:normalise({bin,1,[{bin_element,1,{string,1,"abc"}, 535: default,default}]}), 536: ?line [<<"abc">>] = scan(<<"<<(<<\"abc\">>):3/binary>>.">>), 537: ?line [<<"abc">>] = scan(<<"<<(<<\"abc\">>)/binary>>.">>), 538: ?line "exception error: bad argument" = 539: comm_err(<<"<<(<<\"abc\">>):4/binary>>.">>), 540: ?line true = byte_size(hd(scan("<<3.14:64/float>>."))) =:= 8, 541: ?line true = byte_size(hd(scan("<<3.14:32/float>>."))) =:= 4, 542: ?line "exception error: bad argument" = 543: comm_err(<<"<<3.14:128/float>>.">>), 544: ?line "exception error: bad argument" = 545: comm_err(<<"<<10:default>>.">>), 546: ?line [<<98,1:1>>] = scan(<<"<<3:3,5:6>>.">>), 547: ?line {'EXIT',{badarg,_}} = 548: (catch erl_parse:normalise({bin,1,[{bin_element,1,{integer,1,17}, 549: {atom,1,all}, 550: default}]})), 551: ?line [<<-20/signed>>] = scan(<<"<<-20/signed>> = <<-20>>.">>), 552: ?line [<<-300:16/signed>>] = 553: scan(<<"<<-300:16/signed>> = <<-300:16>>.">>), 554: ?line [<<-1000:24/signed>>] = 555: scan(<<"<<-1000:24/signed>> = <<-1000:24>>.">>), 556: ?line [<<-(1 bsl 29):32/signed>>] = 557: scan(<<"<<-(1 bsl 29):32/signed>> = <<-(1 bsl 29):32>>.">>), 558: 559: ?line "exception error: no match of right hand side value <<0,0,0>>" = 560: comm_err(<<"<<B:3/unit:7-binary,_/binary>> = <<0:24>>.">>), 561: ?line true = [<<103133:64/float>>] =:= 562: scan(<<"<<103133:64/float>> = <<103133:64/float>>.">>), 563: ?line true = [<<103133.0:64/float>>] =:= 564: scan(<<"<<103133.0:64/float>> = <<103133:64/float>>.">>), 565: ?line true = [<<103133:64/float>>] =:= scan(<<"<<103133:64/float>>.">>), 566: Int = 17, 567: ?line true = [<<Int:64/float>>] =:= scan(<<"Int = 17, <<Int:64/float>>.">>), 568: ?line "exception error: no match of right hand side value" ++ _ = 569: comm_err(<<"<<103133:64/binary>> = <<103133:64/float>>.">>), 570: ?line "exception error: interpreted function with arity 1 called with two arguments" = 571: comm_err(<<"(fun(X) -> X end)(a,b).">>), 572: ?line {'EXIT', {{illegal_pattern,_}, _}} = 573: (catch evaluate("<<A:a>> = <<17:32>>.", [])), 574: C = <<" 575: <<A:4,B:4,C:4,D:4,E:4,F:4>> = <<\"hej\">>, 576: case <<7:4,A:4,B:4,C:4,D:4,E:4,F:4,3:4>> of 577: <<_:4,\"hej\",3:4>> -> 1; 578: _ -> 2 579: end. 580: ">>, 581: ?line 1 = evaluate(C, []), 582: %% unbound_var would be nicer... 583: ?line {'EXIT',{{illegal_pattern,_},_}} = 584: (catch evaluate(<<"<<A:B>> = <<17:32>>.">>, [])), 585: %% undefined_bittype is turned into badmatch: 586: ?line {'EXIT',{{badmatch,<<17:32>>},_}} = 587: (catch evaluate(<<"<<A/apa>> = <<17:32>>.">>, [])), 588: ?line {'EXIT',_} = 589: (catch evaluate(<<"<<17/binary-unit:8-unit:16>>.">>, [])), 590: ?line {'EXIT',_} = 591: (catch evaluate(<<"<<17:32/unsigned-signed>> = <<17:32>>.">>, [])), 592: ?line {'EXIT',_} = 593: (catch evaluate(<<"<<17:32/unsigned-signed>>.">>, [])), 594: ?line <<17:32>> = evaluate(<<"<<17:32/signed-signed>>.">>, []), 595: ?line {'EXIT',_} = 596: (catch evaluate(<<"<<32/unit:8>>.">>, [])), 597: ok. 598: 599: otp_5435(doc) -> 600: ["OTP-5435. sys_pre_expand not in the path."]; 601: otp_5435(suite) -> 602: []; 603: otp_5435(Config) when is_list(Config) -> 604: ?line true = <<103133:64/float>> =:= 605: evaluate(<<"<<103133:64/float>> = <<103133:64/float>>.">>, []), 606: ?line true = <<103133.0:64/float>> =:= 607: evaluate(<<"<<103133.0:64/float>> = <<103133:64/float>>.">>, []), 608: ?line true = is_alive(), 609: ?line {ok, Node} = start_node(shell_SUITE_otp_5435), 610: ?line ok = rpc:call(Node, ?MODULE, otp_5435_2, []), 611: ?line ?t:stop_node(Node), 612: ok. 613: 614: start_node(Name) -> 615: ?line PA = filename:dirname(code:which(?MODULE)), 616: ?t:start_node(Name, slave, [{args, "-pa " ++ PA}]). 617: 618: otp_5435_2() -> 619: ?line true = code:del_path(compiler), 620: %% sys_pre_expand can no longer be found 621: %% OTP-5876. But erl_expand_records can! 622: ?line [{attribute,_,record,{bar,_}},ok] = 623: scan(<<"rd(foo,{bar}), 624: rd(bar,{foo = (#foo{})#foo.bar}), 625: rl(bar).">>), 626: ok. 627: 628: otp_5195(doc) -> 629: ["OTP-5195. QLC, mostly."]; 630: otp_5195(suite) -> 631: []; 632: otp_5195(Config) when is_list(Config) -> 633: %% QLC. It was easier to put these cases here than in qlc_SUITE. 634: ?line "[#a{b = undefined}].\n" = 635: t(<<"rd(a,{b}), qlc:e(qlc:q([X || X <- [#a{}],is_record(X, a)])).">>), 636: 637: %% An experimental shell used to translate error tuples: 638: %% "(qlc) \"1: generated variable 'X' must not be used in " 639: %% "list expression\".\n" = 640: %% t(<<"qlc:q([X || X <- [{a}], Y <- [X]]).">>), 641: %% Same as last one (if the shell does not translate error tuples): 642: ?line [{error,qlc,{1,qlc,{used_generator_variable,'X'}}}] = 643: scan(<<"qlc:q([X || X <- [{a}], Y <- [X]]).">>), 644: ?line {error,qlc,{1,qlc,{used_generator_variable,'X'}}} = 645: evaluate(<<"qlc:q([X || X <- [{a}], Y <- [X]]).">>, []), 646: Ugly = <<"qlc:e(qlc:q([X || X <- qlc:append([[1,2,3],ugly()])])).">>, 647: ?line "undefined shell command ugly/0" = error_string(Ugly), 648: ?line {'EXIT',{undef,_}} = (catch evaluate(Ugly, [])), 649: 650: V_1 = <<"qlc:e(qlc:q([X || X <- qlc:append([[1,2,3],v(-1)])])).">>, 651: ?line "- 1: command not found" = comm_err(V_1), 652: ?line {'EXIT', {undef,_}} = (catch evaluate(V_1, [])), 653: 654: ?line "1\n2\n3\n3.\n" = 655: t(<<"1. 2. 3. 3 = fun(A) when A =:= 2 -> v(3) end(v(2)).">>), 656: 657: ?line List4 = t(<<"[a,list]. A = [1,2]. " 658: "qlc:q([X || X <- qlc:append(A, v(1))]). " 659: "[1,2,a,list] = qlc:e(v(-1)).">>), 660: ?line "[1,2,a,list].\n" = string:substr(List4, string:len(List4)-13), 661: 662: ok. 663: 664: otp_5915(doc) -> 665: ["OTP-5915. Strict record tests in guards."]; 666: otp_5915(suite) -> 667: []; 668: otp_5915(Config) when is_list(Config) -> 669: C = <<" 670: rd(r, {a = 4,b}), 671: rd(r1, {a,b}), 672: rd(r2, {a = #r1{},b,c=length([1,2,3])}), 673: rd(r3, {a = fun(_) -> #r1{} end(1), b}), 674: 675: foo = fun(A) when A#r1.a > A#r1.b -> foo end(#r1{b = 2}), 676: 0 = fun(A) when A#r2.a -> 0 end(#r2{a = true}), 677: 1 = fun(A) when (#r1{a = A})#r1.a > 2 -> 1 end(3), 678: 2 = fun(N) when ((#r2{a = #r{a = 4}, b = length([a,b,c])})#r2.a)#r.a > N -> 679: 2 end(2), 680: 3 = fun(A) when (A#r2.a)#r1.a =:= 3 -> 3 end(#r2{a = #r1{a = 3}}), 681: ok = fun() -> 682: F = fun(A) when record(A#r.a, r1) -> 4; 683: (A) when record(A#r1.a, r1) -> 5 684: end, 685: 5 = F(#r1{a = #r1{}}), 686: 4 = F(#r{a = #r1{}}), 687: ok 688: end(), 689: 3 = fun(A) when record(A#r1.a, r), 690: (A#r1.a)#r.a > 3 -> 3 691: end(#r1{a = #r{a = 4}}), 692: 7 = fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}), 693: [#r1{a = 2,b = 1}] = 694: fun() -> 695: [A || A <- [#r1{a = 1, b = 3}, 696: #r2{a = 2,b = 1}, 697: #r1{a = 2, b = 1}], 698: A#r1.a > 699: A#r1.b] 700: end(), 701: {[_],b} = 702: fun(L) -> 703: %% A is checked only once: 704: R1 = [{A,B} || A <- L, A#r1.a, B <- L, A#r1.b], 705: A = #r2{a = true}, 706: %% A is checked again: 707: B = if A#r1.a -> a; true -> b end, 708: {R1,B} 709: end([#r1{a = true, b = true}]), 710: 711: p = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; 712: (_) -> p 713: end(#r1{a = 2}), 714: 715: o = fun(A) when (A#r1.a =:= 2) orelse (A#r2.a =:= 1) -> o; 716: (_) -> p 717: end(#r1{a = 2}), 718: 719: 3 = fun(A) when A#r1.a > 3, 720: record(A, r1) -> 3 721: end(#r1{a = 5}), 722: 723: ok = fun() -> 724: F = fun(A) when (A#r2.a =:= 1) orelse (A#r2.a) -> 2; 725: (A) when (A#r1.a =:= 1) orelse (A#r1.a) -> 1; 726: (A) when (A#r2.a =:= 2) andalso (A#r2.b) -> 3 727: end, 728: 1 = F(#r1{a = 1}), 729: 2 = F(#r2{a = true}), 730: 3 = F(#r2{a = 2, b = true}), 731: ok 732: end(), 733: 734: b = fun(A) when false or not (A#r.a =:= 1) -> a; 735: (_) -> b 736: end(#r1{a = 1}), 737: b = fun(A) when not (A#r.a =:= 1) or false -> a; 738: (_) -> b 739: end(#r1{a = 1}), 740: 741: ok = fun() -> 742: F = fun(A) when not (A#r.a =:= 1) -> yes; 743: (_) -> no 744: end, 745: no = F(#r1{a = 2}), 746: yes = F(#r{a = 2}), 747: no = F(#r{a = 1}), 748: ok 749: end(), 750: 751: a = fun(A) when record(A, r), 752: A#r.a =:= 1, 753: A#r.b =:= 2 ->a 754: end(#r{a = 1, b = 2}), 755: a = fun(A) when erlang:is_record(A, r), 756: A#r.a =:= 1, 757: A#r.b =:= 2 -> a 758: end(#r{a = 1, b = 2}), 759: a = fun(A) when is_record(A, r), 760: A#r.a =:= 1, 761: A#r.b =:= 2 -> a 762: end(#r{a = 1, b = 2}), 763: 764: nop = fun(A) when (is_record(A, r1) and (A#r1.a > 3)) or (A#r2.a < 1) -> 765: japp; 766: (_) -> 767: nop 768: end(#r2{a = 0}), 769: nop = fun(A) when (A#r1.a > 3) or (A#r2.a < 1) -> japp; 770: (_) -> 771: nop 772: end(#r2{a = 0}), 773: 774: ok = fun() -> 775: F = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; 776: (_) -> p 777: end, 778: p = F(#r2{a = 1}), 779: p = F(#r1{a = 2}), 780: ok 781: end(), 782: 783: ok = fun() -> 784: F = fun(A) when fail, A#r1.a; A#r1.a -> ab; 785: (_) -> bu 786: end, 787: ab = F(#r1{a = true}), 788: bu = F(#r2{a = true}), 789: ok 790: end(), 791: 792: both = fun(A) when A#r.a, A#r.b -> both 793: end(#r{a = true, b = true}), 794: 795: ok = fun() -> 796: F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) 797: or (B#r2.b) or (A#r1.b) -> true; 798: (_, _) -> false 799: end, 800: true = F(#r1{a = false, b = false}, #r2{a = false, b = true}), 801: false = F(#r1{a = true, b = true}, #r1{a = false, b = true}), 802: ok 803: end(), 804: 805: ok.">>, 806: [ok] = scan(C), 807: ok. 808: 809: otp_5916(doc) -> 810: ["OTP-5916. erlang:is_record/3 allowed in guards."]; 811: otp_5916(suite) -> 812: []; 813: otp_5916(Config) when is_list(Config) -> 814: C = <<" 815: rd(r1, {a,b}), 816: rd(r2, {a,b}), 817: 818: true = if erlang:is_record(#r1{},r1,3) -> true; true -> false end, 819: false = if erlang:is_record(#r2{},r1,3) -> true; true -> false end, 820: 821: true = if is_record(#r1{},r1,3) -> true; true -> false end, 822: false = if is_record(#r2{},r1,3) -> true; true -> false end, 823: 824: ok.">>, 825: [ok] = scan(C), 826: ok. 827: 828: 829: bs_match_misc_SUITE(doc) -> 830: ["OTP-5327. Adopted from parts of emulator/test/bs_match_misc_SUITE.erl."]; 831: bs_match_misc_SUITE(suite) -> 832: []; 833: bs_match_misc_SUITE(Config) when is_list(Config) -> 834: C = <<" 835: F1 = fun() -> 3.1415 end, 836: 837: FOne = fun() -> 1.0 end, 838: 839: Fcmp = fun(F1, F2) when (F1 - F2) / F2 < 0.0000001 -> ok end, 840: 841: MakeSubBin = fun(Bin0) -> 842: Sz = size(Bin0), 843: Bin1 = <<37,Bin0/binary,38,39>>, 844: <<_:8,Bin:Sz/binary,_:8,_:8>> = Bin1, 845: Bin 846: end, 847: 848: MatchFloat = 849: fun(Bin0, Fsz, I) -> 850: Bin = MakeSubBin(Bin0), 851: Bsz = size(Bin) * 8, 852: Tsz = Bsz - Fsz - I, 853: <<_:I,F:Fsz/float,_:Tsz>> = Bin, 854: F 855: end, 856: 857: TFloat = fun() -> 858: F = F1(), 859: G = FOne(), 860: 861: G = MatchFloat(<<63,128,0,0>>, 32, 0), 862: G = MatchFloat(<<63,240,0,0,0,0,0,0>>, 64, 0), 863: 864: Fcmp(F, MatchFloat(<<F:32/float>>, 32, 0)), 865: Fcmp(F, MatchFloat(<<F:64/float>>, 64, 0)), 866: Fcmp(F, MatchFloat(<<1:1,F:32/float,127:7>>, 32, 1)), 867: Fcmp(F, MatchFloat(<<1:1,F:64/float,127:7>>, 64, 1)), 868: Fcmp(F, MatchFloat(<<1:13,F:32/float,127:3>>, 32, 13)), 869: Fcmp(F, MatchFloat(<<1:13,F:64/float,127:3>>, 64, 13)) 870: end, 871: TFloat(), 872: 873: F2 = fun() -> 2.7133 end, 874: 875: MatchFloatLittle = fun(Bin0, Fsz, I) -> 876: Bin = MakeSubBin(Bin0), 877: Bsz = size(Bin) * 8, 878: Tsz = Bsz - Fsz - I, 879: <<_:I,F:Fsz/float-little,_:Tsz>> = Bin, 880: F 881: end, 882: 883: LittleFloat = fun() -> 884: F = F2(), 885: G = FOne(), 886: 887: G = MatchFloatLittle(<<0,0,0,0,0,0,240,63>>, 64, 0), 888: G = MatchFloatLittle(<<0,0,128,63>>, 32, 0), 889: 890: Fcmp(F, MatchFloatLittle(<<F:32/float-little>>, 32, 0)), 891: Fcmp(F, MatchFloatLittle(<<F:64/float-little>>, 64, 0)), 892: Fcmp(F, MatchFloatLittle(<<1:1,F:32/float-little,127:7>>, 32, 1)), 893: Fcmp(F, MatchFloatLittle(<<1:1,F:64/float-little,127:7>>, 64, 1)), 894: Fcmp(F, MatchFloatLittle(<<1:13,F:32/float-little,127:3>>, 32, 13)), 895: Fcmp(F, MatchFloatLittle(<<1:13,F:64/float-little,127:3>>, 64, 13)) 896: end, 897: LittleFloat(), 898: 899: Sean1 = fun(<<B/binary>>) when size(B) < 4 -> small; 900: (<<1, _B/binary>>) -> large 901: end, 902: 903: Sean = fun() -> 904: small = Sean1(<<>>), 905: small = Sean1(<<1>>), 906: small = Sean1(<<1,2>>), 907: small = Sean1(<<1,2,3>>), 908: large = Sean1(<<1,2,3,4>>), 909: 910: small = Sean1(<<4>>), 911: small = Sean1(<<4,5>>), 912: small = Sean1(<<4,5,6>>), 913: {'EXIT',{function_clause,_}} = (catch Sean1(<<4,5,6,7>>)) 914: end, 915: Sean(), 916: 917: NativeBig = fun() -> 918: <<37.33:64/native-float>> = <<37.33:64/big-float>>, 919: <<3974:16/native-integer>> = <<3974:16/big-integer>> 920: end, 921: 922: NativeLittle = fun() -> 923: <<37869.32343:64/native-float>> = <<37869.32343:64/little-float>>, 924: <<7974:16/native-integer>> = <<7974:16/little-integer>> 925: end, 926: 927: Native = fun() -> 928: <<3.14:64/native-float>> = <<3.14:64/native-float>>, 929: <<333:16/native>> = <<333:16/native>>, 930: <<38658345:32/native>> = <<38658345:32/native>>, 931: case <<1:16/native>> of 932: <<0,1>> -> NativeBig(); 933: <<1,0>> -> NativeLittle() 934: end 935: end, 936: Native(), 937: 938: Split = fun(<<N:16,B:N/binary,T/binary>>) -> {B,T} end, 939: 940: Split2 = fun(N, <<N:16,B:N/binary,T/binary>>) -> {B,T} end, 941: 942: Split_2 = fun(<<N0:8,N:N0,B:N/binary,T/binary>>) -> {B,T} end, 943: 944: Skip = fun(<<N:8,_:N/binary,T/binary>>) -> T end, 945: 946: SizeVar = fun() -> 947: {<<45>>,<<>>} = Split(<<1:16,45>>), 948: {<<45>>,<<46,47>>} = Split(<<1:16,45,46,47>>), 949: {<<45,46>>,<<47>>} = Split(<<2:16,45,46,47>>), 950: 951: {<<45,46,47>>,<<48>>} = Split_2(<<16:8,3:16,45,46,47,48>>), 952: 953: {<<45,46>>,<<47>>} = Split2(2, <<2:16,45,46,47>>), 954: {'EXIT',{function_clause,_}} = 955: (catch Split2(42, <<2:16,45,46,47>>)), 956: 957: <<\"cdef\">> = Skip(<<2:8,\"abcdef\">>) 958: end, 959: SizeVar(), 960: 961: Wcheck = fun(<<A>>) when A==3-> ok1; 962: (<<_,_:2/binary>>) -> ok2; 963: (<<_>>) -> ok3; 964: (Other) -> {error,Other} 965: end, 966: 967: Wiger = fun() -> 968: ok1 = Wcheck(<<3>>), 969: ok2 = Wcheck(<<1,2,3>>), 970: ok3 = Wcheck(<<4>>), 971: {error,<<1,2,3,4>>} = Wcheck(<<1,2,3,4>>), 972: {error,<<>>} = Wcheck(<<>>) 973: end, 974: Wiger(), 975: 976: ok. 977: ">>, 978: [ok] = scan(C), 979: ok = evaluate(C, []). 980: 981: %% This one is not run during night builds since it takes several minutes. 982: bs_match_int_SUITE(doc) -> 983: ["OTP-5327. Adopted from emulator/test/bs_match_int_SUITE.erl."]; 984: bs_match_int_SUITE(suite) -> 985: []; 986: bs_match_int_SUITE(Config) when is_list(Config) -> 987: C = <<" 988: FunClause = fun({'EXIT',{function_clause,_}}) -> ok end, 989: 990: Mkbin = fun(L) when list(L) -> list_to_binary(L) end, 991: 992: GetInt1 = fun(<<I:0>>) -> I; 993: (<<I:8>>) -> I; 994: (<<I:16>>) -> I; 995: (<<I:24>>) -> I; 996: (<<I:32>>) -> I 997: end, 998: 999: GetInt2 = fun(Bin0, I, F) when size(Bin0) < 4 -> 1000: Bin = <<0,Bin0/binary>>, 1001: I = GetInt1(Bin), 1002: F(Bin, I, F); 1003: (_, I, _F) -> I 1004: end, 1005: 1006: GetInt = fun(Bin) -> 1007: I = GetInt1(Bin), 1008: GetInt2(Bin, I, GetInt2) 1009: end, 1010: 1011: 1012: Cmp128 = fun(<<I:128>>, I) -> equal; 1013: (_, _) -> not_equal 1014: end, 1015: 1016: Uint2 = fun([H|T], Acc, F) -> F(T, Acc bsl 8 bor H, F); 1017: ([], Acc, _F) -> Acc 1018: end, 1019: 1020: Uint = fun(L) -> Uint2(L, 0, Uint2) end, 1021: 1022: Integer = fun() -> 1023: 0 = GetInt(Mkbin([])), 1024: 0 = GetInt(Mkbin([0])), 1025: 42 = GetInt(Mkbin([42])), 1026: 255 = GetInt(Mkbin([255])), 1027: 256 = GetInt(Mkbin([1,0])), 1028: 257 = GetInt(Mkbin([1,1])), 1029: 258 = GetInt(Mkbin([1,2])), 1030: 258 = GetInt(Mkbin([1,2])), 1031: 65534 = GetInt(Mkbin([255,254])), 1032: 16776455 = GetInt(Mkbin([255,253,7])), 1033: 4245492555 = GetInt(Mkbin([253,13,19,75])), 1034: 4294967294 = GetInt(Mkbin([255,255,255,254])), 1035: 4294967295 = GetInt(Mkbin([255,255,255,255])), 1036: Eight = [200,1,19,128,222,42,97,111], 1037: Cmp128(Eight, Uint(Eight)), 1038: FunClause(catch GetInt(Mkbin(lists:seq(1,5)))) 1039: end, 1040: Integer(), 1041: 1042: Sint = fun(Bin) -> 1043: case Bin of 1044: <<I:8/signed>> -> I; 1045: <<I:8/signed,_:3,_:5>> -> I; 1046: Other -> {no_match,Other} 1047: end 1048: end, 1049: 1050: SignedInteger = fun() -> 1051: {no_match,_} = Sint(Mkbin([])), 1052: {no_match,_} = Sint(Mkbin([1,2,3])), 1053: 127 = Sint(Mkbin([127])), 1054: -1 = Sint(Mkbin([255])), 1055: -128 = Sint(Mkbin([128])), 1056: 42 = Sint(Mkbin([42,255])), 1057: 127 = Sint(Mkbin([127,255])) 1058: end, 1059: SignedInteger(), 1060: 1061: Dynamic5 = fun(Bin, S1, S2, A, B) -> 1062: case Bin of 1063: <<A:S1,B:S2>> -> 1064: % io:format(\"~p ~p ~p ~p~n\", [S1,S2,A,B]), 1065: ok; 1066: _Other -> erlang:error(badmatch, [Bin,S1,S2,A,B]) 1067: end 1068: end, 1069: 1070: Dynamic2 = fun(Bin, S1, F) when S1 >= 0 -> 1071: S2 = size(Bin) * 8 - S1, 1072: Dynamic5(Bin, S1, S2, (1 bsl S1) - 1, (1 bsl S2) - 1), 1073: F(Bin, S1-1, F); 1074: (_, _, _) -> ok 1075: end, 1076: 1077: Dynamic = fun(Bin, S1) -> 1078: Dynamic2(Bin, S1, Dynamic2) 1079: end, 1080: 1081: Dynamic(Mkbin([255]), 8), 1082: Dynamic(Mkbin([255,255]), 16), 1083: Dynamic(Mkbin([255,255,255]), 24), 1084: Dynamic(Mkbin([255,255,255,255]), 32), 1085: 1086: BigToLittle4 = 1087: fun([B0,B1,B2,B3,B4,B5,B6,B7|T], N, Acc, F) when N >= 8 -> 1088: F(T, N-8, [B0,B1,B2,B3,B4,B5,B6,B7|Acc], F); 1089: (List, N, Acc, _F) -> lists:sublist(List, 1, N) ++ Acc 1090: end, 1091: 1092: BigToLittle = 1093: fun(List, N) -> BigToLittle4(List, N, [], BigToLittle4) end, 1094: 1095: ReversedSublist = 1096: fun(_List, 0, Acc, _F) -> Acc; 1097: ([H|T], N, Acc, F) -> F(T, N-1, [H|Acc], F) 1098: end, 1099: 1100: TwoComplementAndReverse = 1101: fun([H|T], Carry, Acc, F) -> 1102: Sum = 1-H+Carry, 1103: F(T, Sum div 2, [Sum rem 2|Acc], F); 1104: ([], Carry, Acc, _F) -> [Carry|Acc] 1105: end, 1106: 1107: MakeInt = fun(_List, 0, Acc, _F) -> Acc; 1108: ([H|T], N, Acc, F) -> F(T, N-1, Acc bsl 1 bor H, F) 1109: end, 1110: 1111: MakeSignedInt = 1112: fun(_List, 0) -> 0; 1113: ([0|_]=List, N) -> MakeInt(List, N, 0, MakeInt); 1114: ([1|_]=List0, N) -> 1115: List1 = ReversedSublist(List0, N, [], ReversedSublist), 1116: List2 = TwoComplementAndReverse(List1, 1, [], 1117: TwoComplementAndReverse), 1118: -MakeInt(List2, length(List2), 0, MakeInt) 1119: end, 1120: 1121: BitsToList = 1122: fun([H|T], 0, F) -> F(T, 16#80, F); 1123: ([H|_]=List, Mask, F) -> 1124: [case H band Mask of 1125: 0 -> 0; 1126: _ -> 1 1127: end | F(List, Mask bsr 1, F)]; 1128: ([], _, _F) -> [] 1129: end, 1130: 1131: MoreDynamic3 = 1132: fun(Action, Bin, List, Bef, Aft, F) when Bef =< Aft -> 1133: Action(Bin, List, Bef, Aft-Bef), 1134: F(Action, Bin, List, Bef, Aft-1, F); 1135: (_, _, _, _, _, _) -> ok 1136: end, 1137: 1138: MoreDynamic2 = 1139: fun(Action, Bin, [_|T]=List, Bef, F) -> 1140: MoreDynamic3(Action, Bin, List, Bef, size(Bin)*8, 1141: MoreDynamic3), 1142: F(Action, Bin, T, Bef+1, F); 1143: (_, _, [], _, _F) -> ok 1144: end, 1145: 1146: MoreDynamic1 = 1147: fun(Action, Bin) -> 1148: BitList = BitsToList(binary_to_list(Bin),16#80,BitsToList), 1149: MoreDynamic2(Action, Bin, BitList, 0, MoreDynamic2) 1150: end, 1151: 1152: MoreDynamic = fun() -> 1153: % Unsigned big-endian numbers. 1154: Unsigned = fun(Bin, List, SkipBef, N) -> 1155: SkipAft = 8*size(Bin) - N - SkipBef, 1156: <<_:SkipBef,Int:N,_:SkipAft>> = Bin, 1157: Int = MakeInt(List, N, 0, MakeInt) 1158: end, 1159: MoreDynamic1(Unsigned, erlang:md5(Mkbin([42]))), 1160: 1161: %% Signed big-endian numbers. 1162: Signed = fun(Bin, List, SkipBef, N) -> 1163: SkipAft = 8*size(Bin) - N - SkipBef, 1164: <<_:SkipBef,Int:N/signed,_:SkipAft>> = Bin, 1165: case MakeSignedInt(List, N) of 1166: Int -> ok; 1167: Other -> 1168: io:format(\"Bin = ~p,\", [Bin]), 1169: io:format(\"SkipBef = ~p, N = ~p\", 1170: [SkipBef,N]), 1171: io:format(\"Expected ~p, got ~p\", 1172: [Int,Other]) 1173: end 1174: end, 1175: MoreDynamic1(Signed, erlang:md5(Mkbin([43]))), 1176: 1177: %% Unsigned little-endian numbers. 1178: UnsLittle = fun(Bin, List, SkipBef, N) -> 1179: SkipAft = 8*size(Bin) - N - SkipBef, 1180: <<_:SkipBef,Int:N/little,_:SkipAft>> = Bin, 1181: Int = MakeInt(BigToLittle(List, N), N, 0, 1182: MakeInt) 1183: end, 1184: MoreDynamic1(UnsLittle, erlang:md5(Mkbin([44]))), 1185: 1186: %% Signed little-endian numbers. 1187: SignLittle = fun(Bin, List, SkipBef, N) -> 1188: SkipAft = 8*size(Bin) - N - SkipBef, 1189: <<_:SkipBef,Int:N/signed-little,_:SkipAft>> = Bin, 1190: Little = BigToLittle(List, N), 1191: Int = MakeSignedInt(Little, N) 1192: end, 1193: MoreDynamic1(SignLittle, erlang:md5(Mkbin([45]))) 1194: end, 1195: MoreDynamic(), 1196: 1197: ok. 1198: ">>, 1199: [ok] = scan(C), 1200: ok = evaluate(C, []). 1201: 1202: bs_match_tail_SUITE(doc) -> 1203: ["OTP-5327. Adopted from emulator/test/bs_match_tail_SUITE.erl."]; 1204: bs_match_tail_SUITE(suite) -> 1205: []; 1206: bs_match_tail_SUITE(Config) when is_list(Config) -> 1207: C = <<" 1208: GetTailUsed = fun(<<A:1,T/binary>>) -> {A,T} end, 1209: 1210: GetTailUnused = fun(<<A:15,_/binary>>) -> A end, 1211: 1212: GetDynTailUsed = fun(Bin, Sz) -> 1213: <<A:Sz,T/binary>> = Bin, 1214: {A,T} 1215: end, 1216: 1217: GetDynTailUnused = fun(Bin, Sz) -> 1218: <<A:Sz,_/binary>> = Bin, 1219: A 1220: end, 1221: 1222: Mkbin = fun(L) when list(L) -> list_to_binary(L) end, 1223: 1224: TestZeroTail = fun(<<A:8>>) -> A end, 1225: 1226: TestZeroTail2 = fun(<<_A:4,_B:4>>) -> ok end, 1227: 1228: ZeroTail = fun() -> 1229: 7 = (catch TestZeroTail(Mkbin([7]))), 1230: {'EXIT',{function_clause,_}} = 1231: (catch TestZeroTail(Mkbin([1,2]))), 1232: {'EXIT',{function_clause,_}} = 1233: (catch TestZeroTail2(Mkbin([1,2,3]))) 1234: end, 1235: ZeroTail(), 1236: 1237: AlGetTailUsed = fun(<<A:16,T/binary>>) -> {A,T} end, 1238: 1239: AlGetTailUnused = fun(<<A:16,_/binary>>) -> A end, 1240: 1241: Aligned = fun() -> 1242: Tail1 = Mkbin([]), 1243: {258,Tail1} = AlGetTailUsed(Mkbin([1,2])), 1244: Tail2 = Mkbin(lists:seq(1, 127)), 1245: {35091,Tail2} = AlGetTailUsed(Mkbin([137,19|Tail2])), 1246: 1247: 64896 = AlGetTailUnused(Mkbin([253,128])), 1248: 64895 = AlGetTailUnused(Mkbin([253,127|lists:seq(42, 255)])), 1249: 1250: Tail3 = Mkbin(lists:seq(0, 19)), 1251: {0,Tail1} = GetDynTailUsed(Tail1, 0), 1252: {0,Tail3} = GetDynTailUsed(Mkbin([Tail3]), 0), 1253: {73,Tail3} = GetDynTailUsed(Mkbin([73|Tail3]), 8), 1254: 1255: 0 = GetDynTailUnused(Mkbin([]), 0), 1256: 233 = GetDynTailUnused(Mkbin([233]), 8), 1257: 23 = GetDynTailUnused(Mkbin([23,22,2]), 8) 1258: end, 1259: Aligned(), 1260: 1261: UnAligned = fun() -> 1262: {'EXIT',{function_clause,_}} = 1263: (catch GetTailUsed(Mkbin([42]))), 1264: {'EXIT',{{badmatch,_},_}} = 1265: (catch GetDynTailUsed(Mkbin([137]), 3)), 1266: {'EXIT',{function_clause,_}} = 1267: (catch GetTailUnused(Mkbin([42,33]))), 1268: {'EXIT',{{badmatch,_},_}} = 1269: (catch GetDynTailUnused(Mkbin([44]), 7)) 1270: end, 1271: UnAligned(), 1272: ok. 1273: ">>, 1274: [ok] = scan(C), 1275: ok = evaluate(C, []). 1276: 1277: bs_match_bin_SUITE(doc) -> 1278: ["OTP-5327. Adopted from emulator/test/bs_match_bin_SUITE.erl."]; 1279: bs_match_bin_SUITE(suite) -> 1280: []; 1281: bs_match_bin_SUITE(Config) when is_list(Config) -> 1282: ByteSplitBinary = 1283: <<"ByteSplit = 1284: fun(L, B, Pos, Fun) when Pos >= 0 -> 1285: Sz1 = Pos, 1286: Sz2 = size(B) - Pos, 1287: <<B1:Sz1/binary,B2:Sz2/binary>> = B, 1288: B1 = list_to_binary(lists:sublist(L, 1, Pos)), 1289: B2 = list_to_binary(lists:nthtail(Pos, L)), 1290: Fun(L, B, Pos-1, Fun); 1291: (L, B, _, _Fun) -> ok 1292: end, 1293: Mkbin = fun(L) when list(L) -> list_to_binary(L) end, 1294: L = lists:seq(0, 57), 1295: B = Mkbin(L), 1296: ByteSplit(L, B, size(B), ByteSplit), 1297: Id = fun(I) -> I end, 1298: MakeUnalignedSubBinary = 1299: fun(Bin0) -> 1300: Bin1 = <<0:3,Bin0/binary,31:5>>, 1301: Sz = size(Bin0), 1302: <<0:3,Bin:Sz/binary,31:5>> = Id(Bin1), 1303: Bin 1304: end, 1305: Unaligned = MakeUnalignedSubBinary(B), 1306: ByteSplit(L, Unaligned, size(Unaligned), ByteSplit), 1307: ok. 1308: ">>, 1309: [ok] = scan(ByteSplitBinary), 1310: ok = evaluate(ByteSplitBinary, []), 1311: BitSplitBinary = 1312: <<"Mkbin = fun(L) when list(L) -> list_to_binary(L) end, 1313: 1314: MakeInt = 1315: fun(List, 0, Acc, _F) -> Acc; 1316: ([H|T], N, Acc, F) -> F(T, N-1, Acc bsl 1 bor H, F) 1317: end, 1318: 1319: MakeBinFromList = 1320: fun(List, 0, _F) -> Mkbin([]); 1321: (List, N, F) -> 1322: list_to_binary([MakeInt(List, 8, 0, MakeInt), 1323: F(lists:nthtail(8, List), N-8, F)]) 1324: end, 1325: 1326: BitSplitBinary3 = 1327: fun(Action, Bin, List, Bef, Aft, F) when Bef =< Aft -> 1328: Action(Bin, List, Bef, (Aft-Bef) div 8 * 8), 1329: F(Action, Bin, List, Bef, Aft-8, F); 1330: (_, _, _, _, _, _) -> ok 1331: end, 1332: 1333: BitSplitBinary2 = 1334: fun(Action, Bin, [_|T]=List, Bef, F) -> 1335: BitSplitBinary3(Action, Bin, List, Bef, size(Bin)*8, 1336: BitSplitBinary3), 1337: F(Action, Bin, T, Bef+1, F); 1338: (Action, Bin, [], Bef, F) -> ok 1339: end, 1340: 1341: BitsToList = 1342: fun([H|T], 0, F) -> F(T, 16#80, F); 1343: ([H|_]=List, Mask, F) -> 1344: [case H band Mask of 1345: 0 -> 0; 1346: _ -> 1 1347: end | F(List, Mask bsr 1, F)]; 1348: ([], _, _F) -> [] 1349: end, 1350: 1351: BitSplitBinary1 = 1352: fun(Action, Bin) -> 1353: BitList = BitsToList(binary_to_list(Bin), 16#80, 1354: BitsToList), 1355: BitSplitBinary2(Action, Bin, BitList, 0, BitSplitBinary2) 1356: end, 1357: 1358: Fun = fun(Bin, List, SkipBef, N) -> 1359: SkipAft = 8*size(Bin) - N - SkipBef, 1360: <<I1:SkipBef,OutBin:N/binary-unit:1,I2:SkipAft>> = Bin, 1361: OutBin = MakeBinFromList(List, N, MakeBinFromList) 1362: end, 1363: 1364: BitSplitBinary1(Fun, erlang:md5(<<1,2,3>>)), 1365: Id = fun(I) -> I end, 1366: MakeUnalignedSubBinary = 1367: fun(Bin0) -> 1368: Bin1 = <<0:3,Bin0/binary,31:5>>, 1369: Sz = size(Bin0), 1370: <<0:3,Bin:Sz/binary,31:5>> = Id(Bin1), 1371: Bin 1372: end, 1373: BitSplitBinary1(Fun, MakeUnalignedSubBinary(erlang:md5(<<1,2,3>>))), 1374: ok. 1375: ">>, 1376: [ok] = scan(BitSplitBinary), 1377: ok = evaluate(BitSplitBinary, []). 1378: 1379: -define(FAIL(Expr), "{'EXIT',{badarg,_}} = (catch " ??Expr ")"). 1380: 1381: -define(COF(Int0), 1382: "(fun(Int) -> 1383: true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, 1384: true = <<Int:64/float>> =:= <<(float(Int)):64/float>> 1385: end)(Nonliteral(" ??Int0 ")), 1386: true = <<" ??Int0 ":32/float>> =:= <<(float("??Int0")):32/float>>, 1387: true = <<" ??Int0 ":64/float>> =:= <<(float("??Int0")):64/float>>"). 1388: 1389: -define(COF64(Int0), 1390: "(fun(Int) -> 1391: true = <<Int:64/float>> =:= <<(float(Int)):64/float>> 1392: end)(Nonliteral(" ??Int0 ")), 1393: true = <<" ??Int0 ":64/float>> =:= <<(float("??Int0")):64/float>>"). 1394: 1395: bs_construct_SUITE(doc) -> 1396: ["OTP-5327. Adopted from parts of emulator/test/bs_construct_SUITE.erl."]; 1397: bs_construct_SUITE(suite) -> 1398: []; 1399: bs_construct_SUITE(Config) when is_list(Config) -> 1400: C1 = <<" 1401: 1402: Testf_1 = fun(W, B) -> " 1403: ?FAIL(<<42:W>>) "," 1404: ?FAIL(<<3.14:W/float>>) "," 1405: ?FAIL(<<B:W/binary>>) " 1406: end, 1407: 1408: TestF = fun() -> " 1409: ?FAIL(<<3.14>>) "," 1410: ?FAIL(<<<<1,2>>>>) "," 1411: 1412: ?FAIL(<<2.71/binary>>) "," 1413: ?FAIL(<<24334/binary>>) "," 1414: ?FAIL(<<24334344294788947129487129487219847/binary>>) "," 1415: 1416: ?FAIL(<<<<1,2,3>>/float>>) ", 1417: 1418: %% Negative field widths. 1419: Testf_1(-8, <<1,2,3,4,5>>)," 1420: 1421: ?FAIL(<<42:(-16)>>) "," 1422: ?FAIL(<<3.14:(-8)/float>>) "," 1423: ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>) "," 1424: ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>) "," 1425: ?FAIL(<<<<23,56,0,2>>:(anka)>>) " 1426: end, 1427: TestF(), 1428: 1429: NotUsed1 = fun(I, BinString) -> <<I:32,BinString/binary>>, ok end, 1430: 1431: NotUsed2 = fun(I, Sz) -> <<I:Sz>>, ok end, 1432: 1433: NotUsed3 = fun(I) -><<I:(-8)>>, ok end, 1434: 1435: NotUsed = fun() -> 1436: ok = NotUsed1(3, <<\"dum\">>), 1437: {'EXIT',{badarg,_}} = (catch NotUsed1(3, \"dum\")), " 1438: ?FAIL(NotUsed2(444, -2)) "," 1439: ?FAIL(NotUsed2(444, anka)) "," 1440: ?FAIL(NotUsed3(444)) " 1441: end, 1442: NotUsed(), 1443: 1444: InGuard3 = fun(Bin, A, B) when <<A:13,B:3>> == Bin -> 1; 1445: (Bin, A, B) when <<A:16,B/binary>> == Bin -> 2; 1446: (Bin, A, B) when <<A:14,B/float,3:2>> == Bin -> 3; 1447: (Bin, A, B) when {a,b,<<A:14,B/float,3:2>>} == Bin -> 1448: cant_happen; 1449: (_, _, _) -> nope 1450: end, 1451: 1452: InGuard = fun() -> 1453: 1 = InGuard3(<<16#74ad:16>>, 16#e95, 5), 1454: 2 = InGuard3(<<16#3A,16#F7,\"hello\">>, 16#3AF7, <<\"hello\">>), 1455: 3 = InGuard3(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), 1456: nope = InGuard3(<<1>>, 42, b), 1457: nope = InGuard3(<<1>>, a, b), 1458: nope = InGuard3(<<1,2>>, 1, 1), 1459: nope = InGuard3(<<4,5>>, 1, 2.71), 1460: nope = InGuard3(<<4,5>>, 1, <<12,13>>) 1461: end, 1462: InGuard(), 1463: 1464: Nonliteral = fun(X) -> X end, 1465: 1466: CoerceToFloat = fun() -> " 1467: ?COF(0) "," 1468: ?COF(-1) "," 1469: ?COF(1) "," 1470: ?COF(42) "," 1471: ?COF(255) "," 1472: ?COF(-255) "," 1473: ?COF64(298748888888888888888888888883478264866528467367364766666666666666663) "," 1474: ?COF64(-367546729879999999999947826486652846736736476555566666663) " 1475: end, 1476: CoerceToFloat(), 1477: ok. 1478: ">>, 1479: [ok] = scan(C1), 1480: ok = evaluate(C1, []), 1481: 1482: %% There is another one, lib/compiler/test/bs_construct_SUITE.erl... 1483: C2 = <<" 1484: I = fun(X) -> X end, 1485: 1486: Fail = fun() -> 1487: 1488: I_minus_777 = I(-777), 1489: I_minus_2047 = I(-2047), 1490: 1491: %% One negative field size, but the sum of field sizes will be 1 byte. 1492: %% Make sure that we reject that properly. 1493: 1494: {'EXIT',{badarg,_}} = (catch <<I_minus_777:2048/unit:8, 1495: 57:I_minus_2047/unit:8>>), 1496: 1497: %% Same thing, but use literals. 1498: {'EXIT',{badarg,_}} = (catch <<I_minus_777:2048/unit:8, 1499: 57:(-2047)/unit:8>>), 1500: 1501: %% Bad alignment. 1502: I_one = I(1), 1503: <<1:1>> = <<2375:I_one>>, 1504: <<3:2>> = <<45:1,2375:I_one>>, 1505: <<14:4>> = <<45:1,2375:I_one,918:2>>, 1506: <<118:7>> = <<45:1,2375:I_one,918:5>>, 1507: 1508: %% Not numbers. 1509: {'EXIT',{badarg,_}} = (catch <<45:(I(not_a_number))>>), 1510: {'EXIT',{badarg,_}} = (catch <<13:8,45:(I(not_a_number))>>), 1511: 1512: %% Unaligned sizes. 1513: BadSz = I(7), 1514: <<2:4>> = <<34:4>>, 1515: <<34:7>> = <<34:BadSz>>, 1516: 1517: [] = [X || {X} <- [], X == <<3:BadSz>>], 1518: [] = [X || {X} <- [], X == <<3:4>>] 1519: end, 1520: Fail(), 1521: 1522: FloatBin1 = fun(F) -> 1523: {<<1,2,3>>,F+3.0} 1524: end, 1525: 1526: FloatBin = fun() -> 1527: %% Some more coverage. 1528: {<<1,2,3>>,7.0} = FloatBin1(4) 1529: end, 1530: FloatBin(), 1531: 1532: ok. 1533: ">>, 1534: [ok] = scan(C2), 1535: ok = evaluate(C2, []). 1536: 1537: evaluate(B, Vars) when is_binary(B) -> 1538: evaluate(binary_to_list(B), Vars); 1539: evaluate(Str, Vars) -> 1540: {ok,Tokens,_} = 1541: erl_scan:string(Str), 1542: {ok, Exprs} = erl_parse:parse_exprs(Tokens), 1543: case erl_eval:exprs(Exprs, Vars, none) of 1544: {value, Result, _} -> 1545: Result 1546: end. 1547: 1548: 1549: refman_bit_syntax(doc) -> 1550: ["Bit syntax examples from the Reference Manual. OTP-5237."]; 1551: refman_bit_syntax(suite) -> 1552: []; 1553: refman_bit_syntax(Config) when is_list(Config) -> 1554: %% Reference Manual "Bit Syntax Expressions" 1555: ?line Bin1 = <<1,17,42>>, 1556: ?line true = [1,17,42] =:= binary_to_list(Bin1), 1557: ?line Bin2 = <<"abc">>, 1558: ?line true = "abc" =:= binary_to_list(Bin2), 1559: ?line Bin3 = <<1,17,42:16>>, 1560: ?line true = [1,17,0,42] =:= binary_to_list(Bin3), 1561: ?line <<_A,_B,C:16>> = <<1,17,42:16>>, 1562: ?line true = C =:= 42, 1563: ?line <<D:16,_E,F>> = <<1,17,42:16>>, 1564: ?line true = D =:= 273, 1565: ?line true = F =:= 42, 1566: <<_G,H/binary>> = <<1,17,42:16>>, 1567: ?line true = H =:= <<17,0,42>>, 1568: 1569: ?line [ok] = 1570: scan(<<"Bin1 = <<1,17,42>>, 1571: true = [1,17,42] =:= binary_to_list(Bin1), 1572: Bin2 = <<\"abc\">>, 1573: true = \"abc\" =:= binary_to_list(Bin2), 1574: Bin3 = <<1,17,42:16>>, 1575: true = 1576: [1,17,0,42] =:= binary_to_list(Bin3), 1577: <<A,B,C:16>> = <<1,17,42:16>>, 1578: true = C =:= 42, 1579: <<D:16,E,F>> = <<1,17,42:16>>, 1580: true = D =:= 273, 1581: true = F =:= 42, 1582: <<G,H/binary>> = <<1,17,42:16>>, 1583: true = H =:= <<17,0,42>>, 1584: ok.">>), 1585: 1586: %% Binary comprehensions. 1587: ?line <<2,4,6>> = << << (X*2) >> || <<X>> <= << 1,2,3 >> >>, 1588: ok. 1589: 1590: 1591: -define(IP_VERSION, 4). 1592: -define(IP_MIN_HDR_LEN, 5). 1593: progex_bit_syntax(doc) -> 1594: ["Bit syntax examples from Programming Examples. OTP-5237."]; 1595: progex_bit_syntax(suite) -> 1596: []; 1597: progex_bit_syntax(Config) when is_list(Config) -> 1598: Bin11 = <<1, 17, 42>>, 1599: true = [1, 17, 42] =:= binary_to_list(Bin11), 1600: Bin12 = <<"abc">>, 1601: true = [97, 98, 99] =:= binary_to_list(Bin12), 1602: 1603: A = 1, B = 17, C = 42, 1604: Bin2 = <<A, B, C:16>>, 1605: true = [1, 17, 00, 42] =:= binary_to_list(Bin2), 1606: <<D:16, E, F/binary>> = Bin2, 1607: true = D =:= 273, 1608: true = E =:= 00, 1609: true = [42] =:= binary_to_list(F), 1610: 1611: Fun4 = fun(Dgram) -> 1612: DgramSize = byte_size(Dgram), 1613: case Dgram of 1614: <<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16, 1615: ID:16, Flgs:3, FragOff:13, 1616: TTL:8, Proto:8, HdrChkSum:16, 1617: SrcIP:32, DestIP:32, 1618: RestDgram/binary>> when HLen>=5, 4*HLen=<DgramSize -> 1619: OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN), 1620: <<Opts:OptsLen/binary,Data/binary>> = RestDgram, 1621: {SrvcType, TotLen, Flgs, FragOff, ID, HdrChkSum, 1622: Proto, TTL, SrcIP, DestIP, Data, Opts}; 1623: _ -> 1624: not_ok 1625: end 1626: end, 1627: true = Fun4(<<>>) =:= not_ok, 1628: true = is_tuple(Fun4(list_to_binary([<<?IP_VERSION:4,5:4>>, 1629: list_to_binary(lists:seq(1,255))]))), 1630: 1631: X = 23432324, Y = 24324234, 1632: <<10:7>> = <<X:1, Y:6>>, 1633: Z = 234324324, 1634: XYZ = <<X:1, Y:6, Z:1>>, 1635: true = [20] =:= binary_to_list(XYZ), 1636: Hello1 = <<"hello">>, 1637: Hello2 = <<$h,$e,$l,$l,$o>>, 1638: true = "hello" =:= binary_to_list(Hello1), 1639: true = "hello" =:= binary_to_list(Hello2), 1640: 1641: FunM1 = fun(<<X1:7/binary, Y1:1/binary>>) -> {X1,Y1} end, 1642: true = {<<"1234567">>,<<"8">>} =:= FunM1(<<"12345678">>), 1643: 1644: FunM2 = fun(<<_X1:7/binary-unit:7, _Y1:1/binary-unit:1>>) -> ok; 1645: (_) -> not_ok end, 1646: true = not_ok =:= FunM2(<<"1">>), 1647: 1648: BL = [{3,4,5},{6,7,8}], 1649: Lst = [0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8], 1650: B1 = triples_to_bin1(BL), 1651: true = Lst =:= binary_to_list(B1), 1652: B2 = triples_to_bin2(BL), 1653: true = Lst =:= binary_to_list(B2), 1654: 1655: ?line [ok] = scan( 1656: <<"Bin11 = <<1, 17, 42>>, 1657: true = [1, 17, 42] =:= binary_to_list(Bin11), 1658: Bin12 = <<\"abc\">>, 1659: true = [97, 98, 99] =:= binary_to_list(Bin12), 1660: 1661: A = 1, B = 17, C = 42, 1662: Bin2 = <<A, B, C:16>>, 1663: true = [1, 17, 00, 42] =:= binary_to_list(Bin2), 1664: <<D:16, E, F/binary>> = Bin2, 1665: true = D =:= 273, 1666: true = E =:= 00, 1667: true = [42] =:= binary_to_list(F), 1668: 1669: Fun4 = fun(Dgram) -> 1670: DgramSize = byte_size(Dgram), 1671: case Dgram of 1672: <<4:4, HLen:4, SrvcType:8, TotLen:16, 1673: ID:16, Flgs:3, FragOff:13, 1674: TTL:8, Proto:8, HdrChkSum:16, 1675: SrcIP:32, DestIP:32, 1676: RestDgram/binary>> when HLen>=5, 1677: 4*HLen=<DgramSize -> 1678: OptsLen = 4*(HLen - 5), 1679: <<Opts:OptsLen/binary,Data/binary>> = RestDgram, 1680: {SrvcType, TotLen, Flgs, FragOff, ID, HdrChkSum, 1681: Proto, TTL, SrcIP, DestIP, Data, Opts}; 1682: _ -> 1683: not_ok 1684: end 1685: end, 1686: true = Fun4(<<>>) =:= not_ok, 1687: true = is_tuple(Fun4(list_to_binary 1688: ([<<4:4,5:4>>,list_to_binary(lists:seq(1,255))]))), 1689: 1690: X = 23432324, Y = 24324234, 1691: <<10:7>> = <<X:1, Y:6>>, 1692: Z = 234324324, 1693: XYZ = <<X:1, Y:6, Z:1>>, 1694: true = [20] =:= binary_to_list(XYZ), 1695: Hello1 = <<\"hello\">>, 1696: Hello2 = <<$h,$e,$l,$l,$o>>, 1697: true = \"hello\" =:= binary_to_list(Hello1), 1698: true = \"hello\" =:= binary_to_list(Hello2), 1699: 1700: FunM1 = fun(<<X1:7/binary, Y1:1/binary>>) -> {X1,Y1} end, 1701: true = {<<\"1234567\">>,<<\"8\">>} =:= FunM1(<<\"12345678\">>), 1702: 1703: FunM2 = fun(<<_X1:7/binary-unit:7, _Y1:1/binary-unit:1>>) -> ok; 1704: (_) -> not_ok end, 1705: true = not_ok =:= FunM2(<<\"1\">>), 1706: ok.">>), 1707: 1708: ok. 1709: 1710: triples_to_bin1(T) -> 1711: triples_to_bin1(T, <<>>). 1712: 1713: triples_to_bin1([{X,Y,Z} | T], Acc) -> 1714: triples_to_bin1(T, <<Acc/binary, X:32, Y:32, Z:32>>); % inefficient 1715: triples_to_bin1([], Acc) -> 1716: Acc. 1717: 1718: triples_to_bin2(T) -> 1719: triples_to_bin2(T, []). 1720: 1721: triples_to_bin2([{X,Y,Z} | T], Acc) -> 1722: triples_to_bin2(T, [<<X:32, Y:32, Z:32>> | Acc]); 1723: triples_to_bin2([], Acc) -> 1724: list_to_binary(lists:reverse(Acc)). 1725: 1726: progex_records(doc) -> 1727: ["Record examples from Programming Examples. OTP-5237."]; 1728: progex_records(suite) -> 1729: []; 1730: progex_records(Config) when is_list(Config) -> 1731: Test1 = 1732: <<"-module(recs). 1733: -record(person, {name = \"\", phone = [], address}). 1734: -record(name, {first = \"Robert\", last = \"Ericsson\"}). 1735: -record(person2, {name = #name{}, phone}). 1736: -export([t/0]). 1737: 1738: t() -> 1739: _P1 = #person{phone=[0,8,2,3,4,3,1,2], name=\"Robert\"}, 1740: \"Robert\" = _P1#person.name, 1741: [0,8,2,3,4,3,1,2] = _P1#person.phone, 1742: undefined = _P1#person.address, 1743: 1744: _P2 = #person{name = \"Jakob\", _ = '_'}, 1745: \"Jakob\" = _P2#person.name, 1746: '_' = _P2#person.phone, 1747: '_' = _P2#person.address, 1748: 1749: P = #person{name = \"Joe\", phone = [0,8,2,3,4,3,1,2]}, 1750: \"Joe\" = P#person.name, 1751: [0,8,2,3,4,3,1,2] = P#person.phone, 1752: undefined = P#person.address, 1753: 1754: P1 = #person{name=\"Joe\", phone=[1,2,3], address=\"A street\"}, 1755: P2 = P1#person{name=\"Robert\"}, 1756: \"Robert\" = P2#person.name, 1757: [1,2,3] = P2#person.phone, 1758: \"A street\" = P2#person.address, 1759: a_person = foo(P1), 1760: 1761: {found, [1,2,3]} = 1762: find_phone([#person{name = a}, 1763: #person{name = b, phone = [3,2,1]}, 1764: #person{name = c, phone = [1,2,3]}], 1765: c), 1766: 1767: P3 = #person{name=\"Joe\", phone=[0,0,7], address=\"A street\"}, 1768: #person{name = Name} = P3, 1769: \"Joe\" = Name, 1770: 1771: \"Robert\" = demo(), 1772: ok. 1773: 1774: foo(P) when is_record(P, person) -> a_person; 1775: foo(_) -> not_a_person. 1776: 1777: find_phone([#person{name=Name, phone=Phone} | _], Name) -> 1778: {found, Phone}; 1779: find_phone([_| T], Name) -> 1780: find_phone(T, Name); 1781: find_phone([], _Name) -> 1782: not_found. 1783: 1784: demo() -> 1785: P = #person2{name= #name{first=\"Robert\",last=\"Virding\"}, 1786: phone=123}, 1787: _First = (P#person2.name)#name.first. 1788: ">>, 1789: ?line ok = run_file(Config, recs, Test1), 1790: 1791: Test1_shell = 1792: <<"rd(person, {name = \"\", phone = [], address}), 1793: rd(name, {first = \"Robert\", last = \"Ericsson\"}), 1794: rd(person2, {name = #name{}, phone}), 1795: 1796: _P1 = #person{phone=[0,8,2,3,4,3,1,2], name=\"Robert\"}, 1797: \"Robert\" = _P1#person.name, 1798: [0,8,2,3,4,3,1,2] = _P1#person.phone, 1799: undefined = _P1#person.address, 1800: 1801: _P2 = #person{name = \"Jakob\", _ = '_'}, 1802: \"Jakob\" = _P2#person.name, 1803: '_' = _P2#person.phone, 1804: '_' = _P2#person.address, 1805: 1806: P = #person{name = \"Joe\", phone = [0,8,2,3,4,3,1,2]}, 1807: \"Joe\" = P#person.name, 1808: [0,8,2,3,4,3,1,2] = P#person.phone, 1809: undefined = P#person.address, 1810: 1811: P1 = #person{name=\"Joe\", phone=[1,2,3], address=\"A street\"}, 1812: P2 = P1#person{name=\"Robert\"}, 1813: \"Robert\" = P2#person.name, 1814: [1,2,3] = P2#person.phone, 1815: \"A street\" = P2#person.address, 1816: Foo = fun(P) when is_record(P, person) -> a_person; 1817: (_) -> not_a_person 1818: end, 1819: a_person = Foo(P1), 1820: 1821: Find = fun([#person{name=Name, phone=Phone} | _], Name, Fn) -> 1822: {found, Phone}; 1823: ([_| T], Name, Fn) -> 1824: Fn(T, Name, Fn); 1825: ([], _Name, _Fn) -> 1826: not_found 1827: end, 1828: 1829: {found, [1,2,3]} = Find([#person{name = a}, 1830: #person{name = b, phone = [3,2,1]}, 1831: #person{name = c, phone = [1,2,3]}], 1832: c, 1833: Find), 1834: 1835: P3 = #person{name=\"Joe\", phone=[0,0,7], address=\"A street\"}, 1836: #person{name = Name} = P3, 1837: \"Joe\" = Name, 1838: 1839: Demo = fun() -> 1840: P17 = #person2{name= #name{first=\"Robert\",last=\"Virding\"}, 1841: phone=123}, 1842: _First = (P17#person2.name)#name.first 1843: end, 1844: 1845: \"Robert\" = Demo(), 1846: ok. 1847: ">>, 1848: ?line [ok] = scan(Test1_shell), 1849: 1850: Test2 = 1851: <<"-module(recs). 1852: -record(person, {name, age, phone = [], dict = []}). 1853: -compile(export_all). 1854: 1855: t() -> ok. 1856: 1857: make_hacker_without_phone(Name, Age) -> 1858: #person{name = Name, age = Age, 1859: dict = [{computer_knowledge, excellent}, 1860: {drinks, coke}]}. 1861: print(#person{name = Name, age = Age, 1862: phone = Phone, dict = Dict}) -> 1863: io:format(\"Name: ~s, Age: ~w, Phone: ~w ~n\" 1864: \"Dictionary: ~w.~n\", [Name, Age, Phone, Dict]). 1865: 1866: birthday(P) when record(P, person) -> 1867: P#person{age = P#person.age + 1}. 1868: 1869: register_two_hackers() -> 1870: Hacker1 = make_hacker_without_phone(\"Joe\", 29), 1871: OldHacker = birthday(Hacker1), 1872: % The central_register_server should have 1873: % an interface function for this. 1874: central_register_server ! {register_person, Hacker1}, 1875: central_register_server ! {register_person, 1876: OldHacker#person{name = \"Robert\", 1877: phone = [0,8,3,2,4,5,3,1]}}. 1878: ">>, 1879: ?line ok = run_file(Config, recs, Test2), 1880: ok. 1881: 1882: progex_lc(doc) -> 1883: ["List comprehension examples from Programming Examples. OTP-5237."]; 1884: progex_lc(suite) -> 1885: []; 1886: progex_lc(Config) when is_list(Config) -> 1887: Test1 = 1888: <<"-module(lc). 1889: -export([t/0]). 1890: 1891: t() -> 1892: [a,4,b,5,6] = [X || X <- [1,2,a,3,4,b,5,6], X > 3], 1893: [4,5,6] = [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3], 1894: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = 1895: [{X, Y} || X <- [1,2,3], Y <- [a,b]], 1896: 1897: [1,2,3,4,5,6,7,8] = sort([4,5,1,8,3,6,7,2]), 1898: [[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]] = 1899: perms([b,u,g]), 1900: [] = pyth(11), 1901: [{3,4,5},{4,3,5}] = pyth(12), 1902: [{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, 1903: {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, 1904: {16,12,20}] = pyth(50), 1905: [] = pyth1(11), 1906: [{3,4,5},{4,3,5}] = pyth1(12), 1907: [{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, 1908: {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, 1909: {16,12,20}] = pyth1(50), 1910: [1,2,3,4,5] = append([[1,2,3],[4,5]]), 1911: [2,3,4] = map(fun(X) -> X + 1 end, [1,2,3]), 1912: [2,4] = filter(fun(X) -> X > 1 end, [0,2,4]), 1913: [1,2,3,7] = select(b,[{a,1},{b,2},{c,3},{b,7}]), 1914: [2,7] = select2(b,[{a,1},{b,2},{c,3},{b,7}]), 1915: ok. 1916: 1917: sort([Pivot|T]) -> 1918: sort([ X || X <- T, X < Pivot]) ++ 1919: [Pivot] ++ 1920: sort([ X || X <- T, X >= Pivot]); 1921: sort([]) -> []. 1922: 1923: perms([]) -> [[]]; 1924: perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. 1925: 1926: pyth(N) -> 1927: [ {A,B,C} || 1928: A <- lists:seq(1,N), 1929: B <- lists:seq(1,N), 1930: C <- lists:seq(1,N), 1931: A+B+C =< N, 1932: A*A+B*B == C*C 1933: ]. 1934: 1935: pyth1(N) -> 1936: [{A,B,C} || 1937: A <- lists:seq(1,N), 1938: B <- lists:seq(1,N-A+1), 1939: C <- lists:seq(1,N-A-B+2), 1940: A+B+C =< N, 1941: A*A+B*B == C*C ]. 1942: 1943: append(L) -> [X || L1 <- L, X <- L1]. 1944: map(Fun, L) -> [Fun(X) || X <- L]. 1945: filter(Pred, L) -> [X || X <- L, Pred(X)]. 1946: 1947: select(X, L) -> [Y || {X, Y} <- L]. 1948: select2(X, L) -> [Y || {X1, Y} <- L, X == X1]. 1949: ">>, 1950: ?line ok = run_file(Config, lc, Test1), 1951: 1952: Test1_shell = 1953: <<"[a,4,b,5,6] = [X || X <- [1,2,a,3,4,b,5,6], X > 3], 1954: [4,5,6] = [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3], 1955: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = 1956: [{X, Y} || X <- [1,2,3], Y <- [a,b]], 1957: 1958: Sort = fun([Pivot|T], Fn) -> 1959: Fn([ X || X <- T, X < Pivot], Fn) ++ 1960: [Pivot] ++ 1961: Fn([ X || X <- T, X >= Pivot], Fn); 1962: ([], _Fn) -> [] 1963: end, 1964: 1965: [1,2,3,4,5,6,7,8] = Sort([4,5,1,8,3,6,7,2], Sort), 1966: Perms = fun([], _Fn) -> [[]]; 1967: (L, Fn) -> [[H|T] || H <- L, T <- Fn(L--[H], Fn)] 1968: end, 1969: [[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]] = 1970: Perms([b,u,g], Perms), 1971: 1972: Pyth = fun(N) -> 1973: [ {A,B,C} || 1974: A <- lists:seq(1,N), 1975: B <- lists:seq(1,N), 1976: C <- lists:seq(1,N), 1977: A+B+C =< N, 1978: A*A+B*B == C*C 1979: ] 1980: end, 1981: 1982: [] = Pyth(11), 1983: [{3,4,5},{4,3,5}] = Pyth(12), 1984: %[{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, 1985: % {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, 1986: % {16,12,20}] = Pyth(50), 1987: 1988: Pyth1 = fun(N) -> 1989: [{A,B,C} || 1990: A <- lists:seq(1,N), 1991: B <- lists:seq(1,N-A+1), 1992: C <- lists:seq(1,N-A-B+2), 1993: A+B+C =< N, 1994: A*A+B*B == C*C ] 1995: end, 1996: 1997: [] = Pyth1(11), 1998: [{3,4,5},{4,3,5}] = Pyth1(12), 1999: [{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{8,15,17}, 2000: {9,12,15},{12,5,13},{12,9,15},{12,16,20},{15,8,17}, 2001: {16,12,20}] = Pyth1(50), 2002: 2003: Append = fun(L) -> [X || L1 <- L, X <- L1] end, 2004: [1,2,3,4,5] = Append([[1,2,3],[4,5]]), 2005: Map = fun(Fun, L) -> [Fun(X) || X <- L] end, 2006: [2,3,4] = Map(fun(X) -> X + 1 end, [1,2,3]), 2007: Filter = fun(Pred, L) -> [X || X <- L, Pred(X)] end, 2008: [2,4] = Filter(fun(X) -> X > 1 end, [0,2,4]), 2009: 2010: Select = fun(X, L) -> [Y || {X, Y} <- L] end, 2011: [1,2,3,7] = Select(b,[{a,1},{b,2},{c,3},{b,7}]), 2012: Select2 = fun(X, L) -> [Y || {X1, Y} <- L, X == X1] end, 2013: [2,7] = Select2(b,[{a,1},{b,2},{c,3},{b,7}]), 2014: ok. 2015: ">>, 2016: ?line [ok] = scan(Test1_shell), 2017: ok. 2018: 2019: progex_funs(doc) -> 2020: ["Funs examples from Programming Examples. OTP-5237."]; 2021: progex_funs(suite) -> 2022: []; 2023: progex_funs(Config) when is_list(Config) -> 2024: Test1 = 2025: <<"-module(funs). 2026: -compile(export_all). 2027: 2028: double([H|T]) -> [2*H|double(T)]; 2029: double([]) -> []. 2030: 2031: add_one([H|T]) -> [H+1|add_one(T)]; 2032: add_one([]) -> []. 2033: 2034: map(F, [H|T]) -> [F(H)|map(F, T)]; 2035: map(F, []) -> []. 2036: 2037: double2(L) -> map(fun(X) -> 2*X end, L). 2038: add_one2(L) -> map(fun(X) -> 1 + X end, L). 2039: 2040: print_list(Stream, [H|T]) -> 2041: io:format(Stream, \"~p~n\", [H]), 2042: print_list(Stream, T); 2043: print_list(Stream, []) -> 2044: true. 2045: 2046: broadcast(Msg, [Pid|Pids]) -> 2047: Pid ! Msg, 2048: broadcast(Msg, Pids); 2049: broadcast(_, []) -> 2050: true. 2051: 2052: foreach(F, [H|T]) -> 2053: F(H), 2054: foreach(F, T); 2055: foreach(F, []) -> 2056: ok. 2057: 2058: print_list2(S, L) -> 2059: foreach(fun(H) -> io:format(S, \"~p~n\",[H]) end, L). 2060: 2061: broadcast2(M, L) -> foreach(fun(Pid) -> Pid ! M end, L). 2062: 2063: t1() -> map(fun(X) -> 2 * X end, [1,2,3,4,5]). 2064: 2065: t2() -> map(fun double/1, [1,2,3,4,5]). 2066: 2067: t3() -> map({?MODULE, double3}, [1,2,3,4,5]). 2068: 2069: double3(X) -> X * 2. 2070: 2071: f(F, Args) when function(F) -> 2072: apply(F, Args); 2073: f(N, _) when integer(N) -> 2074: N. 2075: 2076: print_list3(File, List) -> 2077: {ok, Stream} = file:open(File, write), 2078: foreach(fun(X) -> io:format(Stream,\"~p~n\",[X]) end, List), 2079: file:close(Stream). 2080: 2081: print_list4(File, List) -> 2082: {ok, Stream} = file:open(File, write), 2083: foreach(fun(File) -> 2084: io:format(Stream,\"~p~n\",[File]) 2085: end, List), 2086: file:close(Stream). 2087: 2088: any(Pred, [H|T]) -> 2089: case Pred(H) of 2090: true -> true; 2091: false -> any(Pred, T) 2092: end; 2093: any(Pred, []) -> 2094: false. 2095: 2096: all(Pred, [H|T]) -> 2097: case Pred(H) of 2098: true -> all(Pred, T); 2099: false -> false 2100: end; 2101: all(Pred, []) -> 2102: true. 2103: 2104: foldl(F, Accu, [Hd|Tail]) -> 2105: foldl(F, F(Hd, Accu), Tail); 2106: foldl(F, Accu, []) -> Accu. 2107: 2108: mapfoldl(F, Accu0, [Hd|Tail]) -> 2109: {R,Accu1} = F(Hd, Accu0), 2110: {Rs,Accu2} = mapfoldl(F, Accu1, Tail), 2111: {[R|Rs], Accu2}; 2112: mapfoldl(F, Accu, []) -> {[], Accu}. 2113: 2114: filter(F, [H|T]) -> 2115: case F(H) of 2116: true -> [H|filter(F, T)]; 2117: false -> filter(F, T) 2118: end; 2119: filter(F, []) -> []. 2120: 2121: diff(L1, L2) -> 2122: filter(fun(X) -> not lists:member(X, L2) end, L1). 2123: 2124: intersection(L1,L2) -> filter(fun(X) -> lists:member(X,L1) end, L2). 2125: 2126: takewhile(Pred, [H|T]) -> 2127: case Pred(H) of 2128: true -> [H|takewhile(Pred, T)]; 2129: false -> [] 2130: end; 2131: takewhile(Pred, []) -> 2132: []. 2133: 2134: dropwhile(Pred, [H|T]) -> 2135: case Pred(H) of 2136: true -> dropwhile(Pred, T); 2137: false -> [H|T] 2138: end; 2139: dropwhile(Pred, []) -> 2140: []. 2141: 2142: splitlist(Pred, L) -> 2143: splitlist(Pred, L, []). 2144: 2145: splitlist(Pred, [H|T], L) -> 2146: case Pred(H) of 2147: true -> splitlist(Pred, T, [H|L]); 2148: false -> {lists:reverse(L), [H|T]} 2149: end; 2150: splitlist(Pred, [], L) -> 2151: {lists:reverse(L), []}. 2152: 2153: first(Pred, [H|T]) -> 2154: case Pred(H) of 2155: true -> 2156: {true, H}; 2157: false -> 2158: first(Pred, T) 2159: end; 2160: first(Pred, []) -> 2161: false. 2162: 2163: ints_from(N) -> 2164: fun() -> 2165: [N|ints_from(N+1)] 2166: end. 2167: 2168: pconst(X) -> 2169: fun (T) -> 2170: case T of 2171: [X|T1] -> {ok, {const, X}, T1}; 2172: _ -> fail 2173: end 2174: end. 2175: 2176: pand(P1, P2) -> 2177: fun (T) -> 2178: case P1(T) of 2179: {ok, R1, T1} -> 2180: case P2(T1) of 2181: {ok, R2, T2} -> 2182: {ok, {'and', R1, R2}}; 2183: fail -> 2184: fail 2185: end; 2186: fail -> 2187: fail 2188: end 2189: end. 2190: 2191: por(P1, P2) -> 2192: fun (T) -> 2193: case P1(T) of 2194: {ok, R, T1} -> 2195: {ok, {'or',1,R}, T1}; 2196: fail -> 2197: case P2(T) of 2198: {ok, R1, T1} -> 2199: {ok, {'or',2,R1}, T1}; 2200: fail -> 2201: fail 2202: end 2203: end 2204: end. 2205: 2206: grammar() -> 2207: pand( 2208: por(pconst(a), pconst(b)), 2209: por(pconst(c), pconst(d))). 2210: 2211: parse(List) -> 2212: (grammar())(List). 2213: 2214: 2215: t() -> 2216: [2,4,6,8] = double([1,2,3,4]), 2217: [2,3,4,5] = add_one([1,2,3,4]), 2218: [2,4,6,8] = double2([1,2,3,4]), 2219: [2,3,4,5] = add_one2([1,2,3,4]), 2220: XX = ints_from(1), 2221: [1 | _] = XX(), 2222: 1 = hd(XX()), 2223: Y = tl(XX()), 2224: 2 = hd(Y()), 2225: 2226: P1 = pconst(a), 2227: {ok,{const,a},[b,c]} = P1([a,b,c]), 2228: fail = P1([x,y,z]), 2229: 2230: {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} = 2231: parse([a,c]), 2232: {ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} = 2233: parse([a,d]), 2234: {ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} = 2235: parse([b,c]), 2236: {ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} = 2237: parse([b,d]), 2238: fail = parse([a,b]), 2239: ok. 2240: ">>, 2241: ?line ok = run_file(Config, funs, Test1), 2242: 2243: Test2_shell = 2244: <<"Double = fun(X) -> 2 * X end, 2245: [2,4,6,8,10] = lists:map(Double, [1,2,3,4,5]), 2246: 2247: Big = fun(X) -> if X > 10 -> true; true -> false end end, 2248: false = lists:any(Big, [1,2,3,4]), 2249: true = lists:any(Big, [1,2,3,12,5]), 2250: false = lists:all(Big, [1,2,3,4,12,6]), 2251: true = lists:all(Big, [12,13,14,15]), 2252: L = [\"I\",\"like\",\"Erlang\"], 2253: 11 = lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L), 2254: Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a; 2255: (X) -> X 2256: end, 2257: Upcase_word = fun(X) -> lists:map(Upcase, X) end, 2258: \"ERLANG\" = Upcase_word(\"Erlang\"), 2259: [\"I\",\"LIKE\",\"ERLANG\"] = lists:map(Upcase_word, L), 2260: {[\"I\",\"LIKE\",\"ERLANG\"],11} = 2261: lists:mapfoldl(fun(Word, Sum) -> 2262: {Upcase_word(Word), Sum + length(Word)} 2263: end, 0, L), 2264: [500,12,45] = lists:filter(Big, [500,12,2,45,6,7]), 2265: [200,500,45] = lists:takewhile(Big, [200,500,45,5,3,45,6]), 2266: [5,3,45,6] = lists:dropwhile(Big, [200,500,45,5,3,45,6]), 2267: {[200,500,45],[5,3,45,6]} = 2268: lists:splitwith(Big, [200,500,45,5,3,45,6]), 2269: %% {true,45} = lists:first(Big, [1,2,45,6,123]), 2270: %% false = lists:first(Big, [1,2,4,5]), 2271: 2272: Adder = fun(X) -> fun(Y) -> X + Y end end, 2273: Add6 = Adder(6), 2274: 16 = Add6(10), 2275: ok. 2276: ">>, 2277: ?line [ok] = scan(Test2_shell), 2278: ok. 2279: 2280: 2281: otp_5990(doc) -> 2282: "OTP-5990. {erlang,is_record}."; 2283: otp_5990(suite) -> []; 2284: otp_5990(Config) when is_list(Config) -> 2285: ?line [true] = 2286: scan(<<"rd('OrdSet', {orddata = {},ordtype = type}), " 2287: "S = #'OrdSet'{ordtype = {}}, " 2288: "if tuple(S#'OrdSet'.ordtype) -> true; true -> false end.">>), 2289: ok. 2290: 2291: otp_6166(doc) -> 2292: "OTP-6166. Order of record definitions."; 2293: otp_6166(suite) -> []; 2294: otp_6166(Config) when is_list(Config) -> 2295: Test1 = filename:join(?config(priv_dir, Config), "test1.hrl"), 2296: Contents1 = <<"-module(test1). 2297: -record(r5, {f}). -record(r3, {f = #r5{}}). " 2298: "-record(r1, {f = #r3{}}). -record(r4, {f = #r1{}}). " 2299: "-record(r2, {f = #r4{}}).">>, 2300: ?line ok = file:write_file(Test1, Contents1), 2301: 2302: Test2 = filename:join(?config(priv_dir, Config), "test2.hrl"), 2303: Contents2 = <<"-module(test2). 2304: -record(r5, {f}). -record(r3, {f = #r5{}}). " 2305: "-record(r1, {f = #r3{}}). -record(r4, {f = #r1{}}). " 2306: "-record(r2, {f = #r4{}}). 2307: -record(r6, {f = #r5{}}). % r6 > r0 2308: -record(r0, {f = #r5{}, g = #r5{}}). % r0 < r5">>, 2309: ?line ok = file:write_file(Test2, Contents2), 2310: 2311: RR12 = "[r1,r2,r3,r4,r5] = rr(\"" ++ Test1 ++ "\"), 2312: [r0,r1,r2,r3,r4,r5,r6] = rr(\"" ++ Test2 ++ "\"), 2313: R0 = #r0{}, R6 = #r6{}, 2314: true = is_record(R0, r0), 2315: true = is_record(R6, r6), 2316: ok. ", 2317: ?line [ok] = scan(RR12), 2318: 2319: file:delete(Test1), 2320: file:delete(Test2), 2321: ok. 2322: 2323: otp_6554(doc) -> 2324: "OTP-6554. Formatted exits and error messages."; 2325: otp_6554(suite) -> []; 2326: otp_6554(Config) when is_list(Config) -> 2327: %% Should check the stacktrace as well... 2328: ?line "exception error: bad argument" = 2329: comm_err(<<"math:sqrt(a).">>), 2330: ?line "exception error: bad argument" = 2331: comm_err(<<"fun(X, Y) -> X ++ Y end(a, b).">>), 2332: ?line "exception error: bad argument" = 2333: comm_err(<<"math:sqrt(lists:seq(1,40)).">>), 2334: ?line "exception error: bad argument" = 2335: comm_err(<<"math:sqrt(lists:seq(1,10)).">>), 2336: ?line "exception error: bad argument" = 2337: comm_err(<<"a ++ b.">>), 2338: ?line "exception error: bad argument" = 2339: comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, 2340: undefined,undefined,undefined,undefined,undefined, 2341: undefined,undefined,undefined,undefined}, 2342: aa ++ I.">>), 2343: ?line "exception error: bad argument" = 2344: comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, 2345: undefined,undefined,undefined,undefined,undefined, 2346: undefined,undefined,undefined,undefined}, 2347: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ++ I.">>), 2348: ?line "exception error: bad argument" = 2349: comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, 2350: undefined,undefined,undefined,undefined,undefined, 2351: undefined,undefined,undefined,undefined}, 2352: I ++ I.">>), 2353: ?line "exception error: bad argument" = 2354: comm_err(<<"fun(X) -> not X end(a).">>), 2355: ?line "exception error: bad argument: a" = 2356: comm_err(<<"fun(A, B) -> A orelse B end(a, b).">>), 2357: ?line "exception error: an error occurred when evaluating an arithmetic expression" = 2358: comm_err(<<"math:sqrt(2)/round(math:sqrt(0)).">>), 2359: ?line "exception error: interpreted function with arity 1 called with no arguments" = 2360: comm_err(<<"fun(V) -> V end().">>), 2361: ?line "exception error: interpreted function with arity 1 called with two arguments" = 2362: comm_err(<<"fun(V) -> V end(1,2).">>), 2363: ?line "exception error: interpreted function with arity 0 called with one argument" = 2364: comm_err(<<"fun() -> v end(1).">>), 2365: ?line "exception error: interpreted function with arity 0 called with 4 arguments" = 2366: comm_err(<<"fun() -> v end(1,2,3,4).">>), 2367: ?line "exception error: math:sqrt/1 called with two arguments" = 2368: comm_err(<<"fun math:sqrt/1(1,2).">>), 2369: ?line "exception error: bad function 1." ++ _ = 2370: comm_err(<<"(math:sqrt(2))().">>), 2371: ?line "exception error: bad function [1," ++ _ = 2372: comm_err(<<"(lists:seq(1, 100))().">>), 2373: ?line "exception error: no match of right hand side value 1" ++ _ = 2374: comm_err(<<"a = math:sqrt(2).">>), 2375: ?line "exception error: no match of right hand side value" ++ _ = 2376: comm_err(<<"I = {file_info,undefined,undefined,undefined,undefined, 2377: undefined,undefined,undefined,undefined,undefined, 2378: undefined,undefined,undefined,undefined}, 2379: a = I.">>), 2380: ?line "exception error: no case clause matching 1" ++ _ = 2381: comm_err(<<"case math:sqrt(2) of a -> ok end.">>), 2382: ?line "exception error: no case clause matching [1," ++ _ = 2383: comm_err(<<"V = lists:seq(1, 20), case V of a -> ok end.">>), 2384: ?line "exception error: no function clause matching" = 2385: comm_err(<<"fun(P) when is_pid(P) -> true end(a).">>), 2386: case test_server:is_native(erl_eval) of 2387: true -> 2388: %% Native code has different exit reason. Don't bother 2389: %% testing them. 2390: ok; 2391: false -> 2392: "exception error: {function_clause," = 2393: comm_err(<<"erlang:error(function_clause, " 2394: "[unproper | list]).">>), 2395: %% Cheating: 2396: "exception error: no function clause matching " 2397: "erl_eval:do_apply(4)" ++ _ = 2398: comm_err(<<"erlang:error(function_clause, [4]).">>), 2399: "exception error: no function clause matching " 2400: "lists:reverse(" ++ _ = 2401: comm_err(<<"F=fun() -> hello end, lists:reverse(F).">>), 2402: "exception error: no function clause matching " 2403: "lists:reverse(34) (lists.erl, line " ++ _ = 2404: comm_err(<<"lists:reverse(34).">>) 2405: end, 2406: ?line "exception error: function_clause" = 2407: comm_err(<<"erlang:error(function_clause, 4).">>), 2408: ?line "exception error: no function clause matching" ++ _ = 2409: comm_err(<<"fun(a, b, c, d) -> foo end" 2410: " (lists:seq(1,17)," 2411: " lists:seq(1, 18)," 2412: " lists:seq(1, 40)," 2413: " lists:seq(1, 5)).">>), 2414: 2415: ?line "exception error: no function clause matching" = 2416: comm_err(<<"fun(P, q) when is_pid(P) -> true end(a, b).">>), 2417: ?line "exception error: no true branch found when evaluating an if expression" = 2418: comm_err(<<"if length([a,b]) > 17 -> a end.">>), 2419: ?line "exception error: no such process or port" = 2420: comm_err(<<"Pid = spawn(fun() -> a end)," 2421: "timer:sleep(1)," 2422: "link(Pid).">>), 2423: ?line "exception error: a system limit has been reached" = 2424: comm_err(<<"list_to_atom(lists:duplicate(300,$a)).">>), 2425: ?line "exception error: bad receive timeout value" = 2426: comm_err(<<"receive after a -> foo end.">>), 2427: ?line "exception error: no try clause matching 1" ++ _ = 2428: comm_err(<<"try math:sqrt(2) of bar -> yes after 3 end.">>), 2429: ?line "exception error: no try clause matching [1" ++ _ = 2430: comm_err(<<"V = lists:seq(1, 20)," 2431: "try V of bar -> yes after 3 end.">>), 2432: ?line "exception error: undefined function math:sqrt/2" = 2433: comm_err(<<"math:sqrt(2, 2).">>), 2434: ?line "exception error: limit of number of arguments to interpreted function " 2435: "exceeded" = 2436: comm_err(<<"fun(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) ->" 2437: " a end().">>), 2438: ?line "exception error: bad filter a" = 2439: comm_err(<<"[b || begin a end].">>), 2440: ?line "exception error: bad generator a" = 2441: comm_err(<<"[X || X <- a].">>), 2442: ?line "exception throw: undef" = comm_err(<<"throw(undef).">>), 2443: ?line "exception exit: undef" = comm_err(<<"exit(undef).">>), 2444: 2445: ?line "exception exit: foo" = 2446: comm_err(<<"catch spawn_link(fun() ->" 2447: " timer:sleep(300), exit(foo) " 2448: " end)," 2449: "timer:sleep(500).">>), 2450: ?line [ok] = scan( 2451: <<"begin process_flag(trap_exit, true)," 2452: " Pid = spawn_link(fun() ->" 2453: " timer:sleep(300), exit(foo) " 2454: " end)," 2455: " timer:sleep(500)," 2456: " receive {'EXIT', Pid, foo} -> ok end end.">>), 2457: ?line "exception exit: badarith" = 2458: comm_err(<<"catch spawn_link(fun() ->" 2459: " timer:sleep(300), 1/0 " 2460: " end)," 2461: "timer:sleep(500).">>), 2462: ?line "exception exit: {nocatch,foo}" = 2463: comm_err(<<"catch spawn_link(fun() ->" 2464: " timer:sleep(300), throw(foo) " 2465: " end)," 2466: "timer:sleep(500).">>), 2467: ?line [ok] = scan( 2468: <<"begin process_flag(trap_exit, true)," 2469: " Pid = spawn_link(fun() ->" 2470: " timer:sleep(300), throw(foo) " 2471: " end)," 2472: " timer:sleep(500)," 2473: " receive {'EXIT', Pid, {{nocatch,foo},_}} -> ok end " 2474: "end.">>), 2475: 2476: ?line "exception error: an error occurred when evaluating an arithmetic expression" = 2477: comm_err(<<"begin catch_exception(true), 1/0 end.">>), 2478: ?line "exception error: an error occurred when evaluating an arithmetic expression" = 2479: comm_err(<<"begin catch_exception(false), 1/0 end.">>), 2480: ?line "exception error: no function clause matching call to catch_exception/1" = 2481: comm_err(<<"catch_exception(1).">>), 2482: 2483: %% A bug was corrected (expansion of 'try'): 2484: ?line "2: command not found" = 2485: comm_err(<<"try 1 of 1 -> v(2) after 3 end.">>), 2486: %% Cover a few lines: 2487: ?line "3: command not found" = 2488: comm_err(<<"receive foo -> foo after 0 -> v(3) end.">>), 2489: ?line "3: command not found" = 2490: comm_err(<<"receive foo -> foo after 0 -> e(3) end.">>), 2491: ?line "1 / 0: command not found" = comm_err(<<"v(1/0).">>), 2492: ?line "1\n1.\n" = t(<<"1. e(1).">>), 2493: ?line [ok] = scan(<<"h().">>), 2494: ?line "exception exit: normal" = comm_err(<<"exit(normal).">>), 2495: ?line [foo] = scan(<<"begin history(0), foo end.">>), 2496: ?line application:unset_env(stdlib, shell_history_length), 2497: ?line [true] = scan(<<"begin <<10:(1024*1024*10)>>," 2498: "<<10:(1024*1024*10)>>, garbage_collect() end.">>), 2499: ?line "1: syntax error before: '.'" = comm_err("1-."), 2500: %% ?line comm_err(<<"exit().">>), % would hang 2501: ?line "exception error: no function clause matching call to history/1" = 2502: comm_err(<<"history(foo).">>), 2503: ?line "exception error: no function clause matching call to results/1" = 2504: comm_err(<<"results(foo).">>), 2505: 2506: ?line Test = filename:join(?config(priv_dir, Config), 2507: "otp_6554.erl"), 2508: Contents = <<"-module(otp_6554). 2509: -export([local_allowed/3, non_local_allowed/3]). 2510: local_allowed(_,_,State) -> 2511: {true,State}. 2512: 2513: non_local_allowed(_,_,State) -> 2514: {true,State}. 2515: ">>, 2516: ?line ok = compile_file(Config, Test, Contents, []), 2517: ?line "exception exit: restricted shell starts now" = 2518: comm_err(<<"begin shell:start_restricted(otp_6554) end.">>), 2519: ?line "-record(r,{}).\n1.\nok.\n" = 2520: t(<<"f(), f(B), h(), b(), history(20), results(20)," 2521: "rd(r, {}), rl(r), rf('_'), rl(), rf()," 2522: "rp(1), _ = rr({foo}), _ = rr({foo}, [])," 2523: "rr({foo}, [], []), ok.">>), 2524: ?line "false.\n" = t(<<"catch_exception(true).">>), 2525: ?line "exception exit: restricted shell stopped"= 2526: comm_err(<<"begin shell:stop_restricted() end.">>), 2527: ?line "true.\n" = t(<<"catch_exception(false).">>), 2528: 2529: ?line "20\n1\n1\n1: results(2)\n2: 1\n-> 1\n3: v(2)\n-> 1.\nok.\n" = 2530: t(<<"results(2). 1. v(2). h().">>), 2531: ?line application:unset_env(stdlib, shell_saved_results), 2532: ?line "1\nfoo\n17\nB = foo\nC = 17\nF = fun() ->\n foo" 2533: "\n end.\nok.\n" = 2534: t(<<"begin F = fun() -> foo end, 1 end. B = F(). C = 17. b().">>), 2535: 2536: %% Tests I'd like to do: (you should try them manually) 2537: %% "catch spawn_link(fun() -> timer:sleep(1000), exit(foo) end)." 2538: %% "** exception error: foo" should be output after 1 second 2539: %% "catch spawn_link(fun() -> timer:sleep(1000), 1/0 end)." 2540: %% "** exception error: bad argument..." should be output after 1 second 2541: %% "1/0", "exit(foo)", "throw(foo)". 2542: %% "h()." should show {'EXIT', {badarith,..}}, {'EXIT',{foo,...}}, 2543: %% and {'EXIT',{{nocatch,foo},...}}. 2544: 2545: ok. 2546: 2547: otp_7184(doc) -> 2548: "OTP-7184. Propagate exit signals from dying evaluator process."; 2549: otp_7184(suite) -> []; 2550: otp_7184(Config) when is_list(Config) -> 2551: register(otp_7184, self()), 2552: ?line catch 2553: t(<<"P = self(), 2554: spawn_link(fun() -> process_flag(trap_exit,true), 2555: P ! up, 2556: receive X -> 2557: otp_7184 ! {otp_7184, X} 2558: end 2559: end), 2560: receive up -> ok end, 2561: erlang:raise(throw, thrown, []).">>), 2562: receive {otp_7184,{'EXIT',_,{{nocatch,thrown},[]}}} -> ok end, 2563: 2564: ?line catch 2565: t(<<"P = self(), 2566: spawn_link(fun() -> process_flag(trap_exit,true), 2567: P ! up, 2568: receive X -> 2569: otp_7184 ! {otp_7184, X} 2570: end 2571: end), 2572: receive up -> ok end, 2573: erlang:raise(exit, fini, []).">>), 2574: receive {otp_7184,{'EXIT',_,{fini,[]}}} -> ok end, 2575: 2576: ?line catch 2577: t(<<"P = self(), 2578: spawn_link(fun() -> process_flag(trap_exit,true), 2579: P ! up, 2580: receive X -> 2581: otp_7184 ! {otp_7184,X} 2582: end 2583: end), 2584: receive up -> ok end, 2585: erlang:raise(error, bad, []).">>), 2586: receive {otp_7184,{'EXIT',_,{bad,[]}}} -> ok end, 2587: 2588: unregister(otp_7184), 2589: 2590: %% v/1, a few missed cases 2591: ?line "17\n<<0,0,0,64>>.\nok.\n" = 2592: t(<<"17. " 2593: "<<64:32>>. " 2594: "<<64>> = << << X >> || << X >> <= v(2), X > v(1) >>, ok.">>), 2595: 2596: ?line "17\n<<0,17>>.\n" =t(<<"17. <<(v(1)):16>>.">>), 2597: 2598: ok. 2599: 2600: otp_7232(doc) -> 2601: "OTP-7232. qlc:info() bug."; 2602: otp_7232(suite) -> []; 2603: otp_7232(Config) when is_list(Config) -> 2604: Info = <<"qlc:info(qlc:sort(qlc:q([X || X <- [1000,2000]]), " 2605: "{order, fun(A,B)-> A>B end})).">>, 2606: "qlc:sort([1000,2000],\n" 2607: " [{order,\n" 2608: " fun(A, B) ->\n" 2609: " A > B\n" 2610: " end}])" = evaluate(Info, []), 2611: ok. 2612: 2613: otp_8393(doc) -> 2614: "OTP-8393. Prompt string."; 2615: otp_8393(suite) -> []; 2616: otp_8393(Config) when is_list(Config) -> 2617: ?line _ = shell:prompt_func(default), 2618: ?line "Bad prompt function: '> '" = 2619: prompt_err(<<"shell:prompt_func('> ').">>), 2620: 2621: ?line _ = shell:prompt_func(default), 2622: ?line "exception error: an error occurred when evaluating an arithmetic expression"++_ = 2623: prompt_err(<<"shell:prompt_func({shell_SUITE,prompt4}).">>), 2624: 2625: ?line _ = shell:prompt_func(default), 2626: ?line "default.\n" = 2627: t(<<"shell:prompt_func({shell_SUITE,prompt2}).">>), 2628: 2629: ?line _ = shell:prompt_func(default), 2630: ?line "default\nl.\n" = 2631: t(<<"shell:prompt_func({shell_SUITE,prompt3}). l.">>), 2632: 2633: %% 2634: %% Although this tests that you can set a unicode prompt function 2635: %% it does not really test that it does work with the io-servers. 2636: %% That is instead tested in the io_proto_SUITE, which has 2637: %% the right infrastructure in place for such tests. /PaN 2638: %% 2639: ?line _ = shell:prompt_func(default), 2640: ?line "default\nl.\n" = 2641: t(<<"shell:prompt_func({shell_SUITE,prompt5}). l.">>), 2642: 2643: %% Restricted shell. 2644: Contents = <<"-module(test_restricted_shell). 2645: -export([local_allowed/3, non_local_allowed/3]). 2646: local_allowed(_,_,State) -> 2647: {false,State}. 2648: 2649: non_local_allowed({shell,stop_restricted},[],State) -> 2650: {true,State}; 2651: non_local_allowed({shell,prompt_func},[_L],State) -> 2652: {true,State}; 2653: non_local_allowed({shell_SUITE,prompt1},[_L],State) -> 2654: {true,State}; 2655: non_local_allowed(_,_,State) -> 2656: {false,State}. 2657: ">>, 2658: ?line Test = filename:join(?config(priv_dir, Config), 2659: "test_restricted_shell.erl"), 2660: ?line ok = compile_file(Config, Test, Contents, []), 2661: ?line _ = shell:prompt_func(default), 2662: ?line "exception exit: restricted shell starts now" = 2663: comm_err(<<"begin shell:start_restricted(" 2664: "test_restricted_shell) end.">>), 2665: ?line "default.\n"++_ = 2666: t(<<"shell:prompt_func({shell_SUITE,prompt1}).">>), 2667: ?line "exception exit: restricted shell does not allow apple(" ++ _ = 2668: comm_err(<<"apple(1).">>), 2669: ?line "{shell_SUITE,prompt1}.\n" = 2670: t(<<"shell:prompt_func(default).">>), 2671: ?line "exception exit: restricted shell stopped"= 2672: comm_err(<<"begin shell:stop_restricted() end.">>), 2673: ?line undefined = 2674: application:get_env(stdlib, restricted_shell), 2675: 2676: ?line NR = shell:results(20), 2677: ?line "default\n20.\n" = 2678: t(<<"shell:prompt_func({shell_SUITE,prompt3}). results(0).">>), 2679: 2680: ?line _ = shell:prompt_func(default), 2681: ?line 0 = shell:results(NR), 2682: ok. 2683: 2684: prompt1(_L) -> 2685: "prompt> ". 2686: 2687: prompt2(_L) -> 2688: {'EXIT', []}. 2689: 2690: prompt3(L) -> 2691: N = proplists:get_value(history, L), 2692: integer_to_list(N). 2693: 2694: prompt4(_L) -> 2695: erlang:apply(fun erlang:'/'/2, [1,0]). 2696: 2697: prompt5(_L) -> 2698: [1050,1072,1082,1074,1086,32,1077,32,85,110,105,99,111,100,101,32,63]. 2699: 2700: -ifdef(not_used). 2701: exit_term(B) -> 2702: "** exception exit:" ++ Reply = t(B), 2703: S0 = string:left(Reply, string:chr(Reply, $\n)-1), 2704: S = string:strip(S0, right, $*), 2705: {ok,Ts,_} = erl_scan:string(S), 2706: {ok,Term} = erl_parse:parse_term(Ts), 2707: Term. 2708: -endif. 2709: 2710: error_string(B) -> 2711: "** exception error:" ++ Reply = t(B), 2712: caught_string(Reply). 2713: 2714: exit_string(B) -> 2715: "** exception exit:" ++ Reply = t(B), 2716: caught_string(Reply). 2717: 2718: caught_string(Reply) -> 2719: S0 = string:left(Reply, string:chr(Reply, $\n)-1), 2720: S1 = string:strip(S0, right, $.), 2721: S2 = string:strip(S1, left, $*), 2722: S = string:strip(S2, both, $ ), 2723: string:strip(S, both, $"). 2724: 2725: comm_err(B) -> 2726: Reply = t(B), 2727: S0 = string:left(Reply, string:chr(Reply, $\n)-1), 2728: S1 = string:strip(S0, left, $*), 2729: S2 = string:strip(S1, both, $ ), 2730: S = string:strip(S2, both, $"), 2731: string:strip(S, right, $.). 2732: 2733: prompt_err(B) -> 2734: Reply = t(B), 2735: S00 = string:sub_string(Reply, string:chr(Reply, $\n)+1), 2736: S0 = string:left(S00, string:chr(S00, $\n)-1), 2737: S1 = string:strip(S0, left, $*), 2738: S2 = string:strip(S1, both, $ ), 2739: S = string:strip(S2, both, $"), 2740: string:strip(S, right, $.). 2741: 2742: otp_10302(doc) -> 2743: "OTP-10302. Unicode."; 2744: otp_10302(suite) -> []; 2745: otp_10302(Config) when is_list(Config) -> 2746: {ok,Node} = start_node(shell_suite_helper_2, 2747: "-pa "++?config(priv_dir,Config)++ 2748: " +pc unicode"), 2749: Test1 = 2750: <<"begin 2751: io:setopts([{encoding,utf8}]), 2752: [1024] = \"\\x{400}\", 2753: rd(rec, {a = \"\\x{400}\"}), 2754: ok = rl(rec) 2755: end.">>, 2756: "-record(rec,{a = \"\x{400}\"}).\nok.\n" = t({Node,Test1}), 2757: 2758: Test3 = 2759: <<"io:setopts([{encoding,utf8}]). 2760: rd(rec, {a = \"\\x{400}\"}). 2761: ok = rp(#rec{}).">>, 2762: "ok.\nrec\n#rec{a = \"\x{400}\"}.\nok.\n" = t({Node,Test3}), 2763: 2764: Test4 = 2765: <<"io:setopts([{encoding,utf8}]). 2766: A = [1024] = \"\\x{400}\". 2767: b(). 2768: h().">>, 2769: 2770: "ok.\n\"\x{400}\"\nA = \"\x{400}\".\nok.\n" 2771: "1: io:setopts([{encoding,utf8}])\n-> ok.\n" 2772: "2: A = [1024] = \"\x{400}\"\n-> \"\x{400}\"\n" 2773: "3: b()\n-> ok.\nok.\n" = t({Node,Test4}), 2774: 2775: Test5 = 2776: <<"begin 2777: io:setopts([{encoding,utf8}]), 2778: results(0), 2779: A = [1024] = \"\\x{400}\", 2780: b(), 2781: h() 2782: end.">>, 2783: "A = \"\x{400}\".\nok.\n" = t({Node,Test5}), 2784: 2785: %% One $" is "lost": 2786: true = 2787: "\x{400}\": command not found" =:= 2788: prompt_err({Node, 2789: <<"io:setopts([{encoding,utf8}]). v(\"\x{400}\")."/utf8>>, 2790: unicode}), 2791: 2792: "ok.\ndefault\n* Bad prompt function: \"\x{400}\".\n" = 2793: t({Node,<<"io:setopts([{encoding,utf8}]). " 2794: "shell:prompt_func(\"\x{400}\")."/utf8>>, 2795: unicode}), 2796: rpc:call(Node,shell, prompt_func, [default]), 2797: _ = shell:prompt_func(default), 2798: 2799: %% Test lib:format_exception() (cf. OTP-6554) 2800: Test6 = 2801: <<"begin 2802: A = <<\"\\xaa\">>, 2803: S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), 2804: {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), 2805: {ok, Es} = erl_parse:parse_exprs(Ts), 2806: B = erl_eval:new_bindings(), 2807: erl_eval:exprs(Es, B) 2808: end.">>, 2809: 2810: "** exception error: an error occurred when evaluating" 2811: " an arithmetic expression\n in operator '/'/2\n" 2812: " called as <<\"\xaa\">> / <<\"\xaa\">>.\n" = t(Test6), 2813: Test7 = 2814: <<"io:setopts([{encoding,utf8}]). 2815: A = <<\"\\xaa\">>, 2816: S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), 2817: {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), 2818: {ok, Es} = erl_parse:parse_exprs(Ts), 2819: B = erl_eval:new_bindings(), 2820: erl_eval:exprs(Es, B).">>, 2821: 2822: "ok.\n** exception error: an error occurred when evaluating" 2823: " an arithmetic expression\n in operator '/'/2\n" 2824: " called as <<\"ª\">> / <<\"ª\">>.\n" = t({Node,Test7}), 2825: Test8 = 2826: <<"begin 2827: A = [1089], 2828: S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), 2829: {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), 2830: {ok, Es} = erl_parse:parse_exprs(Ts), 2831: B = erl_eval:new_bindings(), 2832: erl_eval:exprs(Es, B) 2833: end.">>, 2834: "** exception error: an error occurred when evaluating" 2835: " an arithmetic expression\n in operator '/'/2\n" 2836: " called as [1089] / [1089].\n" = t(Test8), 2837: Test9 = 2838: <<"io:setopts([{encoding,utf8}]). 2839: A = [1089], 2840: S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), 2841: {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), 2842: {ok, Es} = erl_parse:parse_exprs(Ts), 2843: B = erl_eval:new_bindings(), 2844: erl_eval:exprs(Es, B).">>, 2845: 2846: "ok.\n** exception error: an error occurred when evaluating" 2847: " an arithmetic expression\n in operator '/'/2\n" 2848: " called as \"\x{441}\" / \"\x{441}\".\n" = t({Node,Test9}), 2849: Test10 = 2850: <<"A = {\"1\\xaa\", 2851: $\\xaa, 2852: << <<\"hi\">>/binary >>, 2853: <<\"1\xaa\">>}, 2854: fun(a) -> true end(A).">>, 2855: "** exception error: no function clause matching \n" 2856: " erl_eval:'-inside-an-interpreted-fun-'" 2857: "({\"1\xc2\xaa\",170,<<\"hi\">>,\n " 2858: " <<\"1\xc2\xaa\">>}) .\n" = t(Test10), 2859: Test11 = 2860: <<"io:setopts([{encoding,utf8}]). 2861: A = {\"1\\xaa\", 2862: $\\xaa, 2863: << <<\"hi\">>/binary >>, 2864: <<\"1\xaa\">>}, 2865: fun(a) -> true end(A).">>, 2866: 2867: "ok.\n** exception error: no function clause matching \n" 2868: " erl_eval:'-inside-an-interpreted-fun-'" 2869: "({\"1\xaa\",170,<<\"hi\">>,\n " 2870: " <<\"1\xaa\"/utf8>>}) .\n" = t({Node,Test11}), 2871: Test12 = <<"fun(a, b) -> false end(65, [1089]).">>, 2872: "** exception error: no function clause matching \n" 2873: " erl_eval:'-inside-an-interpreted-fun-'(65,[1089])" 2874: " .\n" = t(Test12), 2875: Test13 = 2876: <<"io:setopts([{encoding,utf8}]). 2877: fun(a, b) -> false end(65, [1089]).">>, 2878: "ok.\n** exception error: no function clause matching \n" 2879: " erl_eval:'-inside-an-interpreted-fun-'(65,\"\x{441}\")" 2880: " .\n" = t({Node,Test13}), 2881: 2882: test_server:stop_node(Node), 2883: ok. 2884: 2885: scan(B) -> 2886: F = fun(Ts) -> 2887: case erl_parse:parse_term(Ts) of 2888: {ok,Term} -> 2889: Term; 2890: _Error -> 2891: {ok,Form} = erl_parse:parse_form(Ts), 2892: Form 2893: end 2894: end, 2895: scan(t(B), F). 2896: 2897: scan(S0, F) -> 2898: case erl_scan:tokens([], S0, 1, [unicode]) of 2899: {done,{ok,Ts,_},S} -> 2900: [F(Ts) | scan(S, F)]; 2901: _Else -> 2902: [] 2903: end. 2904: 2905: t({Node,Bin,Enc}) when is_atom(Node),is_binary(Bin), is_atom(Enc) -> 2906: t0({Bin,Enc}, fun() -> start_new_shell(Node) end); 2907: t({Node,Bin}) when is_atom(Node),is_binary(Bin) -> 2908: t0({Bin,latin1}, fun() -> start_new_shell(Node) end); 2909: t(Bin) when is_binary(Bin) -> 2910: t0({Bin,latin1}, fun() -> start_new_shell() end); 2911: t({Bin,Enc}) when is_binary(Bin), is_atom(Enc) -> 2912: t0({Bin,Enc}, fun() -> start_new_shell() end); 2913: t(L) -> 2914: t(list_to_binary(L)). 2915: 2916: t0({Bin,Enc}, F) -> 2917: %% Spawn a process so that io_request messages do not interfer. 2918: P = self(), 2919: C = spawn(fun() -> t1(P, {Bin, Enc}, F) end), 2920: receive {C, R} -> R end. 2921: 2922: t1(Parent, {Bin,Enc}, F) -> 2923: io:format("*** Testing ~s~n", [binary_to_list(Bin)]), 2924: S = #state{bin = Bin, unic = Enc, reply = [], leader = group_leader()}, 2925: group_leader(self(), self()), 2926: _Shell = F(), 2927: try 2928: server_loop(S) 2929: catch exit:R -> Parent ! {self(), R}; 2930: throw:{?MODULE,LoopReply,latin1} -> 2931: L0 = binary_to_list(list_to_binary(LoopReply)), 2932: [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), 2933: Parent ! {self(), dotify(L1)}; 2934: throw:{?MODULE,LoopReply,_Uni} -> 2935: Tmp = unicode:characters_to_binary(LoopReply), 2936: L0 = unicode:characters_to_list(Tmp), 2937: [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), 2938: Parent ! {self(), dotify(L1)} 2939: after group_leader(S#state.leader, self()) 2940: end. 2941: 2942: dotify([$., $\n | L]) -> 2943: [$., $\n | dotify(L)]; 2944: dotify([$,, $\n | L]) -> 2945: [$,, $\n | dotify(L)]; 2946: dotify("ok\n" ++ L) -> 2947: "ok.\n" ++ dotify(L); 2948: dotify("\nok\n" ++ L) -> 2949: ".\nok.\n" ++ dotify(L); 2950: dotify([$\n]) -> 2951: [$., $\n]; 2952: dotify([C | L]) -> 2953: [C | dotify(L)]; 2954: dotify([]) -> 2955: []. 2956: 2957: start_new_shell() -> 2958: Shell = shell:start(), 2959: link(Shell), 2960: Shell. 2961: 2962: start_new_shell(Node) -> 2963: Shell = rpc:call(Node,shell,start,[]), 2964: link(Shell), 2965: Shell. 2966: 2967: %% This is a very minimal implementation of the IO protocol... 2968: 2969: server_loop(S) -> 2970: receive 2971: {io_request, From, ReplyAs, Request} when is_pid(From) -> 2972: server_loop(do_io_request(Request, From, S, ReplyAs)); 2973: NotExpected -> 2974: exit(NotExpected) 2975: end. 2976: 2977: do_io_request(Req, From, S, ReplyAs) -> 2978: case io_requests([Req], [], S) of 2979: {_Status,{eof,_},S1} -> 2980: io_reply(From, ReplyAs, {error,terminated}), 2981: throw({?MODULE,S1#state.reply,S1#state.unic}); 2982: {_Status,Reply,S1} -> 2983: io_reply(From, ReplyAs, Reply), 2984: S1 2985: end. 2986: 2987: io_reply(From, ReplyAs, Reply) -> 2988: From ! {io_reply, ReplyAs, Reply}. 2989: 2990: io_requests([{requests, Rs1} | Rs], Cont, S) -> 2991: io_requests(Rs1, [Rs | Cont], S); 2992: io_requests([R | Rs], Cont, S) -> 2993: case io_request(R, S) of 2994: {ok, ok, S1} -> 2995: io_requests(Rs, Cont, S1); 2996: Reply -> 2997: Reply 2998: end; 2999: io_requests([], [Rs|Cont], S) -> 3000: io_requests(Rs, Cont, S); 3001: io_requests([], [], S) -> 3002: {ok,ok,S}. 3003: 3004: io_request({setopts, Opts}, S) -> 3005: #state{unic = OldEnc, bin = Bin} = S, 3006: NewEnc = case proplists:get_value(encoding, Opts) of 3007: undefined -> OldEnc; 3008: utf8 -> unicode; 3009: New -> New 3010: end, 3011: NewBin = case {OldEnc, NewEnc} of 3012: {E, E} -> Bin; 3013: {latin1, _} -> 3014: unicode:characters_to_binary(Bin, latin1, unicode); 3015: {_, latin1} -> 3016: unicode:characters_to_binary(Bin, unicode, latin1); 3017: {_, _} -> Bin 3018: end, 3019: {ok, ok, S#state{unic = NewEnc, bin = NewBin}}; 3020: io_request(getopts, S) -> 3021: {ok,[{encoding,S#state.unic}],S}; 3022: io_request({get_geometry,columns}, S) -> 3023: {ok,80,S}; 3024: io_request({get_geometry,rows}, S) -> 3025: {ok,24,S}; 3026: io_request({put_chars,Chars}, S) -> 3027: {ok,ok,S#state{reply = [S#state.reply | Chars]}}; 3028: io_request({put_chars,latin1,Chars}, S) -> 3029: {ok,ok,S#state{reply = [S#state.reply | Chars]}}; 3030: io_request({put_chars,unicode,Chars0}, S) -> 3031: Chars = unicode:characters_to_list(Chars0), 3032: {ok,ok,S#state{reply = [S#state.reply | Chars]}}; 3033: io_request({put_chars,Mod,Func,Args}, S) -> 3034: case catch apply(Mod, Func, Args) of 3035: Chars when is_list(Chars) -> 3036: io_request({put_chars,Chars}, S) 3037: end; 3038: io_request({put_chars,Enc,Mod,Func,Args}, S) -> 3039: case catch apply(Mod, Func, Args) of 3040: Chars when is_list(Chars) -> 3041: io_request({put_chars,Enc,Chars}, S) 3042: end; 3043: io_request({get_until,_Prompt,Mod,Func,ExtraArgs}, S) -> 3044: get_until(Mod, Func, ExtraArgs, S, latin1); 3045: io_request({get_until,Enc,_Prompt,Mod,Func,ExtraArgs}, S) -> 3046: get_until(Mod, Func, ExtraArgs, S, Enc). 3047: 3048: get_until(Mod, Func, ExtraArgs, S, Enc) -> 3049: get_until_loop(Mod, Func, ExtraArgs, S, {more,[]}, Enc). 3050: 3051: get_until_loop(M, F, As, S, {more,Cont}, Enc) -> 3052: Bin = S#state.bin, 3053: case byte_size(Bin) of 3054: 0 -> 3055: get_until_loop(M, F, As, S, 3056: catch apply(M, F, [Cont,eof|As]), Enc); 3057: _ when S#state.unic =:= latin1 -> 3058: get_until_loop(M, F, As, S#state{bin = <<>>}, 3059: catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc); 3060: _ -> 3061: get_until_loop(M, F, As, S#state{bin = <<>>}, 3062: catch apply(M, F, [Cont,unicode:characters_to_list(Bin)|As]), Enc) 3063: end; 3064: get_until_loop(_M, _F, _As, S, {done,Res,Buf}, Enc) -> 3065: {ok,Res,S#state{bin = buf2bin(Buf, Enc)}}; 3066: get_until_loop(_M, F, _As, S, _Other, _Enc) -> 3067: {error,{error,F},S}. 3068: 3069: buf2bin(eof,_) -> 3070: <<>>; 3071: buf2bin(Buf,latin1) -> 3072: list_to_binary(Buf); 3073: buf2bin(Buf,utf8) -> 3074: unicode:characters_to_binary(Buf,unicode,unicode); 3075: buf2bin(Buf,unicode) -> 3076: unicode:characters_to_binary(Buf,unicode,unicode). 3077: 3078: run_file(Config, Module, Test) -> 3079: FileName = filename(lists:concat([Module, ".erl"]), Config), 3080: BeamFile = filename(lists:concat([Module, ".beam"]), Config), 3081: LoadBeamFile = filename(Module, Config), 3082: ok = file:write_file(FileName, Test), 3083: ok = compile_file(Config, FileName, Test, []), 3084: code:purge(Module), 3085: {module, Module} = code:load_abs(LoadBeamFile), 3086: ok = Module:t(), 3087: file:delete(FileName), 3088: file:delete(BeamFile), 3089: ok. 3090: 3091: compile_file(Config, File, Test, Opts0) -> 3092: ?line Opts = [export_all,return,{outdir,?config(priv_dir, Config)}|Opts0], 3093: ?line ok = file:write_file(File, Test), 3094: ?line case compile:file(File, Opts) of 3095: {ok, _M, _Ws} -> ok; 3096: _ -> error 3097: end. 3098: 3099: filename(Name, Config) when is_atom(Name) -> 3100: filename(atom_to_list(Name), Config); 3101: filename(Name, Config) -> 3102: filename:join(?config(priv_dir, Config), Name). 3103: 3104: start_node(Name, Xargs) -> 3105: ?line N = test_server:start_node(Name, slave, [{args, " " ++ Xargs}]), 3106: global:sync(), 3107: N. 3108: