1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1997-2013. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: -module(port_SUITE).
   21: 
   22: %%%
   23: %%% Author: Bjorn Gustavsson; iter_max_ports contributed by Peter Hogfeldt.
   24: %%%
   25: 
   26: %%
   27: %% There are a lot of things to test with open_port(Name, Settings).
   28: %%
   29: %%   Name can be
   30: %%
   31: %%       {spawn, Command}
   32: %%         which according to The Book and the manual page starts an
   33: %%         external program. That is not true. It might very well be
   34: %%         a linked-in program (the notion of 'linked-in driver' is
   35: %%         silly, since any driver is 'linked-in').
   36: %%	  [Spawn of external program is tested.]
   37: %%
   38: %%       Atom
   39: %%         Read all contents of Atom, or write to it.
   40: %%
   41: %%       {fd, In, Out}
   42: %%       Open file descriptors In and Out. [Not tested]
   43: %%
   44: %%   PortSettings can be
   45: %%
   46: %%       {packet, N}
   47: %%         N is 1, 2 or 4.
   48: %%
   49: %%       stream (default)
   50: %%         Without packet length.
   51: %%
   52: %%       use_stdio (default for spawned ports)
   53: %%         The spawned process use file descriptors 0 and 1 for I/O.
   54: %%
   55: %%       nouse_stdio					[Not tested]
   56: %%         Use filedescriptors 3 and 4.  This option is probably only
   57: %%	   meaningful on Unix.
   58: %%
   59: %%       in (default for Atom)
   60: %%         Input only (from Erlang's point of view).
   61: %%
   62: %%       out
   63: %%         Output only (from Erlang's point of view).
   64: %%
   65: %%       binary
   66: %%         The port is a binary port, i.e. messages received and sent
   67: %%         to a port are binaries.
   68: %%
   69: %%       eof
   70: %%         Port is not closed on eof and will not send an exit signal,
   71: %%         instead it will send a {Port, eof} to the controlling process
   72: %%         (output can still be sent to the port (??)).
   73: %%
   74: 
   75: 
   76: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, 
   77: 	 init_per_testcase/2, end_per_testcase/2,
   78: 	 init_per_suite/1, end_per_suite/1,
   79: 	 stream_small/1, stream_big/1,
   80: 	 basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1,
   81: 	 mul_basic/1, mul_slow_writes/1,
   82: 	 dying_port/1, port_program_with_path/1,
   83: 	 open_input_file_port/1, open_output_file_port/1,
   84: 	 iter_max_ports/1, eof/1, input_only/1, output_only/1,
   85: 	 name1/1,
   86: 	 t_binary/1, parallell/1, t_exit/1,
   87: 	 env/1, bad_env/1, cd/1, exit_status/1,
   88: 	 tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1,
   89: 	 otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1,
   90: 	 mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1,
   91: 	 exit_status_multi_scheduling_block/1, ports/1,
   92: 	 spawn_driver/1, spawn_executable/1, close_deaf_port/1,
   93: 	 unregister_name/1, parallelism_option/1]).
   94: 
   95: -export([do_iter_max_ports/2]).
   96: 
   97: %% Internal exports.
   98: -export([tps/3]).
   99: -export([otp_3906_forker/5, otp_3906_start_forker_starter/4]).
  100: -export([env_slave_main/1]).
  101: 
  102: -include_lib("test_server/include/test_server.hrl").
  103: -include_lib("kernel/include/file.hrl").
  104: 
  105: suite() -> [{ct_hooks,[ts_install_cth]}].
  106: 
  107: all() -> 
  108:     [otp_6224, {group, stream}, basic_ping, slow_writes,
  109:      bad_packet, bad_port_messages, {group, options},
  110:      {group, multiple_packets}, parallell, dying_port,
  111:      port_program_with_path, open_input_file_port,
  112:      open_output_file_port, name1, env, bad_env, cd,
  113:      exit_status, iter_max_ports, t_exit, {group, tps}, line,
  114:      stderr_to_stdout, otp_3906, otp_4389, win_massive,
  115:      mix_up_ports, otp_5112, otp_5119,
  116:      exit_status_multi_scheduling_block, ports, spawn_driver,
  117:      spawn_executable, close_deaf_port, unregister_name,
  118:      parallelism_option].
  119: 
  120: groups() -> 
  121:     [{stream, [], [stream_small, stream_big]},
  122:      {options, [], [t_binary, eof, input_only, output_only]},
  123:      {multiple_packets, [], [mul_basic, mul_slow_writes]},
  124:      {tps, [], [tps_16_bytes, tps_1K]}].
  125: 
  126: init_per_group(_GroupName, Config) ->
  127:     Config.
  128: 
  129: end_per_group(_GroupName, Config) ->
  130:     Config.
  131: 
  132: 
  133: -define(DEFAULT_TIMEOUT, ?t:minutes(5)).
  134: 
  135: init_per_testcase(Case, Config) ->
  136:     [{testcase, Case} |Config].
  137: 
  138: end_per_testcase(_Case, _Config) ->
  139:     ok.
  140: 
  141: init_per_suite(Config) when is_list(Config) ->
  142:     ignore_cores:init(Config).
  143: 
  144: end_per_suite(Config) when is_list(Config) ->
  145:     ignore_cores:fini(Config).
  146: 
  147: 
  148: -define(WIN_MASSIVE_PORT, 50000).
  149: 
  150: %% Tests that you can open a massive amount of ports (sockets)
  151: %% on a Windows machine given the correct environment.
  152: win_massive(Config) when is_list(Config) ->
  153:     case os:type() of
  154: 	{win32,_} ->
  155: 	    do_win_massive();
  156: 	_ ->
  157: 	    {skip,"Only on Windows."}
  158:     end.
  159: 
  160: do_win_massive() ->
  161:     Dog = test_server:timetrap(test_server:seconds(360)),
  162:     SuiteDir = filename:dirname(code:which(?MODULE)),
  163:     Ports = " +Q 8192",
  164:     {ok, Node} = 
  165: 	test_server:start_node(win_massive,
  166: 			       slave,
  167: 			       [{args, " -pa " ++ SuiteDir ++ Ports}]),
  168:     ok = rpc:call(Node,?MODULE,win_massive_client,[3000]),
  169:     test_server:stop_node(Node),
  170:     test_server:timetrap_cancel(Dog),
  171:     ok.
  172:     
  173: win_massive_client(N) ->
  174:     {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), 
  175:     L = win_massive_loop(P,N),
  176:     Len = length(L),
  177:     lists:foreach(fun(E) ->
  178: 			  gen_tcp:close(E)
  179: 		  end,
  180: 		  L),
  181:     case Len div 2 of
  182: 	N ->
  183: 	    ok;
  184: 	_Else ->
  185: 	    {too_few, Len}
  186:     end.
  187: 
  188: win_massive_loop(_,0) ->
  189:     [];
  190: win_massive_loop(P,N) ->
  191:     case (catch gen_tcp:connect("localhost",?WIN_MASSIVE_PORT,[])) of
  192: 	{ok,A} ->
  193: 	    case (catch gen_tcp:accept(P)) of
  194: 		{ok,B} ->
  195: 		    %erlang:display(N),
  196: 		    [A,B|win_massive_loop(P,N-1)];
  197: 		_Else ->
  198: 		    [A]
  199: 	    end;
  200: 	_Else0 ->
  201: 	    []
  202:     end.
  203: 	    
  204:     
  205: 
  206: 
  207: 
  208: %% Test that we can send a stream of bytes and get it back.
  209: %% We will send only a small amount of data, to avoid deadlock.
  210: 
  211: stream_small(Config) when is_list(Config) ->
  212:     Dog = test_server:timetrap(test_server:seconds(10)),
  213:     stream_ping(Config, 512, "", []),
  214:     stream_ping(Config, 1777, "", []),
  215:     stream_ping(Config, 1777, "-s512", []),
  216:     test_server:timetrap_cancel(Dog),
  217:     ok.
  218: 
  219: %% Send big amounts of data (much bigger than the buffer size in port test).
  220: %% This will deadlock the emulator if the spawn driver haven't proper
  221: %% non-blocking reads and writes.
  222: 
  223: stream_big(Config) when is_list(Config) ->
  224:     Dog = test_server:timetrap(test_server:seconds(180)),
  225:     stream_ping(Config, 43755, "", []),
  226:     stream_ping(Config, 100000, "", []),
  227:     stream_ping(Config, 77777, " -s40000", []),
  228:     test_server:timetrap_cancel(Dog),
  229:     ok.
  230: 
  231: %% Sends packet with header size of 1, 2, and 4, with packets of various
  232: %% sizes.
  233: 
  234: basic_ping(Config) when is_list(Config) ->
  235:     Dog = test_server:timetrap(test_server:seconds(120)),
  236:     ping(Config, sizes(1), 1, "", []),
  237:     ping(Config, sizes(2), 2, "", []),
  238:     ping(Config, sizes(4), 4, "", []),
  239:     test_server:timetrap_cancel(Dog),
  240:     ok.
  241: 
  242: %% Let the port program insert delays between characters sent back to
  243: %% Erlang, to test that the Erlang emulator can handle a packet coming in
  244: %% small chunks rather than all at once.
  245: 
  246: slow_writes(Config) when is_list(Config) ->
  247:     Dog = test_server:timetrap(test_server:seconds(20)),
  248:     ping(Config, [8], 4, "-s1", []),
  249:     ping(Config, [10], 2, "-s2", []),
  250:     test_server:timetrap_cancel(Dog),
  251:     ok.
  252: 
  253: bad_packet(doc) ->
  254:     ["Test that we get {'EXIT', Port, einval} if we try to send a bigger "
  255:      "packet than the packet header allows."];
  256: bad_packet(Config) when is_list(Config) ->
  257:     Dog = test_server:timetrap(test_server:seconds(10)),
  258:     PortTest = port_test(Config),
  259:     process_flag(trap_exit, true),
  260: 
  261:     bad_packet(PortTest, 1, 256),
  262:     bad_packet(PortTest, 1, 257),
  263:     bad_packet(PortTest, 2, 65536),
  264:     bad_packet(PortTest, 2, 65537),
  265: 
  266:     test_server:timetrap_cancel(Dog),
  267:     ok.
  268: 
  269: bad_packet(PortTest, HeaderSize, PacketSize) ->
  270:     P = open_port({spawn, PortTest}, [{packet, HeaderSize}]),
  271:     P ! {self(), {command, make_zero_packet(PacketSize)}},
  272:     receive
  273: 	{'EXIT', P, einval} -> ok;
  274: 	Other -> test_server:fail({unexpected_message, Other})
  275:     end.
  276: 
  277: make_zero_packet(0) -> [];
  278: make_zero_packet(N) when N rem 2 == 0 ->
  279:     P = make_zero_packet(N div 2),
  280:     [P|P];
  281: make_zero_packet(N) ->
  282:     P = make_zero_packet(N div 2),
  283:     [0, P|P].
  284: 
  285: %% Test sending bad messages to a port.
  286: bad_port_messages(Config) when is_list(Config) ->
  287:     Dog = test_server:timetrap(test_server:seconds(10)),
  288:     PortTest = port_test(Config),
  289:     process_flag(trap_exit, true),
  290: 
  291:     bad_message(PortTest, {a,b}),
  292:     bad_message(PortTest, {a}),
  293:     bad_message(PortTest, {self(),{command,bad_command}}),
  294:     bad_message(PortTest, {self(),{connect,no_pid}}),
  295: 
  296:     test_server:timetrap_cancel(Dog),
  297:     ok.
  298: 
  299: bad_message(PortTest, Message) ->    
  300:     P = open_port({spawn,PortTest}, []),
  301:     P ! Message,
  302:     receive
  303: 	{'EXIT',P,badsig} -> ok;
  304: 	Other -> test_server:fail({unexpected_message, Other})
  305:     end.
  306: 
  307: %% Tests various options (stream and {packet, Number} are implicitly
  308: %% tested in other test cases).
  309: 
  310: 
  311: %% Tests the 'binary' option for a port.
  312: 
  313: t_binary(Config) when is_list(Config) ->
  314:     Dog = test_server:timetrap(test_server:seconds(300)),
  315: 
  316:     %% Packet mode.
  317:     ping(Config, sizes(1), 1, "", [binary]),
  318:     ping(Config, sizes(2), 2, "", [binary]),
  319:     ping(Config, sizes(4), 4, "", [binary]),
  320: 
  321:     %% Stream mode.
  322:     stream_ping(Config, 435, "", [binary]),
  323:     stream_ping(Config, 43755, "", [binary]),
  324:     stream_ping(Config, 100000, "", [binary]),
  325: 
  326:     test_server:timetrap_cancel(Dog),
  327:     ok.
  328: 
  329: name1(Config) when is_list(Config) ->
  330:     Dog = test_server:timetrap(test_server:seconds(100)),
  331:     PortTest = port_test(Config),
  332:     Command = lists:concat([PortTest, " "]),
  333:     P = open_port({spawn, Command}, []),
  334:     register(myport, P),
  335:     P = whereis(myport),
  336:     Text = "hej",
  337:     myport ! {self(), {command, Text}},
  338:     receive
  339:         {P, {data, Text}} ->
  340:             ok
  341:     end,
  342:     myport ! {self(), close},
  343:     receive
  344:         {P, closed} -> ok
  345:     end,
  346:     undefined = whereis(myport),
  347:     test_server:timetrap_cancel(Dog),
  348:     ok.
  349: 
  350: %% Test that the 'eof' option works.
  351: 
  352: eof(Config) when is_list(Config) ->
  353:     Dog = test_server:timetrap(test_server:seconds(100)),
  354:     PortTest = port_test(Config),
  355:     Command = lists:concat([PortTest, " -h0 -q"]),
  356:     P = open_port({spawn, Command}, [eof]),
  357:     receive
  358:         {P, eof} ->
  359:             ok
  360:     end,
  361:     P ! {self(), close},
  362:     receive
  363:         {P, closed} -> ok
  364:     end,
  365:     test_server:timetrap_cancel(Dog),
  366:     ok.
  367: 
  368: %% Tests that the 'in' option for a port works.
  369: 
  370: input_only(Config) when is_list(Config) ->
  371:     Dog = test_server:timetrap(test_server:seconds(300)),
  372:     expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]),
  373:     expect_input(Config, [0, 1, 255, 2048], 2, "", [in]),
  374:     expect_input(Config, [0, 1, 255, 2048], 4, "", [in]),
  375:     expect_input(Config, [0, 1, 10, 13, 127, 128, 255],
  376:                  1, "", [in, binary]),
  377:     test_server:timetrap_cancel(Dog),
  378:     ok.
  379: 
  380: %% Tests that the 'out' option for a port works.
  381: 
  382: output_only(Config) when is_list(Config) ->
  383:     Dog = test_server:timetrap(test_server:seconds(100)),
  384:     Dir = ?config(priv_dir, Config),
  385:     Filename = filename:join(Dir, "output_only_stream"),
  386:     output_and_verify(Config, Filename, "-h0",
  387:           	    random_packet(35777, "echo")),
  388:     test_server:timetrap_cancel(Dog),
  389:     ok.
  390: 
  391: output_and_verify(Config, Filename, Options, Data) ->
  392:     PortTest = port_test(Config),
  393:     Command = lists:concat([PortTest, " ",
  394:           		  Options, " -o", Filename]),
  395:     Port = open_port({spawn, Command}, [out]),
  396:     Port ! {self(), {command, Data}},
  397:     Port ! {self(), close},
  398:     receive
  399:         {Port, closed} -> ok
  400:     end,
  401:     Wait_time = 500,
  402:     test_server:sleep(Wait_time),
  403:     {ok, Written} = file:read_file(Filename),
  404:     Data = binary_to_list(Written),
  405:     ok.
  406: 
  407: %% Test that receiving several packages written in the same
  408: %% write operation works.
  409: 
  410: 
  411: %% Basic test of receiving multiple packages, written in
  412: %% one operation by the other end.
  413: mul_basic(Config) when is_list(Config) ->
  414:     Dog = test_server:timetrap(test_server:seconds(600)),
  415:     expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
  416:     expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []),
  417:     expect_input(Config, [10, 70000], 4, "", []),
  418:     test_server:timetrap_cancel(Dog),
  419:     ok.
  420: 
  421: %% Test reading a buffer consisting of several packets, some
  422: %% of which might be incomplete.  (The port program builds
  423: %% a buffer with several packets, but writes it in chunks with
  424: %% delays in between.)
  425: 
  426: mul_slow_writes(Config) when is_list(Config) ->
  427:     Dog = test_server:timetrap(test_server:seconds(250)),
  428:     expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []),
  429:     test_server:timetrap_cancel(Dog),
  430:     ok.
  431: 
  432: %% Runs several port tests in parallell.  Each individual test
  433: %% finishes in about 5 seconds.  Running in parallell, all tests
  434: %% should also finish in about 5 seconds.
  435: 
  436: parallell(Config) when is_list(Config) ->
  437:     Dog = test_server:timetrap(test_server:seconds(300)),
  438:     Testers = [
  439: 	fun() -> stream_ping(Config, 1007, "-s100", []) end,
  440: 	fun() -> stream_ping(Config, 10007, "-s1000", []) end,
  441: 	fun() -> stream_ping(Config, 10007, "-s1000", []) end,
  442: 
  443: 	fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1,
  444: 		    "-s10", [in]) end,
  445: 
  446: 	fun() -> ping(Config, [10], 1, "-d", []) end,
  447: 	fun() -> ping(Config, [20000], 2, "-d", []) end,
  448: 	fun() -> ping(Config, [101], 1, "-s10", []) end,
  449: 	fun() -> ping(Config, [1001], 2, "-s100", []) end,
  450: 	fun() -> ping(Config, [10001], 4, "-s1000", []) end,
  451: 
  452: 	fun() -> ping(Config, [501, 501], 2, "-s100", []) end,
  453: 	fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end],
  454:     process_flag(trap_exit, true),
  455:     Pids = lists:map(fun fun_spawn/1, Testers),
  456:     wait_for(Pids),
  457:     test_server:timetrap_cancel(Dog),
  458:     ok.
  459: 
  460: wait_for([]) ->
  461:     ok;
  462: wait_for(Pids) ->
  463:     io:format("Waiting for ~p", [Pids]),
  464:     receive
  465: 	{'EXIT', Pid, normal} ->
  466: 	    wait_for(lists:delete(Pid, Pids));
  467: 	Other ->
  468: 	    test_server:fail({bad_exit, Other})
  469:     end.
  470: 
  471: %% Tests starting port programs that terminate by themselves.
  472: %% This used to cause problems on Windows.
  473: 
  474: dying_port(suite) -> [];
  475: dying_port(Config) when is_list(Config) ->
  476:     Dog = test_server:timetrap(test_server:seconds(150)),
  477:     process_flag(trap_exit, true),
  478: 
  479:     P1 = make_dying_port(Config),
  480:     P2 = make_dying_port(Config),
  481:     P3 = make_dying_port(Config),
  482:     P4 = make_dying_port(Config),
  483:     P5 = make_dying_port(Config),
  484: 
  485:     %% This should be big enough to be sure to block in the write.
  486:     Garbage = random_packet(16384),
  487: 
  488:     P1 ! {self(), {command, Garbage}},
  489:     P3 ! {self(), {command, Garbage}},
  490:     P5 ! {self(), {command, Garbage}},
  491: 
  492:     wait_for_port_exit(P1),
  493:     wait_for_port_exit(P2),
  494:     wait_for_port_exit(P3),
  495:     wait_for_port_exit(P4),
  496:     wait_for_port_exit(P5),
  497: 
  498:     test_server:timetrap_cancel(Dog),
  499:     ok.
  500: 
  501: wait_for_port_exit(Port) ->
  502:     receive
  503: 	{'EXIT', Port, _} ->
  504: 	    ok
  505:     end.
  506: 
  507: make_dying_port(Config) when is_list(Config) ->
  508:     PortTest = port_test(Config),
  509:     Command = lists:concat([PortTest, " -h0 -d -q"]),
  510:     open_port({spawn, Command}, [stream]).
  511: 
  512: %% Tests that port program with complete path (but without any
  513: %% .exe extension) can be started, even if there is a file with
  514: %% the same name but without the extension in the same directory.
  515: %% (In practice, the file with the same name could be a Unix
  516: %% executable.)
  517: %%
  518: %% This used to failed on Windows (the .exe extension had to be
  519: %% explicitly given).
  520: %%
  521: %% This testcase works on Unix, but is not very useful.
  522: 
  523: port_program_with_path(suite) -> [];
  524: port_program_with_path(Config) when is_list(Config) ->
  525:     Dog = test_server:timetrap(test_server:seconds(100)),
  526:     DataDir = ?config(data_dir, Config),
  527:     PrivDir = ?config(priv_dir, Config),
  528:     
  529:     %% Create a copy of the port test program in a directory not
  530:     %% included in PATH (i.e. in priv_dir), with the name 'my_port_test.exe'.
  531:     %% Also, place a file named 'my_port_test' in the same directory.
  532:     %% This used to confuse the CreateProcess() call in spawn driver.
  533:     %% (On Unix, there will be a single file created, which will be
  534:     %% a copy of the port program.)
  535:     
  536:     PortTest = os:find_executable("port_test", DataDir),
  537:     io:format("os:find_executable(~p, ~p) returned ~p",
  538: 	      ["port_test", DataDir, PortTest]),
  539:     {ok, PortTestPgm} = file:read_file(PortTest),
  540:     NewName = filename:join(PrivDir, filename:basename(PortTest)),
  541:     RedHerring = filename:rootname(NewName),
  542:     ok = file:write_file(RedHerring, "I'm just here to confuse.\n"),
  543:     ok = file:write_file(NewName, PortTestPgm),
  544:     ok = file:write_file_info(NewName, #file_info{mode=8#111}),
  545:     PgmWithPathAndNoExt = filename:rootname(NewName),
  546:     
  547:     %% Open the port using the path to the copied port test program,
  548:     %% but without the .exe extension, and verified that it was started.
  549:     %%
  550:     %% If the bug is present the open_port call will fail with badarg.
  551:     
  552:     Command = lists:concat([PgmWithPathAndNoExt, " -h2"]),
  553:     P = open_port({spawn, Command}, [{packet, 2}]),
  554:     Message = "echo back to me",
  555:     P ! {self(), {command, Message}},
  556:     receive
  557:         {P, {data, Message}} ->
  558:             ok
  559:     end,
  560:     test_server:timetrap_cancel(Dog),
  561:     ok.
  562: 
  563: 
  564: %% Tests that files can be read using open_port(Filename, [in]).
  565: %% This used to fail on Windows.
  566: open_input_file_port(suite) -> [];
  567: open_input_file_port(Config) when is_list(Config) ->
  568:     Dog = test_server:timetrap(test_server:seconds(10)),
  569:     PrivDir = ?config(priv_dir, Config),
  570:     
  571:     %% Create a file with the file driver and read it back using
  572:     %% open_port/2.
  573: 
  574:     MyFile1 = filename:join(PrivDir, "my_input_file"),
  575:     FileData1 = "An input file",
  576:     ok = file:write_file(MyFile1, FileData1),
  577:     case open_port(MyFile1, [in]) of
  578: 	InputPort when is_port(InputPort) ->
  579: 	    receive
  580: 		{InputPort, {data, FileData1}} ->
  581: 		    ok
  582: 	    end
  583:     end,
  584:     test_server:timetrap_cancel(Dog),
  585:     ok.
  586: 
  587: %% Tests that files can be written using open_port(Filename, [out]).
  588: open_output_file_port(suite) -> [];
  589: open_output_file_port(Config) when is_list(Config) ->
  590:     Dog = test_server:timetrap(test_server:seconds(100)),
  591:     PrivDir = ?config(priv_dir, Config),
  592: 
  593:     %% Create a file with open_port/2 and read it back with
  594:     %% the file driver.
  595: 
  596:     MyFile2 = filename:join(PrivDir, "my_output_file"),
  597:     FileData2_0 = "A file created ",
  598:     FileData2_1 = "with open_port/2.\n",
  599:     FileData2 = FileData2_0 ++ FileData2_1,
  600:     OutputPort = open_port(MyFile2, [out]),
  601:     OutputPort ! {self(), {command, FileData2_0}},
  602:     OutputPort ! {self(), {command, FileData2_1}},
  603:     OutputPort ! {self(), close},
  604:     {ok, Bin} = file:read_file(MyFile2),
  605:     FileData2 = binary_to_list(Bin),
  606: 
  607:     test_server:timetrap_cancel(Dog),
  608:     ok.
  609: 
  610: %%
  611: %% Open as many ports as possible. Do this several times and check
  612: %% that we get the same number of ports every time.
  613: %%
  614: 
  615: iter_max_ports(suite) -> [];
  616: iter_max_ports(Config) when is_list(Config) ->
  617:     %% The child_setup program might dump core if we get out of memory.
  618:     %% This is hard to do anything about and is harmless. We run this test
  619:     %% in a working directory with an ignore_core_files file which will make
  620:     %% the search for core files ignore cores generated by this test.
  621:     %%
  622:     Config2 = ignore_cores:setup(?MODULE, iter_max_ports, Config, true),
  623:     try
  624: 	iter_max_ports_test(Config2)
  625:     after
  626: 	ignore_cores:restore(Config2)
  627:     end.
  628:     
  629:     
  630: iter_max_ports_test(Config) ->
  631:     Dog = test_server:timetrap(test_server:minutes(30)),
  632:     PortTest = port_test(Config),
  633:     Command = lists:concat([PortTest, " -h0 -q"]),
  634:     Iters = case os:type() of
  635: 		      {win32,_} -> 4;
  636: 		      _ -> 10
  637: 		  end,
  638:     %% Run on a different node in order to limit the effect if this test fails.
  639:     Dir = filename:dirname(code:which(?MODULE)),
  640:     {ok,Node} = test_server:start_node(test_iter_max_socks,slave,
  641: 				       [{args,"+Q 2048 -pa " ++ Dir}]),
  642:     L = rpc:call(Node,?MODULE,do_iter_max_ports,[Iters, Command]),
  643:     test_server:stop_node(Node),
  644: 
  645:     io:format("Result: ~p",[L]),
  646:     all_equal(L),
  647:     all_equal(L),
  648:     test_server:timetrap_cancel(Dog),
  649:     {comment, "Max ports: " ++ integer_to_list(hd(L))}.
  650: 
  651: do_iter_max_ports(N, Command) when N > 0 ->
  652:     [max_ports(Command)| do_iter_max_ports(N-1, Command)];
  653: do_iter_max_ports(_, _) ->
  654:     [].
  655: 
  656: all_equal([E,E|T]) ->
  657:     all_equal([E|T]);
  658: all_equal([_]) -> ok;
  659: all_equal([]) -> ok.
  660: 
  661: max_ports(Command) ->
  662:     test_server:sleep(500),
  663:     Ps = open_ports({spawn, Command}, [eof]),
  664:     N = length(Ps),
  665:     close_ports(Ps),
  666:     io:format("Got ~p ports\n",[N]),
  667:     N.
  668: 
  669: close_ports([P|Ps]) ->
  670:     P ! {self(), close},
  671:     receive
  672: 	{P,closed} ->
  673: 	    ok
  674:     end,
  675:     close_ports(Ps);
  676: close_ports([]) ->
  677:     ok.
  678: 
  679: open_ports(Name, Settings) ->
  680:     test_server:sleep(5),
  681:     case catch open_port(Name, Settings) of
  682: 	P when is_port(P) ->
  683: 	    [P| open_ports(Name, Settings)];
  684: 	{'EXIT', {Code, _}} ->
  685: 	    case Code of
  686: 		enfile ->
  687: 		    [];
  688: 		emfile ->
  689: 		    [];
  690: 		system_limit ->
  691: 		    [];
  692: 		enomem ->
  693: 		    [];
  694: 		Other ->
  695: 		    test_server:fail({open_ports, Other})
  696: 	    end;
  697: 	Other ->
  698: 	    test_server:fail({open_ports, Other})
  699:     end.
  700: 
  701: %% Tests that exit(Port, Term) works (has been known to crash the emulator).
  702: 
  703: t_exit(suite) -> [];
  704: t_exit(Config) when is_list(Config) ->
  705:     process_flag(trap_exit, true),
  706:     Pid = fun_spawn(fun suicide_port/1, [Config]),
  707:     receive
  708: 	{'EXIT', Pid, die} ->
  709: 	    ok;
  710: 	Other ->
  711: 	    test_server:fail({bad_message, Other})
  712:     end.
  713: 
  714: suicide_port(Config) when is_list(Config) ->
  715:     Port = port_expect(Config, [], 0, "", []),
  716:     exit(Port, die),
  717:     receive after infinity -> ok end.
  718: 
  719: 
  720: tps_16_bytes(doc) -> "";
  721: tps_16_bytes(suite) -> [];
  722: tps_16_bytes(Config) when is_list(Config) ->
  723:     tps(16, Config).
  724: 
  725: tps_1K(doc) -> "";
  726: tps_1K(suite) -> [];
  727: tps_1K(Config) when is_list(Config) ->
  728:     tps(1024, Config).
  729: 
  730: tps(Size, Config) ->
  731:     Dog = test_server:timetrap(test_server:seconds(300)),
  732:     PortTest = port_test(Config),
  733:     Packet = list_to_binary(random_packet(Size, "e")),
  734:     Port = open_port({spawn, PortTest}, [binary, {packet, 2}]),
  735:     Transactions = 10000,
  736:     {Elapsed, ok} = test_server:timecall(?MODULE, tps,
  737: 					       [Port, Packet, Transactions]),
  738:     test_server:timetrap_cancel(Dog),
  739:     {comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}.
  740: 
  741: tps(_Port, _Packet, 0) -> ok;
  742: tps(Port, Packet, N) ->
  743:     port_command(Port, Packet),
  744:     receive
  745: 	{Port, {data, Packet}} ->
  746: 	    tps(Port, Packet, N-1);
  747: 	Other ->
  748: 	    test_server:fail({bad_message, Other})
  749:     end.
  750: 
  751: %% Line I/O test
  752: line(Config) when is_list(Config) ->
  753:     Siz = 110,
  754:     Dog = test_server:timetrap(test_server:seconds(300)),
  755:     Packet1 = random_packet(Siz),
  756:     Packet2 = random_packet(Siz div 2),
  757:     %% Test that packets are split into lines
  758:     port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
  759: 					     io_lib:nl()]),
  760: 			       [{eol, Packet1}, {eol, Packet2}]}],
  761: 		      0, "", [{line,Siz}]),
  762:     %% Test the same for binaries
  763:     port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
  764: 					     io_lib:nl()]),
  765: 			       [{eol, Packet1}, {eol, Packet2}]}],
  766: 		      0, "", [{line,Siz},binary]),
  767:     %% Test that too long lines get split
  768:     port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1,
  769: 					     Packet2, io_lib:nl()]),
  770: 			       [{eol, Packet1}, {noeol, Packet1},
  771: 				{eol, Packet2}]}], 0, "", [{line,Siz}]),
  772:     %% Test that last output from closing port program gets received.
  773:     L1 = lists:append([Packet1, io_lib:nl(), Packet2]),
  774:     S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])),
  775:     io:format("S1 = ~w, L1 = ~w~n", [S1,L1]),
  776:     port_expect(Config,[{L1,
  777: 			       [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, 
  778: 		      S1, [{line,Siz},eof]),
  779:     %% Test that lonely <CR> Don't get treated as newlines
  780:     port_expect(Config,[{lists:append([Packet1, [13], Packet2,
  781: 					     io_lib:nl()]),
  782: 			       [{noeol, Packet1}, {eol, [13 |Packet2]}]}],
  783: 		      0, "", [{line,Siz}]),
  784:     %% Test that packets get built up to lines (delayed output from
  785:     %% port program)
  786:     port_expect(Config,[{Packet2,[]},
  787: 			      {lists:append([Packet2, io_lib:nl(),
  788: 					     Packet1, io_lib:nl()]),
  789: 			       [{eol, lists:append(Packet2, Packet2)},
  790: 				{eol, Packet1}]}], 0, "-d", [{line,Siz}]),
  791:     %% Test that we get badarg if trying both packet and line
  792:     bad_argument(Config, [{packet, 5}, {line, 5}]),
  793:     test_server:timetrap_cancel(Dog),
  794:     ok.
  795: 
  796: %%% Redirection of stderr test
  797: stderr_to_stdout(suite) ->
  798:     [];
  799: stderr_to_stdout(doc) ->
  800:     "Test that redirection of standard error to standard output works.";
  801: stderr_to_stdout(Config) when is_list(Config) ->
  802:     Dog = test_server:timetrap(test_server:seconds(60)),
  803:     %% See that it works
  804:     Packet = random_packet(10),
  805:     port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10",
  806: 		      [stderr_to_stdout]),
  807:     %% stream_ping(Config, 10, "-e", [stderr_to_stdout]),
  808:     %% See that it doesn't always happen (will generate garbage on stderr)
  809:     port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]),
  810:     test_server:timetrap_cancel(Dog),
  811:     ok.
  812: 
  813: 
  814: bad_argument(Config, ArgList) ->
  815:     PortTest = port_test(Config),
  816:     case catch open_port({spawn, PortTest}, ArgList) of
  817: 	{'EXIT', {badarg, _}} ->
  818: 	    ok
  819:     end.
  820:     
  821: 
  822: %% 'env' option
  823: %% (Can perhaps be made smaller by calling the other utility functions
  824: %% in this module.)
  825: env(suite) ->
  826:     [];
  827: env(doc) ->
  828:     ["Test that the 'env' option works"];
  829: env(Config)  when is_list(Config) ->
  830:     Dog = test_server:timetrap(test_server:seconds(60)),
  831:     Priv = ?config(priv_dir, Config),
  832:     Temp = filename:join(Priv, "env_fun.bin"),
  833: 
  834:     PluppVal = "dirty monkey",
  835:     env_slave(Temp, [{"plupp",PluppVal}]),
  836: 
  837:     Long = "LongAndBoringEnvName",
  838:     os:putenv(Long, "nisse"),
  839: 
  840:     env_slave(Temp, [{"plupp",PluppVal},
  841: 	    {"DIR_PLUPP","###glurfrik"}],
  842: 	fun() ->
  843: 		PluppVal = os:getenv("plupp"),
  844: 		"###glurfrik" = os:getenv("DIR_PLUPP"),
  845: 		"nisse" = os:getenv(Long)
  846: 	end),
  847: 
  848: 
  849:     env_slave(Temp, [{"must_define_something","some_value"},
  850: 	    {"certainly_not_existing",false},
  851: 	    {"ends_with_equal", "value="},
  852: 	    {Long,false},
  853: 	    {"glurf","a glorfy string"}]),
  854: 
  855:     %% A lot of non existing variables (mingled with existing)
  856:     NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} 
  857:                         ||  X <- lists:seq(1,150)],
  858:     ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} 
  859:                         ||  X <- lists:seq(1,150)],
  860:     env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)),
  861: 
  862:     test_server:timetrap_cancel(Dog),
  863:     ok.
  864: 
  865: env_slave(File, Env) ->
  866:     F = fun() ->
  867: 		lists:foreach(fun({Name,Val}) ->
  868: 				      Val = os:getenv(Name)
  869: 			      end, Env)
  870: 	end,
  871:     env_slave(File, Env, F).
  872: 
  873: env_slave(File, Env, Body) ->
  874:     file:write_file(File, term_to_binary(Body)),
  875:     Program = atom_to_list(lib:progname()),
  876:     Dir = filename:dirname(code:which(?MODULE)),
  877:     Cmd = Program ++ " -pz " ++ Dir ++
  878: 	" -noinput -run " ++ ?MODULE_STRING ++ " env_slave_main " ++
  879: 	File ++ " -run erlang halt",
  880:     Port = open_port({spawn, Cmd}, [{env,Env},{line,256}]),
  881:     receive
  882: 	{Port,{data,{eol,"ok"}}} ->
  883: 	    ok;
  884: 	{Port,{data,{eol,Error}}} ->
  885: 	    io:format("~p\n", [Error]),
  886: 	    test_server:fail();
  887: 	Other ->
  888: 	    test_server:fail(Other)
  889:     end.
  890: 
  891: env_slave_main([File]) ->
  892:     {ok,Body0} = file:read_file(File),
  893:     Body = binary_to_term(Body0),
  894:     case Body() of
  895: 	{'EXIT',Reason} ->
  896: 	    io:format("Error: ~p\n", [Reason]);
  897: 	_ ->
  898: 	    io:format("ok\n")
  899:     end,
  900:     init:stop().
  901: 
  902: 
  903: %% 'env' option
  904: %%   Test bad environments.
  905: bad_env(Config) when is_list(Config) ->
  906:     try_bad_env([abbb]),
  907:     try_bad_env([{"key","value"}|{"another","value"}]),
  908:     try_bad_env([{"key","value","value2"}]),
  909:     try_bad_env([{"key",[a,b,c]}]),
  910:     try_bad_env([{"key",value}]),
  911:     try_bad_env({a,tuple}),
  912:     try_bad_env(42),
  913:     try_bad_env([a|b]),
  914:     try_bad_env(self()),
  915:     ok.
  916: 
  917: try_bad_env(Env) ->
  918:     try open_port({spawn,"ls"}, [{env,Env}])
  919:     catch
  920: 	error:badarg -> ok
  921:     end.
  922: 
  923: %% 'cd' option
  924: %% (Can perhaps be made smaller by calling the other utility functions
  925: %% in this module.)
  926: cd(suite) ->
  927:     [];
  928: cd(doc) ->
  929:     ["Test that the 'cd' option works"];
  930: cd(Config)  when is_list(Config) ->
  931:     Dog = test_server:timetrap(test_server:seconds(60)),
  932: 
  933:     Program = atom_to_list(lib:progname()),
  934:     DataDir = ?config(data_dir, Config),
  935:     TestDir = filename:join(DataDir, "dir"),
  936:     Cmd = Program ++ " -pz " ++ DataDir ++
  937:         " -noshell -s port_test pwd -s erlang halt",
  938:     _ = open_port({spawn, Cmd},
  939: 	[{cd, TestDir},
  940: 	    {line, 256}]),
  941:     receive
  942: 	{_, {data, {eol, String}}} ->
  943: 	    case filename_equal(String, TestDir) of
  944: 		true ->
  945: 		    ok;
  946: 		false ->
  947: 		    test_server:fail({cd, String})
  948: 	    end;
  949: 	Other2 ->
  950: 	    test_server:fail({env, Other2})
  951:     end,
  952: 
  953:     test_server:timetrap_cancel(Dog),
  954:     ok.
  955: 
  956: filename_equal(A, B) ->
  957:     case os:type() of
  958: 	{win32, _} ->
  959: 	    win_filename_equal(A, B);
  960: 	_ ->
  961: 	    A == B
  962:     end.
  963: 
  964: win_filename_equal([], []) ->
  965:     true;
  966: win_filename_equal([], _) ->
  967:     false;
  968: win_filename_equal(_, []) ->
  969:     false;
  970: win_filename_equal([C1 | Rest1], [C2 | Rest2]) ->
  971:     case tolower(C1) == tolower(C2) of
  972: 	true ->
  973: 	    win_filename_equal(Rest1, Rest2);
  974: 	false ->
  975: 	    false
  976:     end.
  977: 
  978: tolower(C) when C >= $A, C =< $Z ->
  979:     C + 32;
  980: tolower(C) ->
  981:     C.
  982: 
  983: otp_3906(suite) ->
  984:     [];
  985: otp_3906(doc) ->
  986:     ["Tests that child process deaths are managed correctly when there are "
  987:      " a large amount of concurrently dying children. See ticket OTP-3906."];
  988: otp_3906(Config)  when is_list(Config) ->
  989:     case os:type() of
  990: 	{unix, OSName} ->
  991: 	    otp_3906(Config, OSName);
  992: 	_ ->
  993: 	    {skipped, "Only run on Unix systems"}
  994:     end.
  995: 
  996: -define(OTP_3906_CHILDREN,     1000).
  997: -define(OTP_3906_EXIT_STATUS,  17).
  998: -define(OTP_3906_PROGNAME,     "otp_3906").
  999: -define(OTP_3906_TICK_TIMEOUT, 5000).
 1000: -define(OTP_3906_OSP_P_ERLP,   10).
 1001: -define(OTP_3906_MAX_CONC_OSP, 50).
 1002: 
 1003: otp_3906(Config, OSName) ->
 1004:     DataDir = filename:dirname(proplists:get_value(data_dir,Config)),
 1005:     {ok, Variables} = file:consult(
 1006: 			      filename:join([DataDir,"..","..",
 1007: 					     "test_server","variables"])),
 1008:     case lists:keysearch('CC', 1, Variables) of
 1009: 	{value,{'CC', CC}} ->
 1010: 	    SuiteDir = filename:dirname(code:which(?MODULE)),
 1011: 	    PrivDir = ?config(priv_dir, Config),
 1012: 	    Prog = otp_3906_make_prog(CC, PrivDir),
 1013: 	    {ok, Node} = test_server:start_node(otp_3906,
 1014: 						slave,
 1015: 						[{args, " -pa " ++ SuiteDir},
 1016: 						 {linked, false}]),
 1017: 	    OP = process_flag(priority, max),
 1018: 	    OTE = process_flag(trap_exit, true),
 1019: 	    FS = spawn_link(Node,
 1020: 			    ?MODULE,
 1021: 			    otp_3906_start_forker_starter,
 1022: 			    [?OTP_3906_CHILDREN, [], self(), Prog]),
 1023: 	    Result = receive
 1024: 			 {'EXIT', _ForkerStarter, Reason} ->
 1025: 			     {failed, Reason};
 1026: 			 {emulator_pid, EmPid} ->
 1027: 			     case otp_3906_wait_result(FS, 0, 0) of
 1028: 				 {succeded,
 1029: 				  ?OTP_3906_CHILDREN,
 1030: 				  ?OTP_3906_CHILDREN} ->
 1031: 				     succeded;
 1032: 				 {succeded, Forked, Exited} ->
 1033: 				     otp_3906_list_defunct(EmPid, OSName),
 1034: 				     {failed,
 1035: 				      {mismatch,
 1036: 				       {forked, Forked},
 1037: 				       {exited, Exited}}};
 1038: 				 Res ->
 1039: 				     otp_3906_list_defunct(EmPid, OSName),
 1040: 				     Res
 1041: 			     end
 1042: 		     end,
 1043: 	    process_flag(trap_exit, OTE),
 1044: 	    process_flag(priority, OP),
 1045: 	    test_server:stop_node(Node),
 1046: 	    case Result of
 1047: 		succeded ->
 1048: 		    ok;
 1049: 		_ ->
 1050: 		    test_server:fail(Result)
 1051: 	    end;
 1052: 	_ ->
 1053: 	    {skipped, "No C compiler found"}
 1054:     end.
 1055: 
 1056: otp_3906_list_defunct(EmPid, OSName) ->
 1057:     % Guess ps switches to use and what to grep for (could be improved)
 1058:     {Switches, Zombie} = case OSName of
 1059: 			     BSD when BSD == darwin;
 1060: 				      BSD == openbsd;
 1061: 				      BSD == netbsd;
 1062: 				      BSD == freebsd ->
 1063: 				 {"-ajx", "Z"};
 1064: 			     _ ->
 1065: 				 {"-ef", "[dD]efunct"}
 1066: 			 end,
 1067:     test_server:format("Emulator pid: ~s~n"
 1068: 		       "Listing of zombie processes:~n"
 1069: 		       "~s~n",
 1070: 		       [EmPid,
 1071: 			otp_3906_htmlize(os:cmd("ps "
 1072: 						++ Switches
 1073: 						++ " | grep "
 1074: 						++ Zombie))]).
 1075: 
 1076: otp_3906_htmlize([]) ->
 1077:     [];
 1078: otp_3906_htmlize([C | Cs]) ->
 1079:     case [C] of
 1080: 	"<" -> "&lt;" ++ otp_3906_htmlize(Cs);
 1081: 	">" -> "&gt;" ++ otp_3906_htmlize(Cs);
 1082: 	_ ->   [C | otp_3906_htmlize(Cs)]
 1083:     end.
 1084: 
 1085: otp_3906_make_prog(CC, PrivDir) ->
 1086:     SrcFileName = filename:join(PrivDir, ?OTP_3906_PROGNAME ++ ".c"),
 1087:     TrgtFileName = filename:join(PrivDir, ?OTP_3906_PROGNAME),
 1088:     {ok, SrcFile} = file:open(SrcFileName, write),
 1089:     io:format(SrcFile,
 1090: 	      "int           ~n"
 1091: 	      "main(void)    ~n"
 1092: 	      "{             ~n"
 1093: 	      "   return ~p; ~n"
 1094: 	      "}             ~n",
 1095: 	      [?OTP_3906_EXIT_STATUS]),
 1096:     file:close(SrcFile),
 1097:     os:cmd(CC ++ " " ++ SrcFileName ++ " -o " ++ TrgtFileName),
 1098:     TrgtFileName.
 1099: 
 1100: 
 1101: otp_3906_wait_result(ForkerStarter, F, E) ->
 1102:     receive
 1103: 	{'EXIT', ForkerStarter, Reason} ->
 1104: 	    {failed, {Reason, {forked, F}, {exited, E}}};
 1105: 	forked ->
 1106: 	    otp_3906_wait_result(ForkerStarter, F+1, E);
 1107: 	exited ->
 1108: 	    otp_3906_wait_result(ForkerStarter, F, E+1);
 1109: 	tick ->
 1110: 	    otp_3906_wait_result(ForkerStarter, F, E);
 1111: 	succeded ->
 1112: 	    {succeded, F, E}
 1113:     after
 1114: 	?OTP_3906_TICK_TIMEOUT ->
 1115: 	    unlink(ForkerStarter),
 1116: 	    exit(ForkerStarter, timeout),
 1117: 	    {failed, {timeout, {forked, F}, {exited, E}}}
 1118:     end.
 1119: 
 1120: otp_3906_collect([], _) ->
 1121:     done;
 1122: otp_3906_collect(RefList, Sup) ->
 1123:     otp_3906_collect(otp_3906_collect_one(RefList, Sup), Sup).
 1124: 
 1125: otp_3906_collect_one(RefList, Sup) ->
 1126:     receive
 1127: 	Ref when is_reference(Ref) ->
 1128: 	    Sup ! tick,
 1129: 	    lists:delete(Ref, RefList)
 1130:     end.
 1131:     
 1132: otp_3906_start_forker(N, Sup, Prog) ->
 1133:     Ref = make_ref(),
 1134:     spawn_opt(?MODULE,
 1135: 	      otp_3906_forker,
 1136: 	      [N, self(), Ref, Sup, Prog],
 1137: 	      [link, {priority, max}]),
 1138:     Ref.
 1139: 
 1140: otp_3906_start_forker_starter(N, RefList, Sup, Prog) ->
 1141:     process_flag(priority, max),
 1142:     EmPid = os:getpid(),
 1143:     Sup ! {emulator_pid, EmPid},
 1144:     otp_3906_forker_starter(N, RefList, Sup, Prog).
 1145: 
 1146: otp_3906_forker_starter(0, RefList, Sup, _) ->
 1147:     otp_3906_collect(RefList, Sup),
 1148:     unlink(Sup),
 1149:     Sup ! succeded;
 1150: otp_3906_forker_starter(N, RefList, Sup, Prog)
 1151:   when length(RefList) >= ?OTP_3906_MAX_CONC_OSP ->
 1152:     otp_3906_forker_starter(N, otp_3906_collect_one(RefList, Sup), Sup, Prog);
 1153: otp_3906_forker_starter(N, RefList, Sup, Prog)
 1154:   when is_integer(N), N > ?OTP_3906_OSP_P_ERLP ->
 1155:     otp_3906_forker_starter(N-?OTP_3906_OSP_P_ERLP,
 1156: 			    [otp_3906_start_forker(?OTP_3906_OSP_P_ERLP,
 1157: 						   Sup,
 1158: 						   Prog)|RefList],
 1159: 			    Sup,
 1160: 			    Prog);
 1161: otp_3906_forker_starter(N, RefList, Sup, Prog) when is_integer(N) ->
 1162:     otp_3906_forker_starter(0,
 1163: 			    [otp_3906_start_forker(N,
 1164: 						   Sup,
 1165: 						   Prog)|RefList],
 1166: 			    Sup,
 1167: 			    Prog).
 1168: 
 1169: otp_3906_forker(0, Parent, Ref, _, _) ->
 1170:     unlink(Parent),
 1171:     Parent ! Ref;
 1172: otp_3906_forker(N, Parent, Ref, Sup, Prog) ->
 1173:     Port = erlang:open_port({spawn, Prog}, [exit_status, in]),
 1174:     Sup ! forked,
 1175:     receive
 1176: 	{Port, {exit_status, ?OTP_3906_EXIT_STATUS}} ->
 1177: 	    Sup ! exited,
 1178: 	    otp_3906_forker(N-1, Parent, Ref, Sup, Prog);
 1179: 	{Port, Res} ->
 1180: 	    exit(Res);
 1181: 	Other ->
 1182: 	    exit(Other)
 1183:     end.
 1184: 
 1185: 
 1186: otp_4389(suite) -> [];
 1187: otp_4389(doc) -> [];
 1188: otp_4389(Config)  when is_list(Config) ->
 1189:     case os:type() of
 1190: 	{unix, _} ->
 1191: 	    Dog = test_server:timetrap(test_server:seconds(240)),
 1192: 	    TCR = self(),
 1193: 	    case get_true_cmd() of
 1194: 		True when is_list(True) ->
 1195: 		    lists:foreach(
 1196: 			    fun (P) ->
 1197: 				    receive
 1198: 					      {P, ok} ->  ok;
 1199: 					      {P, Err} -> ?t:fail(Err)
 1200: 					  end
 1201: 			    end,
 1202: 			    lists:map(
 1203: 			      fun(_) ->
 1204: 				      spawn_link(
 1205: 					fun() ->
 1206: 						process_flag(trap_exit, true),
 1207: 						case catch open_port({spawn, True},
 1208: 								     [stream,exit_status]) of
 1209: 						    P when is_port(P) ->
 1210: 							receive
 1211: 							    {P,{exit_status,_}} ->
 1212: 								TCR ! {self(),ok};
 1213: 							    {'EXIT',_,{R2,_}} when R2 == emfile;
 1214: 										   R2 == eagain ->
 1215: 								TCR ! {self(),ok};
 1216: 							    Err2 ->
 1217: 								TCR ! {self(),{msg,Err2}}
 1218: 							end;
 1219: 						    {'EXIT',{R1,_}} when R1 == emfile;
 1220: 									 R1 == eagain ->
 1221: 							TCR ! {self(),ok};
 1222: 						    Err1 ->
 1223: 							TCR ! {self(), {open_port,Err1}}
 1224: 						end
 1225: 					end)
 1226: 			      end,
 1227: 			      lists:duplicate(1000,[]))),
 1228: 		    test_server:timetrap_cancel(Dog),
 1229: 		    {comment,
 1230: 		     "This test case doesn't always fail when the bug that "
 1231: 		     "it tests for is present (it is most likely to fail on"
 1232: 		     " a multi processor machine). If the test case fails it"
 1233: 		     " will fail by deadlocking the emulator."};
 1234: 		_ ->
 1235: 		    {skipped, "\"true\" command not found"}
 1236: 	    end;
 1237: 	_ ->
 1238: 	    {skip,"Only run on Unix"}
 1239:     end.
 1240: 
 1241: get_true_cmd() ->
 1242:     DoFileExist = fun (FileName) ->
 1243:  			  case file:read_file_info(FileName) of
 1244:  			      {ok, _} -> throw(FileName);
 1245:  			      _ -> not_found
 1246:  			  end
 1247:  		  end,
 1248:     catch begin
 1249:  	      %% First check in /usr/bin and /bin
 1250:  	      DoFileExist("/usr/bin/true"),
 1251:  	      DoFileExist("/bin/true"),
 1252:  	      %% Try which
 1253:  	      case filename:dirname(os:cmd("which true")) of
 1254:  		  "." -> not_found;
 1255:  		  TrueDir -> filename:join(TrueDir, "true")
 1256:  	      end
 1257:  	  end.
 1258: 
 1259: %% 'exit_status' option
 1260: exit_status(suite) ->
 1261:     [];
 1262: exit_status(doc) ->
 1263:     ["Test that the 'exit_status' option works"];
 1264: exit_status(Config)  when is_list(Config) ->
 1265:     Dog = test_server:timetrap(test_server:seconds(60)),
 1266:     port_expect(Config,[{"x",
 1267: 			       [{exit_status, 5}]}],
 1268: 		      1, "", [exit_status]),
 1269:     test_server:timetrap_cancel(Dog),
 1270:     ok.
 1271: 
 1272: spawn_driver(suite) ->
 1273:     [];
 1274: spawn_driver(doc) ->
 1275:     ["Test spawning a driver specifically"];
 1276: spawn_driver(Config) when is_list(Config) ->
 1277:     Dog = test_server:timetrap(test_server:seconds(10)),
 1278:     Path = ?config(data_dir, Config),
 1279:     ok = load_driver(Path, "echo_drv"),
 1280:     Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
 1281:     Port ! {self(), {command, "Hello port!"}},
 1282:     receive 
 1283: 	      {Port, {data, "Hello port!"}} = Msg1 -> 
 1284: 		  io:format("~p~n", [Msg1]),
 1285: 		  ok; 
 1286: 	      Other ->
 1287: 		  test_server:fail({unexpected, Other})
 1288: 	  end,
 1289:     Port ! {self(), close},
 1290:     receive {Port, closed} -> ok end,
 1291: 
 1292:     Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, 
 1293: 				   []),
 1294:     receive 
 1295: 	      {Port2, {data, "Hello port?"}} = Msg2 -> 
 1296: 		  io:format("~p~n", [Msg2]),
 1297: 		  ok; 
 1298: 	      Other2 ->
 1299: 		  test_server:fail({unexpected2, Other2})
 1300: 	  end,
 1301:     Port2 ! {self(), close},
 1302:     receive {Port2, closed} -> ok end,
 1303:     {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])),
 1304:     {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])),
 1305:     {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])),
 1306:     test_server:timetrap_cancel(Dog),
 1307:     ok.
 1308: 
 1309: parallelism_option(suite) ->
 1310:     [];
 1311: parallelism_option(doc) ->
 1312:     ["Test parallelism option of open_port"];
 1313: parallelism_option(Config) when is_list(Config) ->
 1314:     ?line Dog = test_server:timetrap(test_server:seconds(10)),
 1315:     ?line Path = ?config(data_dir, Config),
 1316:     ?line ok = load_driver(Path, "echo_drv"),
 1317:     ?line Port = erlang:open_port({spawn_driver, "echo_drv"},
 1318: 				  [{parallelism, true}]),
 1319:     ?line {parallelism, true} = erlang:port_info(Port, parallelism),
 1320:     ?line Port ! {self(), {command, "Hello port!"}},
 1321:     ?line receive 
 1322: 	      {Port, {data, "Hello port!"}} = Msg1 -> 
 1323: 		  io:format("~p~n", [Msg1]),
 1324: 		  ok; 
 1325: 	      Other ->
 1326: 		  test_server:fail({unexpected, Other})
 1327: 	  end,
 1328:     ?line Port ! {self(), close},
 1329:     ?line receive {Port, closed} -> ok end,
 1330: 
 1331:     ?line Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, 
 1332: 				   [{parallelism, false}]),
 1333:     ?line {parallelism, false} = erlang:port_info(Port2, parallelism),
 1334:     ?line receive 
 1335: 	      {Port2, {data, "Hello port?"}} = Msg2 -> 
 1336: 		  io:format("~p~n", [Msg2]),
 1337: 		  ok; 
 1338: 	      Other2 ->
 1339: 		  test_server:fail({unexpected2, Other2})
 1340: 	  end,
 1341:     ?line Port2 ! {self(), close},
 1342:     ?line receive {Port2, closed} -> ok end,
 1343:     ?line test_server:timetrap_cancel(Dog),
 1344:     ok.
 1345: 
 1346: spawn_executable(suite) ->
 1347:     [];
 1348: spawn_executable(doc) ->
 1349:     ["Test spawning an executable specifically"];
 1350: spawn_executable(Config) when is_list(Config) ->
 1351:     Dog = test_server:timetrap(test_server:seconds(10)),
 1352:     DataDir = ?config(data_dir, Config),
 1353:     EchoArgs1 = filename:join([DataDir,"echo_args"]),
 1354:     ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
 1355:     [ExactFile1] = run_echo_args(DataDir,[]),
 1356:     ["echo_args"] = run_echo_args(DataDir,["echo_args"]),
 1357:     ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
 1358:     [ExactFile1,"hello world","dlrow olleh"] = 
 1359: 	run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]),
 1360:     [ExactFile1] = run_echo_args(DataDir,[default]),
 1361:     [ExactFile1,"hello world","dlrow olleh"] = 
 1362: 	run_echo_args(DataDir,[switch_order,ExactFile1,"hello world",
 1363: 			       "dlrow olleh"]),
 1364:     [ExactFile1,"hello world","dlrow olleh"] = 
 1365: 	run_echo_args(DataDir,[default,"hello world","dlrow olleh"]),
 1366: 
 1367:     [ExactFile1,"hello world","dlrow olleh"] = 
 1368: 	run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""),
 1369: 
 1370:     PrivDir = ?config(priv_dir, Config),
 1371:     SpaceDir =filename:join([PrivDir,"With Spaces"]),
 1372:     file:make_dir(SpaceDir),
 1373:     Executable = filename:basename(ExactFile1),
 1374:     file:copy(ExactFile1,filename:join([SpaceDir,Executable])),
 1375:     ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])),
 1376:     chmodplusx(ExactFile2),
 1377:     io:format("|~s|~n",[ExactFile2]),
 1378:     [ExactFile2] = run_echo_args(SpaceDir,[]),
 1379:     ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]),
 1380:     ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]),
 1381:     [ExactFile2,"hello world","dlrow olleh"] = 
 1382: 	run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]),
 1383:     [ExactFile2] = run_echo_args(SpaceDir,[default]),
 1384:     [ExactFile2,"hello world","dlrow olleh"] = 
 1385: 	run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world",
 1386: 			       "dlrow olleh"]),
 1387:     [ExactFile2,"hello world","dlrow olleh"] = 
 1388: 	run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]),
 1389:     [ExactFile2,"hello world","dlrow olleh"] = 
 1390: 	run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""),
 1391: 
 1392:     ExeExt = 
 1393: 	case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
 1394: 	    "exe" ->
 1395: 		".exe";
 1396: 	    _ ->
 1397: 		""
 1398: 	end,
 1399:     Executable2 = "spoky name"++ExeExt,
 1400:     file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
 1401:     ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
 1402:     chmodplusx(ExactFile3),
 1403:     [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]),
 1404:     ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]),
 1405:     ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]),
 1406:     [ExactFile3,"hello world","dlrow olleh"] = 
 1407: 	run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]),
 1408:     [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]),
 1409:     [ExactFile3,"hello world","dlrow olleh"] = 
 1410: 	run_echo_args(SpaceDir,Executable2,
 1411: 		      [switch_order,ExactFile3,"hello world",
 1412: 		       "dlrow olleh"]),
 1413:     [ExactFile3,"hello world","dlrow olleh"] = 
 1414: 	run_echo_args(SpaceDir,Executable2,
 1415: 		      [default,"hello world","dlrow olleh"]),
 1416:     [ExactFile3,"hello world","dlrow olleh"] = 
 1417: 	run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""),
 1418:     {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
 1419: 						     [default,"hello world",
 1420: 						      "dlrow olleh"])),
 1421:     NonExec = "kronxfrt"++ExeExt,
 1422:     file:write_file(filename:join([SpaceDir,NonExec]),
 1423: 			  <<"Not an executable">>),
 1424:     {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec,
 1425: 						     [default,"hello world",
 1426: 						      "dlrow olleh"])),
 1427:     {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])),
 1428:     {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])),
 1429:     case os:type() of
 1430: 	{win32,_} ->
 1431: 	    test_bat_file(SpaceDir);
 1432: 	{unix,_} ->
 1433: 	    test_sh_file(SpaceDir)
 1434:     end,
 1435:     test_server:timetrap_cancel(Dog),
 1436:     ok.
 1437: 
 1438: unregister_name(Config) when is_list(Config) ->
 1439:     true = register(crash, open_port({spawn, "sleep 100"}, [])),
 1440:     true = unregister(crash).
 1441: 
 1442: test_bat_file(Dir) ->
 1443:     FN = "tf.bat",
 1444:     Full = filename:join([Dir,FN]),
 1445:     D = [<<"@echo off\r\n">>,
 1446: 	 <<"echo argv[0]:^|%0^|\r\n">>,
 1447: 	 <<"if \"%1\" == \"\" goto done\r\n">>,
 1448: 	 <<"echo argv[1]:^|%1^|\r\n">>,
 1449: 	 <<"if \"%2\" == \"\" goto done\r\n">>,
 1450: 	 <<"echo argv[2]:^|%2^|\r\n">>,
 1451: 	 <<"if \"%3\" == \"\" goto done\r\n">>,
 1452: 	 <<"echo argv[3]:^|%3^|\r\n">>,
 1453: 	 <<"if \"%4\" == \"\" goto done\r\n">>,
 1454: 	 <<"echo argv[4]:^|%4^|\r\n">>,
 1455: 	 <<"if \"%5\" == \"\" goto done\r\n">>,
 1456: 	 <<"echo argv[5]:^|%5^|\r\n">>,
 1457: 	 <<"\r\n">>,
 1458: 	 <<":done\r\n">>,
 1459: 	 <<"\r\n">>],
 1460:     file:write_file(Full,list_to_binary(D)),
 1461:     EF = filename:basename(FN),
 1462:     [DN,"hello","world"] = 
 1463: 	run_echo_args(Dir,FN,
 1464: 		      [default,"hello","world"]),
 1465:     %% The arg0 argumant should be ignored when running batch files
 1466:     [DN,"hello","world"] = 
 1467: 	run_echo_args(Dir,FN,
 1468: 		      ["knaskurt","hello","world"]),
 1469:     EF = filename:basename(DN),
 1470:     ok.
 1471: 
 1472: test_sh_file(Dir) ->
 1473:     FN = "tf.sh",
 1474:     Full = filename:join([Dir,FN]),
 1475:     D = [<<"#! /bin/sh\n">>,
 1476: 	 <<"echo 'argv[0]:|'$0'|'\n">>,
 1477: 	 <<"i=1\n">>,
 1478: 	 <<"while [ '!' -z \"$1\" ]; do\n">>,
 1479: 	 <<"    echo 'argv['$i']:|'\"$1\"'|'\n">>,
 1480: 	 <<"    shift\n">>,
 1481: 	 <<"    i=`expr $i + 1`\n">>,
 1482: 	 <<"done\n">>],
 1483:     file:write_file(Full,list_to_binary(D)),
 1484:     chmodplusx(Full),
 1485:     [Full,"hello","world"] = 
 1486: 	run_echo_args(Dir,FN,
 1487: 		      [default,"hello","world"]),
 1488:     [Full,"hello","world of spaces"] = 
 1489: 	run_echo_args(Dir,FN,
 1490: 		      [default,"hello","world of spaces"]),
 1491:     file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>),
 1492:     file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>),
 1493:     Pattern = filename:join([Dir,"testfile*"]),
 1494:     L = filelib:wildcard(Pattern),
 1495:     2 = length(L),
 1496:     [Full,"hello",Pattern] = 
 1497: 	run_echo_args(Dir,FN,
 1498: 		      [default,"hello",Pattern]),
 1499:      ok.
 1500: 
 1501:     
 1502: 
 1503: chmodplusx(Filename) ->
 1504:     case file:read_file_info(Filename) of
 1505: 	{ok,FI} ->
 1506: 	    FI2 = FI#file_info{mode = ((FI#file_info.mode) bor 8#00100)},
 1507: 	    file:write_file_info(Filename,FI2);
 1508: 	_ ->
 1509: 	    ok
 1510:     end.
 1511: 
 1512: run_echo_args_2(FullnameAndArgs) ->
 1513:     Port = open_port({spawn,FullnameAndArgs},[eof]),
 1514:     Data = collect_data(Port),
 1515:     Port ! {self(), close},
 1516:     receive {Port, closed} -> ok end,
 1517:     parse_echo_args_output(Data).
 1518:     
 1519: 
 1520: run_echo_args(Where,Args) ->
 1521:     run_echo_args(Where,"echo_args",Args).   
 1522: run_echo_args(Where,Prog,Args) ->
 1523:     ArgvArg = case Args of
 1524: 		  [] ->
 1525: 		      [];
 1526: 		  [default|T] ->
 1527: 		      [{args,T}];
 1528: 		  [switch_order,H|T] ->
 1529: 		      [{args,T},{arg0,H}];
 1530: 		  [H|T] ->
 1531: 		      [{arg0,H},{args,T}]
 1532: 	      end,
 1533:     Command = filename:join([Where,Prog]),
 1534:     Port = open_port({spawn_executable,Command},ArgvArg++[eof]),
 1535:     Data = collect_data(Port),
 1536:     Port ! {self(), close},
 1537:     receive {Port, closed} -> ok end,
 1538:     parse_echo_args_output(Data).
 1539:     
 1540: collect_data(Port) ->
 1541:     receive
 1542: 	{Port, {data, Data}} ->
 1543: 	    Data ++ collect_data(Port);
 1544: 	{Port, eof} ->
 1545: 	    []
 1546:     end.
 1547: 
 1548: parse_echo_args_output(Data) ->
 1549:     [lists:last(string:tokens(S,"|")) || S <- string:tokens(Data,"\r\n")].
 1550: 
 1551: mix_up_ports(suite) ->
 1552:     [];
 1553: mix_up_ports(doc) ->
 1554:     ["Test that the emulator does not mix up ports when the port table wraps"];
 1555: mix_up_ports(Config) when is_list(Config) ->
 1556:     Dog = test_server:timetrap(test_server:seconds(10)),
 1557:     Path = ?config(data_dir, Config),
 1558:     ok = load_driver(Path, "echo_drv"),
 1559:     Port = erlang:open_port({spawn, "echo_drv"}, []),
 1560:     Port ! {self(), {command, "Hello port!"}},
 1561:     receive 
 1562: 	      {Port, {data, "Hello port!"}} = Msg1 -> 
 1563: 		  io:format("~p~n", [Msg1]),
 1564: 		  ok; 
 1565: 	      Other ->
 1566: 		  test_server:fail({unexpected, Other})
 1567: 	  end,
 1568:     Port ! {self(), close},
 1569:     receive {Port, closed} -> ok end,
 1570:     loop(start, done,
 1571: 	       fun(P) ->
 1572: 		       Q = 
 1573: 			   (catch erlang:open_port({spawn, "echo_drv"}, [])),
 1574: %%		       io:format("~p ", [Q]),
 1575: 		       if is_port(Q) ->
 1576: 			       Q;
 1577: 			  true ->
 1578: 			       io:format("~p~n", [P]),
 1579: 			       done
 1580: 		       end
 1581: 	       end),
 1582:     Port ! {self(), {command, "Hello again port!"}},
 1583:     receive 
 1584: 	      Msg2 ->
 1585: 		  test_server:fail({unexpected, Msg2})
 1586: 	  after 1000 ->
 1587: 		  ok
 1588: 	  end,
 1589:     test_server:timetrap_cancel(Dog),
 1590:     ok.
 1591: 
 1592: loop(Stop, Stop, Fun) when is_function(Fun) ->
 1593:     ok;
 1594: loop(Start, Stop, Fun) when is_function(Fun) ->
 1595:     loop(Fun(Start), Stop, Fun).
 1596: 
 1597: 
 1598: otp_5112(suite) ->
 1599:     [];
 1600: otp_5112(doc) ->
 1601:     ["Test that link to connected process is taken away when port calls",
 1602:      "driver_exit() also when the port index has wrapped"];
 1603: otp_5112(Config) when is_list(Config) ->
 1604:     Dog = test_server:timetrap(test_server:seconds(10)),
 1605:     Path = ?config(data_dir, Config),
 1606:     ok = load_driver(Path, "exit_drv"),
 1607:     Port = otp_5112_get_wrapped_port(),
 1608:     ?t:format("Max ports: ~p~n",[max_ports()]),
 1609:     ?t:format("Port: ~p~n",[Port]),
 1610:     {links, Links1} = process_info(self(),links),
 1611:     ?t:format("Links1: ~p~n",[Links1]),
 1612:     true = lists:member(Port, Links1),
 1613:     Port ! {self(), {command, ""}},
 1614:     ?line wait_until(fun () -> lists:member(Port, erlang:ports()) == false end),
 1615:     {links, Links2} = process_info(self(),links),
 1616:     ?t:format("Links2: ~p~n",[Links2]),
 1617:     false = lists:member(Port, Links2), %% This used to fail
 1618:     test_server:timetrap_cancel(Dog),
 1619:     ok.
 1620: 
 1621: otp_5112_get_wrapped_port() ->
 1622:     P1 = erlang:open_port({spawn, "exit_drv"}, []),
 1623:     case port_ix(P1) < max_ports() of
 1624: 	      true ->
 1625: 		  ?t:format("Need to wrap port index (~p)~n", [P1]),
 1626: 		  otp_5112_wrap_port_ix([P1]),
 1627: 		  P2 = erlang:open_port({spawn, "exit_drv"}, []),
 1628: 		  false = port_ix(P2) < max_ports(),
 1629: 		  P2;
 1630: 	      false ->
 1631: 		  ?t:format("Port index already wrapped (~p)~n", [P1]),
 1632: 		  P1
 1633: 	  end.
 1634: 
 1635: otp_5112_wrap_port_ix(Ports) ->
 1636:     case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
 1637: 	      Port when is_port(Port) ->
 1638: 		  otp_5112_wrap_port_ix([Port|Ports]);
 1639: 	      _ ->
 1640: 		  %% Port table now full; empty port table
 1641: 		  lists:foreach(fun (P) ->  P ! {self(), close} end,
 1642: 				      Ports),
 1643: 		  ok
 1644: 	  end.
 1645: 
 1646: 
 1647: otp_5119(suite) ->
 1648:     [];
 1649: otp_5119(doc) ->
 1650:     ["Test that port index is not unnecessarily wrapped"];
 1651: otp_5119(Config) when is_list(Config) ->
 1652:     Dog = test_server:timetrap(test_server:seconds(10)),
 1653:     Path = ?config(data_dir, Config),
 1654:     ok = load_driver(Path, "exit_drv"),
 1655:     PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
 1656:     PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])),
 1657:     {PortIx1, PortIx2} = case PI2 > PI1 of
 1658: 	true ->
 1659: 	    {PI1, PI2};
 1660: 	false ->
 1661: 	    {port_ix(otp_5119_fill_empty_port_tab([PI2])),
 1662: 		port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
 1663:     end,
 1664:     MaxPorts = max_ports(),
 1665:     ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]),
 1666:     ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]),
 1667:     ?t:format("MaxPorts = ~p~n", [MaxPorts]),
 1668:     true = PortIx2 > PortIx1,
 1669:     true = PortIx2 =< PortIx1 + MaxPorts,
 1670:     test_server:timetrap_cancel(Dog),
 1671:     ok.
 1672: 
 1673: otp_5119_fill_empty_port_tab(Ports) ->
 1674:     case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
 1675: 	      Port when is_port(Port) ->
 1676: 		  otp_5119_fill_empty_port_tab([Port|Ports]);
 1677: 	      _ ->
 1678: 		  %% Port table now full; empty port table
 1679: 		  lists:foreach(fun (P) ->  P ! {self(), close} end,
 1680: 				      Ports),
 1681: 		  [LastPort|_] = Ports,
 1682: 		  LastPort
 1683: 	  end.
 1684: 
 1685: max_ports() ->
 1686:     erlang:system_info(port_limit).
 1687: 
 1688: port_ix(Port) when is_port(Port) ->
 1689:     ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
 1690: 						"<.>"),
 1691:     list_to_integer(PortIxStr).
 1692: 
 1693: 
 1694: otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"];
 1695: otp_6224(suite) -> [];
 1696: otp_6224(Config) when is_list(Config) ->
 1697:     Dog = test_server:timetrap(test_server:seconds(10)),
 1698:     Path = ?config(data_dir, Config),
 1699:     ok = load_driver(Path, "failure_drv"),
 1700:     Go = make_ref(),
 1701:     Failer = spawn(fun () ->
 1702: 				 receive Go -> ok end,
 1703: 				 Port = open_port({spawn, "failure_drv"},
 1704: 							[]),
 1705: 				 Port ! {self(), {command, "Fail, please!"}},
 1706: 				 otp_6224_loop()
 1707: 			 end),
 1708:     Mon = erlang:monitor(process, Failer),
 1709:     Failer ! Go,
 1710:     receive
 1711: 	      {'DOWN', Mon, process, Failer, Reason} ->
 1712: 		  case Reason of
 1713: 			    {driver_failed, _} -> ok;
 1714: 			    driver_failed -> ok;
 1715: 			    _ -> ?t:fail({unexpected_exit_reason,
 1716: 						Reason})
 1717: 			end
 1718: 	  end,
 1719:     test_server:timetrap_cancel(Dog),
 1720:     ok.
 1721:     
 1722: otp_6224_loop() ->
 1723:     receive _ -> ok after 0 -> ok end,
 1724:     otp_6224_loop().
 1725: 
 1726: 
 1727: -define(EXIT_STATUS_MSB_MAX_PROCS, 64).
 1728: -define(EXIT_STATUS_MSB_MAX_PORTS, 300).
 1729: 
 1730: exit_status_multi_scheduling_block(doc) -> [];
 1731: exit_status_multi_scheduling_block(suite) -> [];
 1732: exit_status_multi_scheduling_block(Config) when is_list(Config) ->
 1733:     Repeat = 3,
 1734:     case ?t:os_type() of
 1735: 	      {unix, _} ->
 1736: 		  Dog = ?t:timetrap(test_server:minutes(2*Repeat)),
 1737: 		  SleepSecs = 6,
 1738: 		  try
 1739: 		      lists:foreach(fun (_) ->
 1740: 					    exit_status_msb_test(Config,
 1741: 								 SleepSecs)
 1742: 				    end,
 1743: 				    lists:seq(1, Repeat))
 1744: 		  after
 1745: 		      %% Wait for the system to recover (regardless
 1746: 		      %% of success or not) otherwise later testcases
 1747: 		      %% may unnecessarily fail.
 1748: 		      ?t:timetrap_cancel(Dog),
 1749: 		      receive after SleepSecs+500 -> ok end
 1750: 		  end;
 1751: 	      _ -> {skip, "Not implemented for this OS"}
 1752: 	  end.
 1753: 
 1754: exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
 1755:     %%
 1756:     %% We want to start port programs from as many schedulers as possible
 1757:     %% and we want these port programs to terminate while multi-scheduling
 1758:     %% is blocked.
 1759:     %% 
 1760:     NoSchedsOnln = erlang:system_info(schedulers_online),
 1761:     Parent = self(),
 1762:     ?t:format("SleepSecs = ~p~n", [SleepSecs]),
 1763:     PortProg = "sleep " ++ integer_to_list(SleepSecs),
 1764:     Start = now(),
 1765:     NoProcs = case NoSchedsOnln of
 1766: 			NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
 1767: 			    NProcs;
 1768: 			_ ->
 1769: 			    ?EXIT_STATUS_MSB_MAX_PROCS
 1770: 		    end,
 1771:     NoPortsPerProc = case 20*NoProcs of
 1772: 			       TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20;
 1773: 			       _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs
 1774: 			   end,
 1775:     ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
 1776: 		    [NoProcs, NoPortsPerProc]),
 1777:     ProcFun
 1778: 	= fun () ->
 1779: 		  PrtSIds = lists:map(
 1780: 			      fun (_) ->
 1781: 				      erlang:yield(),
 1782: 				      case catch open_port({spawn, PortProg},
 1783: 							   [exit_status]) of
 1784: 					  Prt when is_port(Prt) ->
 1785: 					      {Prt,
 1786: 					       erlang:system_info(scheduler_id)};
 1787: 					  {'EXIT', {Err, _}} when Err == eagain;
 1788: 								  Err == emfile ->
 1789: 					      noop;
 1790: 					  {'EXIT', Err} when Err == eagain;
 1791: 							     Err == emfile ->
 1792: 					      noop;
 1793: 					  Error ->
 1794: 					      ?t:fail(Error)
 1795: 				      end
 1796: 			      end,
 1797: 			      lists:seq(1, NoPortsPerProc)),
 1798: 		  SIds = lists:filter(fun (noop) -> false;
 1799: 					  (_) -> true
 1800: 				      end,
 1801: 				      lists:map(fun (noop) -> noop;
 1802: 						    ({_, SId}) -> SId
 1803: 						end,
 1804: 						PrtSIds)),
 1805: 		  process_flag(scheduler, 0),
 1806: 		  Parent ! {self(), started, SIds},
 1807: 		  lists:foreach(
 1808: 		    fun (noop) ->
 1809: 			    noop;
 1810: 			({Port, _}) ->
 1811: 			    receive
 1812: 				{Port, {exit_status, 0}} ->
 1813: 				    ok;
 1814: 				{Port, {exit_status, Status}} when Status > 128 ->
 1815: 				    %% Sometimes happens when we have created
 1816: 				    %% too many ports.
 1817: 				    ok;
 1818: 				{Port, {exit_status, _}} = ESMsg ->
 1819: 				    {Port, {exit_status, 0}} = ESMsg
 1820: 			    end
 1821: 		    end,
 1822: 		    PrtSIds),
 1823: 		  Parent ! {self(), done}
 1824: 	  end,
 1825:     Procs = lists:map(fun (N) ->
 1826: 				    spawn_opt(ProcFun,
 1827: 					      [link,
 1828: 					       {scheduler,
 1829: 						(N rem NoSchedsOnln)+1}])
 1830: 			    end,
 1831: 			    lists:seq(1, NoProcs)),
 1832:     SIds = lists:map(fun (P) ->
 1833: 				   receive {P, started, SIds} -> SIds end
 1834: 			   end,
 1835: 			   Procs),
 1836:     StartedTime = timer:now_diff(now(), Start)/1000000,
 1837:     ?t:format("StartedTime = ~p~n", [StartedTime]),
 1838:     true = StartedTime < SleepSecs,
 1839:     erlang:system_flag(multi_scheduling, block),
 1840:     lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
 1841:     DoneTime = timer:now_diff(now(), Start)/1000000,
 1842:     ?t:format("DoneTime = ~p~n", [DoneTime]),
 1843:     true = DoneTime > SleepSecs,
 1844:     ok = verify_multi_scheduling_blocked(),
 1845:     erlang:system_flag(multi_scheduling, unblock),
 1846:     case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of
 1847: 	      {N, N} ->
 1848: 		  ok;
 1849: 	      {N, M} ->
 1850: 		  ?t:fail("Failed to create ports on all"
 1851: 				++ integer_to_list(M) ++ " available"
 1852: 				"schedulers. Only created ports on "
 1853: 				++ integer_to_list(N) ++ " schedulers.")
 1854: 	  end.
 1855: 
 1856: save_sid(SIds) ->
 1857:     SId = erlang:system_info(scheduler_id),
 1858:     case lists:member(SId, SIds) of
 1859: 	true -> SIds;
 1860: 	false -> [SId|SIds]
 1861:     end.
 1862: 
 1863: sid_proc(SIds) ->
 1864:     NewSIds = save_sid(SIds),
 1865:     receive
 1866: 	{From, want_sids} ->
 1867: 	    From ! {self(), sids, NewSIds}
 1868:     after 0 ->
 1869: 	    sid_proc(NewSIds)
 1870:     end.
 1871: 
 1872: verify_multi_scheduling_blocked() ->
 1873:     Procs = lists:map(fun (_) ->
 1874: 				    spawn_link(fun () -> sid_proc([]) end)
 1875: 			    end,
 1876: 			    lists:seq(1, 3*erlang:system_info(schedulers_online))),
 1877:     receive after 1000 -> ok end,
 1878:     SIds = lists:map(fun (P) ->
 1879: 				   P ! {self(), want_sids},
 1880: 				   receive {P, sids, PSIds} -> PSIds end
 1881: 			   end,
 1882: 			   Procs),
 1883:     1 = length(lists:usort(lists:flatten(SIds))),
 1884:     ok.
 1885: 		      
 1886:     
 1887: %%% Pinging functions.
 1888: 
 1889: stream_ping(Config, Size, CmdLine, Options) ->
 1890:     Data = random_packet(Size),
 1891:     port_expect(Config, [{Data, [Data]}], 0, CmdLine, Options).
 1892: 
 1893: ping(Config, Sizes, HSize, CmdLine, Options) ->
 1894:     Actions = lists:map(fun(Size) ->
 1895: 				[$p|Packet] = random_packet(Size, "ping"),
 1896: 				{[$p|Packet], [[$P|Packet]]}
 1897: 				end,
 1898: 			Sizes),
 1899:     port_expect(Config, Actions, HSize, CmdLine, Options).
 1900: 
 1901: %% expect_input(Sizes, HSize, CmdLine, Options)
 1902: %%
 1903: %% Sizes = Size of packets to generated.
 1904: %% HSize = Header size: 1, 2, or 4
 1905: %% CmdLine = Additional command line options.
 1906: %% Options = Addtional port options.
 1907: 
 1908: expect_input(Config, Sizes, HSize, CmdLine, Options) ->
 1909:     expect_input1(Config, Sizes, {HSize, CmdLine, Options}, [], []).
 1910: 
 1911: expect_input1(Config, [0|Rest], Params, Expect, ReplyCommand) ->
 1912:     expect_input1(Config, Rest, Params, [""|Expect], ["x0"|ReplyCommand]);
 1913: expect_input1(Config, [Size|Rest], Params, Expect, ReplyCommand) ->
 1914:     Packet = random_packet(Size),
 1915:     Fmt = io_lib:format("~c~p", [hd(Packet), Size]),
 1916:     expect_input1(Config, Rest, Params, [Packet|Expect], [Fmt|ReplyCommand]);
 1917: expect_input1(Config, [], {HSize, CmdLine0, Options}, Expect, ReplyCommand) ->
 1918:     CmdLine = build_cmd_line(CmdLine0, ReplyCommand, []),
 1919:     port_expect(Config, [{false, lists:reverse(Expect)}],
 1920: 		HSize, CmdLine, Options).
 1921: 
 1922: build_cmd_line(FixedCmdLine, [Cmd|Rest], []) ->
 1923:     build_cmd_line(FixedCmdLine, Rest, [Cmd]);
 1924: build_cmd_line(FixedCmdLine, [Cmd|Rest], Result) ->
 1925:     build_cmd_line(FixedCmdLine, Rest, [Cmd, $:|Result]);
 1926: build_cmd_line(FixedCmdLine, [], Result) ->
 1927:     lists:flatten([FixedCmdLine, " -r", Result, " -n"]).
 1928: 
 1929: %% port_expect(Actions, HSize, CmdLine, Options)
 1930: %%
 1931: %% Actions = [{Send, ExpectList}|Rest]
 1932: %% HSize = 0 (stream), or 1, 2, 4   (header size aka "packet bytes")
 1933: %% CmdLine = Command line for port_test.  Don't include -h<digit>.
 1934: %% Options = Options for open_port/2.  Don't include {packet, Number} or
 1935: %%           or stream.
 1936: %%
 1937: %% Send = false | list()
 1938: %% ExpectList = List of lists or binaries.
 1939: %%
 1940: %% Returns the port.
 1941: 
 1942: port_expect(Config, Actions, HSize, CmdLine, Options0) ->
 1943: %    io:format("port_expect(~p, ~p, ~p, ~p)",
 1944: %		[Actions, HSize, CmdLine, Options0]),
 1945:     PortTest = port_test(Config),
 1946:     Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]),
 1947:     PortType =
 1948: 	case HSize of
 1949: 	    0 -> stream;
 1950: 	    _ -> {packet, HSize}
 1951: 	end,
 1952:     Options = [PortType|Options0],
 1953:     io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]),
 1954:     Port = open_port({spawn, Cmd}, Options),
 1955:     port_expect(Port, Actions, Options),
 1956:     Port.
 1957: 
 1958: port_expect(Port, [{Send, Expects}|Rest], Options) when is_list(Expects) ->
 1959:     port_send(Port, Send),
 1960:     IsBinaryPort = lists:member(binary, Options),
 1961:     Receiver =
 1962: 	case {lists:member(stream, Options), line_option(Options)} of
 1963: 	    {false, _} -> fun receive_all/2;
 1964: 	    {true,false}  -> fun stream_receive_all/2;
 1965: 	    {_, true} -> fun receive_all/2
 1966: 	end,
 1967:     Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)),
 1968:     port_expect(Port, Rest, Options);
 1969: port_expect(_, [], _) ->
 1970:     ok.
 1971: 
 1972: %%% Check for either line or {line,N} in option list
 1973: line_option([{line,_}|_]) ->
 1974:     true;
 1975: line_option([line|_]) ->
 1976:     true;
 1977: line_option([_|T]) ->
 1978:     line_option(T);
 1979: line_option([]) ->
 1980:     false.
 1981: 
 1982: any_list_to_binary({Atom, List}) ->
 1983:     {Atom, list_to_binary(List)};
 1984: any_list_to_binary(List) ->
 1985:     list_to_binary(List).
 1986: 
 1987: maybe_to_binary(Expects, true) ->
 1988:     lists:map(fun any_list_to_binary/1, Expects);
 1989: maybe_to_binary(Expects, false) ->
 1990:     Expects.
 1991: 
 1992: port_send(_Port, false) -> ok;
 1993: port_send(Port, Send) when is_list(Send) ->
 1994: %    io:format("port_send(~p, ~p)", [Port, Send]),
 1995:     Port ! {self(), {command, Send}}.
 1996: 
 1997: receive_all(Port, [Expect|Rest]) ->
 1998: %    io:format("receive_all(~p, [~p|Rest])", [Port, Expect]),
 1999:     receive
 2000: 	{Port, {data, Expect}} ->
 2001: 	    io:format("Received ~s", [format(Expect)]),
 2002: 	    ok;
 2003: 	{Port, {data, Other}} ->
 2004: 	    io:format("Received ~s; expected ~s",
 2005: 		      [format(Other), format(Expect)]),
 2006: 	    test_server:fail(bad_message);
 2007: 	Other ->
 2008: 	    %% (We're not yet prepared for receiving both 'eol' and
 2009: 	    %% 'exit_status'; remember that they may appear in any order.)
 2010: 	    case {Expect, Rest, Other} of
 2011: 		{eof, [], {Port, eof}} ->
 2012: 		    io:format("Received soft EOF.",[]),
 2013: 		    ok;
 2014: 		{{exit_status, S}, [], {Port, {exit_status, S}}} ->
 2015: 		    io:format("Received exit status ~p.",[S]),
 2016: 		    ok;
 2017: 		_ ->
 2018: %%%	            io:format("Unexpected message: ~s", [format(Other)]),
 2019: 		    io:format("Unexpected message: ~w", [Other]),
 2020: 		    test_server:fail(unexpected_message)
 2021: 	    end
 2022:     end,
 2023:     receive_all(Port, Rest);
 2024: receive_all(_Port, []) ->
 2025:     ok.
 2026: 
 2027: stream_receive_all(Port, [Expect]) ->
 2028:     stream_receive_all1(Port, Expect).
 2029: 
 2030: stream_receive_all1(_Port, Empty) when is_binary(Empty), size(Empty) == 0 ->
 2031:     ok;
 2032: stream_receive_all1(_Port, []) ->
 2033:     ok;
 2034: stream_receive_all1(Port, Expect) ->
 2035:     receive
 2036: 	{Port, {data, Data}} ->
 2037: 	    Remaining = compare(Data, Expect),
 2038: 	    stream_receive_all1(Port, Remaining);
 2039: 	Other ->
 2040: 	    test_server:fail({bad_message, Other})
 2041:     end.
 2042: 
 2043: compare(B1, B2) when is_binary(B1), is_binary(B2), byte_size(B1) =< byte_size(B2) ->
 2044:     case split_binary(B2, size(B1)) of
 2045: 	{B1,Remaining} ->
 2046: 	    Remaining;
 2047: 	_Other ->
 2048: 	    test_server:fail(nomatch)
 2049:     end;
 2050: compare(B1, B2) when is_binary(B1), is_binary(B2) ->
 2051:     test_server:fail(too_much_data);
 2052: compare([X|Rest1], [X|Rest2]) ->
 2053:     compare(Rest1, Rest2);
 2054: compare([_|_], [_|_]) ->
 2055:     test_server:fail(nomatch);
 2056: compare([], Remaining) ->
 2057:     Remaining;
 2058: compare(_Data, []) ->
 2059:     test_server:fail(too_much_data).
 2060: 
 2061: maybe_to_list(Bin) when is_binary(Bin) ->
 2062:     binary_to_list(Bin);
 2063: maybe_to_list(List) ->
 2064:     List.
 2065: 
 2066: format({Eol,List}) ->
 2067:     io_lib:format("tuple<~w,~s>",[Eol, maybe_to_list(List)]);
 2068: format(List) when is_list(List) ->
 2069:     case list_at_least(50, List) of
 2070: 	true ->
 2071: 	    io_lib:format("\"~-50s...\"", [List]);
 2072: 	false ->
 2073: 	    io_lib:format("~p", [List])
 2074:     end;
 2075: format(Bin) when is_binary(Bin), size(Bin) >= 50 ->
 2076:     io_lib:format("binary<~-50s...>", [binary_to_list(Bin, 1, 50)]);
 2077: format(Bin) when is_binary(Bin) ->
 2078:     io_lib:format("binary<~s>", [binary_to_list(Bin)]).
 2079: 
 2080: 
 2081: list_at_least(Number, [_|Rest]) when Number > 0 ->
 2082:     list_at_least(Number-1, Rest);
 2083: list_at_least(Number, []) when Number > 0 ->
 2084:     false;
 2085: list_at_least(0, _List) -> true.
 2086: 
 2087: 
 2088: %%% Utility functions.
 2089: 
 2090: random_packet(Size) ->
 2091:     random_packet(Size, "").
 2092: 
 2093: random_packet(Size, Prefix) ->
 2094:     build_packet(Size-length(Prefix), lists:reverse(Prefix), random_char()).
 2095: 
 2096: build_packet(0, Result, _NextChar) ->
 2097:     lists:reverse(Result);
 2098: build_packet(Left, Result, NextChar0) ->
 2099:     NextChar =
 2100: 	if
 2101: 	    NextChar0 >= 126 ->
 2102: 		33;
 2103: 	    true ->
 2104: 		NextChar0+1
 2105: 	end,
 2106:     build_packet(Left-1, [NextChar0|Result], NextChar).
 2107: 
 2108: sizes() ->
 2109:     [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
 2110: 	32767, 32768, 65535, 65536].
 2111: 
 2112: sizes(Header_Size) ->
 2113:     sizes(Header_Size, sizes(), []).
 2114: 
 2115: sizes(1, [Packet_Size|Rest], Result) when Packet_Size < 256 ->
 2116:     sizes(1, Rest, [Packet_Size|Result]);
 2117: sizes(2, [Packet_Size|Rest], Result) when Packet_Size < 65536 ->
 2118:     sizes(2, Rest, [Packet_Size|Result]);
 2119: sizes(4, [Packet_Size|Rest], Result) ->
 2120:     sizes(4, Rest, [Packet_Size|Result]);
 2121: sizes(_, _, Result) ->
 2122:     Result.
 2123: 
 2124: random_char() ->
 2125:     random_char("abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789").
 2126: 
 2127: random_char(Chars) ->
 2128:     lists:nth(uniform(length(Chars)), Chars).
 2129: 
 2130: uniform(N) ->
 2131:     case get(random_seed) of
 2132: 	undefined ->	    
 2133: 	    {X, Y, Z} = Seed = time(),
 2134: 	    io:format("Random seed = ~p\n",[Seed]),
 2135: 	    random:seed(X, Y, Z);
 2136: 	_ ->
 2137: 	    ok
 2138:     end,
 2139:     random:uniform(N).
 2140: 
 2141: fun_spawn(Fun) ->
 2142:     fun_spawn(Fun, []).
 2143: 
 2144: fun_spawn(Fun, Args) ->
 2145:     spawn_link(erlang, apply, [Fun, Args]).
 2146: 
 2147: port_test(Config) when is_list(Config) ->
 2148:     filename:join(?config(data_dir, Config), "port_test").
 2149: 
 2150: 
 2151: ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports";
 2152: ports(suite) -> [];
 2153: ports(Config) when is_list(Config) ->
 2154:     Path = ?config(data_dir, Config),
 2155:     ok = load_driver(Path, "exit_drv"),
 2156: 
 2157:     receive after 1000 -> ok end, % Wait for other ports to stabilize
 2158: 
 2159:     OtherPorts = erlang:ports(),
 2160:     io:format("Other ports: ~p\n",[OtherPorts]),
 2161:     MaxPorts = 1024 - length(OtherPorts),
 2162: 
 2163:     TrafficPid = spawn_link(fun() -> ports_traffic(MaxPorts) end),
 2164: 
 2165:     ports_snapshots(100, TrafficPid, OtherPorts),
 2166:     TrafficPid ! {self(),die},
 2167:     receive {TrafficPid, dead} -> ok end,
 2168:     ok.
 2169: 
 2170: ports_snapshots(0, _, _) ->
 2171:     ok;
 2172: ports_snapshots(Iter, TrafficPid, OtherPorts) ->
 2173: 
 2174:     TrafficPid ! start,    
 2175:     receive after 1 -> ok end,
 2176: 
 2177:     Snapshot = erlang:ports(),
 2178: 
 2179:     TrafficPid ! {self(), stop},
 2180:     receive {TrafficPid, EventList, TrafficPorts} -> ok end,
 2181:     
 2182:     %%io:format("Snapshot=~p\n", [Snapshot]),
 2183:     ports_verify(Snapshot, OtherPorts ++ TrafficPorts, EventList),
 2184: 
 2185:     ports_snapshots(Iter-1, TrafficPid, OtherPorts).
 2186: 
 2187: 
 2188: ports_traffic(MaxPorts) ->
 2189:     ports_traffic_stopped(MaxPorts, {[],0}).
 2190: 
 2191: ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) ->
 2192:     receive
 2193: 	start ->
 2194: 	    %%io:format("Traffic started in ~p\n",[self()]),
 2195: 	    ports_traffic_started(MaxPorts, {PortList, PortCnt}, []);
 2196: 	{Pid,die} ->
 2197: 	    lists:foreach(fun(Port)-> erlang:port_close(Port) end,
 2198: 				PortList),
 2199: 	    Pid ! {self(),dead}
 2200:     end.
 2201: 
 2202: ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) ->
 2203:     receive 
 2204: 	{Pid, stop} ->
 2205: 	    %%io:format("Traffic stopped in ~p\n",[self()]),
 2206: 	    Pid ! {self(), EventList, PortList},
 2207: 	    ports_traffic_stopped(MaxPorts, {PortList, PortCnt})
 2208: 
 2209:     after 0 ->
 2210: 	    ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList)
 2211:     end.
 2212: 
 2213: ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) ->
 2214:     N = uniform(MaxPorts),
 2215:     case N > PortCnt of
 2216: 	true -> % Open port	    
 2217: 	    P = open_port({spawn, "exit_drv"}, []),
 2218: 	    %%io:format("Created port ~p\n",[P]),
 2219: 	    ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1},
 2220: 				  [{open,P}|EventList]);
 2221: 	
 2222: 	false -> % Close port
 2223: 	    P = lists:nth(N, PortList),
 2224: 	    %%io:format("Close port ~p\n",[P]),
 2225: 	    true = erlang:port_close(P),
 2226: 	    ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1},
 2227: 				  [{close,P}|EventList])
 2228:     end.
 2229: 
 2230: ports_verify(Ports, PortsAfter, EventList) ->    
 2231:     %%io:format("Candidate=~p\nEvents=~p\n", [PortsAfter, EventList]),
 2232:     case lists:sort(Ports) =:= lists:sort(PortsAfter) of
 2233: 	true ->
 2234: 	    io:format("Snapshot of ~p ports verified ok.\n",[length(Ports)]),
 2235: 	    ok;
 2236: 	false ->
 2237: 	    %% Note that we track the event list "backwards", undoing open/close:
 2238: 	    case EventList of
 2239: 		[{open,P} | Tail] ->
 2240: 		    ports_verify(Ports, lists:delete(P,PortsAfter), Tail);		    
 2241: 
 2242: 		[{close,P} | Tail] ->
 2243: 		    ports_verify(Ports, [P | PortsAfter], Tail);		    
 2244: 
 2245: 		[] ->
 2246: 		    test_server:fail("Inconsistent snapshot from erlang:ports()")
 2247: 	    end
 2248:     end.
 2249: 
 2250: load_driver(Dir, Driver) ->
 2251:     case erl_ddll:load_driver(Dir, Driver) of
 2252: 	ok -> ok;
 2253: 	{error, Error} = Res ->
 2254: 	    io:format("~s\n", [erl_ddll:format_error(Error)]),
 2255: 	    Res
 2256:     end.
 2257: 
 2258: 
 2259: close_deaf_port(doc) -> ["Send data to port program that does not read it, then close port."
 2260: 			 "Primary targeting Windows to test threaded_handle_closer in sys.c"];
 2261: close_deaf_port(suite) -> [];
 2262: close_deaf_port(Config) when is_list(Config) ->
 2263:     Dog = test_server:timetrap(test_server:seconds(100)),
 2264:     DataDir = ?config(data_dir, Config),
 2265:     DeadPort = os:find_executable("dead_port", DataDir),
 2266:     Port = open_port({spawn,DeadPort++" 60"},[]),
 2267:     erlang:port_command(Port,"Hello, can you hear me!?!?"),
 2268:     port_close(Port),
 2269: 
 2270:     Res = close_deaf_port_1(0, DeadPort),
 2271:     io:format("Waiting for OS procs to terminate...\n"),
 2272:     receive after 5*1000 -> ok end,
 2273:     test_server:timetrap_cancel(Dog),
 2274:     Res.
 2275: 
 2276: close_deaf_port_1(1000, _) ->
 2277:     ok;
 2278: close_deaf_port_1(N, Cmd) ->
 2279:     Timeout = integer_to_list(random:uniform(5*1000)),
 2280:     try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
 2281:     	Port ->
 2282: 	    erlang:port_command(Port,"Hello, can you hear me!?!?"),
 2283: 	    port_close(Port),
 2284: 	    close_deaf_port_1(N+1, Cmd)
 2285:     catch
 2286:     	_:eagain ->
 2287: 	    {comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."}
 2288:     end.
 2289: 
 2290: wait_until(Fun) ->
 2291:     case catch Fun() of
 2292: 	true ->
 2293: 	    ok;
 2294: 	_ ->
 2295: 	    receive after 100 -> ok end,
 2296: 	    wait_until(Fun)
 2297:     end.