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.