1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1997-2012. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: -module(port_bif_SUITE). 21: 22: 23: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 24: init_per_group/2,end_per_group/2, command/1, 25: command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, 26: port_info1/1, port_info2/1, 27: port_info_os_pid/1, 28: connect/1, control/1, echo_to_busy/1]). 29: 30: -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). 31: 32: -export([init_per_testcase/2, end_per_testcase/2]). 33: 34: -include_lib("test_server/include/test_server.hrl"). 35: 36: suite() -> [{ct_hooks,[ts_install_cth]}]. 37: 38: all() -> 39: [command, {group, port_info}, connect, control, 40: echo_to_busy]. 41: 42: groups() -> 43: [{command_e, [], 44: [command_e_1, command_e_2, command_e_3, command_e_4]}, 45: {port_info, [], [port_info1, port_info2, port_info_os_pid]}]. 46: 47: init_per_suite(Config) -> 48: Config. 49: 50: end_per_suite(_Config) -> 51: ok. 52: 53: init_per_group(_GroupName, Config) -> 54: Config. 55: 56: end_per_group(_GroupName, Config) -> 57: Config. 58: 59: 60: 61: init_per_testcase(_Func, Config) when is_list(Config) -> 62: Dog=test_server:timetrap(test_server:minutes(10)), 63: [{watchdog, Dog}|Config]. 64: end_per_testcase(_Func, Config) when is_list(Config) -> 65: Dog=?config(watchdog, Config), 66: test_server:timetrap_cancel(Dog). 67: 68: command(Config) when is_list(Config) -> 69: load_control_drv(Config), 70: 71: P = open_port({spawn, control_drv}, []), 72: do_command(P, "hello"), 73: do_command(P, <<"hello">>), 74: do_command(P, sub_bin(<<"1234kalle">>)), 75: do_command(P, unaligned_sub_bin(<<"blurf">>)), 76: do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), 77: true = erlang:port_close(P), 78: ok. 79: 80: do_command(P, Data) -> 81: true = erlang:port_command(P, Data), 82: receive 83: {P,{data,Data}} -> 84: ok; 85: {P,{data,Data0}} -> 86: case {list_to_binary(Data0),list_to_binary([Data])} of 87: {B,B} -> ok; 88: _ -> test_server:fail({unexpected_data,Data0}) 89: end; 90: Other -> 91: test_server:fail({unexpected_message,Other}) 92: end. 93: 94: 95: 96: %% port_command/2: badarg 1st arg 97: command_e_1(Config) when is_list(Config) -> 98: DataDir = ?config(data_dir, Config), 99: Program = filename:join(DataDir, "port_test"), 100: 101: process_flag(trap_exit, true), 102: _ = spawn_link(?MODULE, do_command_e_1, [Program]), 103: receive 104: {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> 105: ok; 106: Other -> 107: test_server:fail(Other) 108: after 10000 -> 109: test_server:fail(timeout) 110: end, 111: ok. 112: 113: do_command_e_1(Program) -> 114: _ = open_port({spawn, Program}, []), 115: erlang:port_command(apple, "plock"), 116: exit(survived). 117: 118: %% port_command/2: badarg 2nd arg 119: command_e_2(Config) when is_list(Config) -> 120: DataDir = ?config(data_dir, Config), 121: Program = filename:join(DataDir, "port_test"), 122: 123: process_flag(trap_exit, true), 124: _ = spawn_link(?MODULE, do_command_e_2, [Program]), 125: receive 126: {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> 127: ok; 128: Other -> 129: test_server:fail(Other) 130: after 10000 -> 131: test_server:fail(timeout) 132: end, 133: ok. 134: 135: do_command_e_2(Program) -> 136: P = open_port({spawn, Program}, []), 137: erlang:port_command(P, 1), 138: exit(survived). 139: 140: %% port_command/2: Posix signals trapped 141: command_e_3(Config) when is_list(Config) -> 142: DataDir = ?config(data_dir, Config), 143: Program = filename:join(DataDir, "port_test"), 144: 145: process_flag(trap_exit, true), 146: P = open_port({spawn, Program}, [{packet, 1}]), 147: Data = lists:duplicate(257, $a), 148: erlang:port_command(P, Data), 149: receive 150: {'EXIT', Port, einval} when is_port(Port) -> 151: ok; 152: Other -> 153: test_server:fail(Other) 154: after 10000 -> 155: test_server:fail(timeout) 156: end, 157: ok. 158: 159: %% port_command/2: Posix exit signals not trapped 160: command_e_4(Config) when is_list(Config) -> 161: DataDir = ?config(data_dir, Config), 162: Program = filename:join(DataDir, "port_test"), 163: 164: process_flag(trap_exit, true), 165: _ = spawn_link(?MODULE, do_command_e_4, [Program]), 166: receive 167: {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> 168: ok; 169: Other -> 170: test_server:fail(Other) 171: after 10000 -> 172: test_server:fail(timeout) 173: end, 174: ok. 175: 176: do_command_e_4(Program) -> 177: P = open_port({spawn, Program}, [{packet, 1}]), 178: Data = lists:duplicate(257, $a), 179: erlang:port_command(P, Data), 180: exit(survived). 181: 182: 183: %% Tests the port_info/1 BIF 184: port_info1(Config) when is_list(Config) -> 185: load_control_drv(Config), 186: Me=self(), 187: P = open_port({spawn, control_drv}, []), 188: A1 = erlang:port_info(P), 189: false = lists:keysearch(registered_name, 1, A1), 190: register(myport, P), 191: A = erlang:port_info(P), 192: {value,{registered_name,myport}}= lists:keysearch(registered_name, 1, A), 193: {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), 194: {value,{links,[Me]}}=lists:keysearch(links, 1, A), 195: {value,{id,_IdNum}}=lists:keysearch(id, 1, A), 196: {value,{connected,_}}=lists:keysearch(connected, 1, A), 197: {value,{input,0}}=lists:keysearch(input, 1, A), 198: {value,{output,0}}=lists:keysearch(output, 1, A), 199: {value,{os_pid,undefined}}=lists:keysearch(os_pid, 1, A), % linked-in driver doesn't have a OS pid 200: true=erlang:port_close(P), 201: ok. 202: 203: %% Tests erlang:port_info/2" 204: port_info2(Config) when is_list(Config) -> 205: load_control_drv(Config), 206: 207: P = open_port({spawn,control_drv}, [binary]), 208: [] = erlang:port_info(P, registered_name), 209: register(myport, P), 210: {registered_name, myport} = erlang:port_info(P, registered_name), 211: 212: {name, "control_drv"}=erlang:port_info(P, name), 213: {id, _IdNum} = erlang:port_info(P, id), 214: Me=self(), 215: {links, [Me]} = erlang:port_info(P, links), 216: {connected, Me} = erlang:port_info(P, connected), 217: {input, 0}=erlang:port_info(P, input), 218: {output,0}=erlang:port_info(P, output), 219: {os_pid, undefined}=erlang:port_info(P, os_pid), % linked-in driver doesn't have a OS pid 220: 221: erlang:port_control(P, $i, "abc"), 222: receive 223: {P,{data,<<"abc">>}} -> ok 224: end, 225: {input,3} = erlang:port_info(P, input), 226: {output,0} = erlang:port_info(P, output), 227: 228: Bin = list_to_binary(lists:duplicate(2047, 42)), 229: output_test(P, Bin, 3, 0), 230: 231: true = erlang:port_close(P), 232: ok. 233: 234: %% Tests the port_info/1,2 os_pid option BIF 235: port_info_os_pid(Config) when is_list(Config) -> 236: case os:type() of 237: {unix,_} -> 238: do_port_info_os_pid(); 239: _ -> 240: {skip,"Only on Unix."} 241: end. 242: 243: do_port_info_os_pid() -> 244: P = open_port({spawn, "echo $$"}, [eof]), 245: A = erlang:port_info(P), 246: {os_pid, InfoOSPid} = erlang:port_info(P, os_pid), 247: EchoPidStr = receive 248: {P, {data, EchoPidStr0}} -> EchoPidStr0 249: after 10000 -> test_server:fail(timeout) 250: end, 251: {ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr), 252: {value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A), 253: EchoPid = InfoOSPid, 254: true = erlang:port_close(P), 255: ok. 256: 257: output_test(_, _, Input, Output) when Output > 16#1fffffff -> 258: io:format("~p bytes received\n", [Input]); 259: output_test(P, Bin, Input0, Output0) -> 260: erlang:port_command(P, Bin), 261: receive 262: {P,{data,Bin}} -> ok; 263: Other -> 264: io:format("~p", [Other]), 265: ?t:fail() 266: end, 267: Input = Input0 + size(Bin), 268: Output = Output0 + size(Bin), 269: {input,Input} = erlang:port_info(P, input), 270: {output,Output} = erlang:port_info(P, output), 271: 272: %% We can't test much here, but hopefully a debug-built emulator will crasch 273: %% if there is something wrong with the heap allocation. 274: case erlang:statistics(io) of 275: {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> 276: ok 277: end, 278: output_test(P, Bin, Input, Output). 279: 280: %% Tests the port_connect/2 BIF. 281: connect(Config) when is_list(Config) -> 282: load_control_drv(Config), 283: 284: P = open_port({spawn, control_drv}, []), 285: register(myport, P), 286: 287: true = erlang:port_connect(myport, self()), 288: 289: %% Connect the port to another process. 290: 291: Data = "hello, world", 292: Parent = self(), 293: Rec = fun(Me) -> 294: receive 295: {P,{data,Data}} -> 296: Parent ! connect_ok, 297: Me(Me) 298: end 299: end, 300: RecPid = spawn_link(fun() -> Rec(Rec) end), 301: true = erlang:port_connect(P, RecPid), 302: unlink(P), 303: 304: %% Send a command to the port and make sure that the 305: %% other process receives the echo. 306: 307: erlang:port_command(P, Data), 308: receive 309: connect_ok -> ok 310: end, 311: 312: %% Tests some errors. 313: 314: {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), 315: {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), 316: {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), 317: {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), 318: {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), 319: 320: process_flag(trap_exit, true), 321: exit(P, you_should_die), 322: receive 323: {'EXIT',RecPid,you_should_die} -> ok; 324: Other -> ?line ?t:fail({bad_message,Other}) 325: end, 326: 327: %% Done. 328: ok. 329: 330: %% Tests port_control/3 331: control(Config) when is_list(Config) -> 332: load_control_drv(Config), 333: P = open_port({spawn, control_drv}, []), 334: 335: %% Test invalid (out-of-range) arguments. 336: 337: {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), 338: 339: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), 340: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), 341: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), 342: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), 343: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), 344: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), 345: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), 346: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [fun(X) -> X end])), 347: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [1|fun(X) -> X end])), 348: 349: %% Test errors detected by the driver. 350: 351: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), 352: {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, random_packet(1024))), 353: 354: %% Test big op codes. 355: 356: register(myport, P), 357: test_op(myport, 256), 358: test_op(P, 256), 359: test_op(P, 16#0033A837), 360: test_op(P, 16#0ab37938), 361: test_op(P, 16#eab37938), 362: test_op(P, 16#ffffFFFF), 363: 364: %% Test the echo function of the driver. 365: 366: echo(P, 0), 367: echo(P, 1), 368: echo(P, 10), 369: echo(P, 13), 370: echo(P, 63), 371: echo(P, 64), 372: echo(P, 65), 373: echo(P, 127), 374: echo(P, 1023), 375: echo(P, 1024), 376: echo(P, 11243), 377: echo(P, 70000), 378: 379: %% Done. 380: 381: true = erlang:port_close(myport), 382: ok. 383: 384: test_op(P, Op) -> 385: R = port_control(P, Op, []), 386: <<Op:32>> = list_to_binary(R). 387: 388: echo_to_busy(Config) when is_list(Config) -> 389: Dog = test_server:timetrap(test_server:seconds(10)), 390: load_control_drv(Config), 391: P = open_port({spawn, control_drv}, []), 392: erlang:port_control(P, $b, [1]), % Set to busy. 393: Self = self(), 394: Echoer = spawn(fun() -> echoer(P, Self) end), 395: receive after 500 -> ok end, 396: erlang:port_control(P, $b, [0]), % Set to not busy. 397: receive 398: {Echoer, done} -> 399: ok; 400: {Echoer, Other} -> 401: test_server:fail(Other); 402: Other -> 403: test_server:fail({unexpected_message, Other}) 404: end, 405: test_server:timetrap_cancel(Dog), 406: ok. 407: 408: echoer(P, ReplyTo) -> 409: Msg = random_packet(73), 410: true = erlang:port_connect(P, self()), 411: erlang:port_command(P, Msg), 412: receive 413: {P, {data, Msg}} -> 414: ReplyTo ! {self(), done}; 415: Other -> 416: ReplyTo ! {self(), {bad_message, Other}} 417: end. 418: 419: echo(P, Size) -> 420: io:format("Echo test, size ~w", [Size]), 421: Packet = random_packet(Size), 422: Packet = erlang:port_control(P, $e, Packet), 423: Bin = list_to_binary(Packet), 424: Packet = erlang:port_control(P, $e, Bin), 425: Packet = erlang:port_control(P, $e, sub_bin(Bin)), 426: Packet = erlang:port_control(P, $e, unaligned_sub_bin(Bin)), 427: Packet = erlang:port_control(P, $e, [unaligned_sub_bin(Bin)]). 428: 429: load_control_drv(Config) when is_list(Config) -> 430: DataDir = ?config(data_dir, Config), 431: erl_ddll:start(), 432: ok = load_driver(DataDir, "control_drv"). 433: 434: load_driver(Dir, Driver) -> 435: case erl_ddll:load_driver(Dir, Driver) of 436: ok -> ok; 437: {error, Error} = Res -> 438: io:format("~s\n", [erl_ddll:format_error(Error)]), 439: Res 440: end. 441: 442: random_packet(Size) -> 443: random_packet(Size, "", random_char()). 444: 445: random_packet(0, Result, _NextChar) -> 446: Result; 447: random_packet(Left, Result, NextChar0) -> 448: NextChar = 449: if 450: NextChar0 >= 126 -> 451: 33; 452: true -> 453: NextChar0+1 454: end, 455: random_packet(Left-1, [NextChar0|Result], NextChar). 456: 457: random_char() -> 458: random_char("abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789"). 459: 460: random_char(Chars) -> 461: lists:nth(uniform(length(Chars)), Chars). 462: 463: uniform(N) -> 464: case get(random_seed) of 465: undefined -> 466: {X, Y, Z} = time(), 467: random:seed(X, Y, Z); 468: _ -> 469: ok 470: end, 471: random:uniform(N). 472: 473: unaligned_sub_bin(Bin0) -> 474: Bin1 = <<0:3,Bin0/binary,31:5>>, 475: Sz = size(Bin0), 476: <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), 477: Bin. 478: 479: sub_bin(Bin) when is_binary(Bin) -> 480: {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), 481: B. 482: 483: id(I) -> I.