aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test')
-rw-r--r--lib/kernel/test/file_SUITE.erl184
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl17
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl11
-rw-r--r--lib/kernel/test/global_SUITE.erl13
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl60
-rw-r--r--lib/kernel/test/interactive_shell_SUITE_data/.gitignore1
-rw-r--r--lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl6
-rw-r--r--lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl6
8 files changed, 281 insertions, 17 deletions
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 21aaefa654..9efe83d8b3 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -58,6 +58,8 @@
-export([ file_info_basic_file/1, file_info_basic_directory/1,
file_info_bad/1, file_info_times/1, file_write_file_info/1,
file_wfi_helpers/1]).
+-export([ file_handle_info_basic_file/1, file_handle_info_basic_directory/1,
+ file_handle_info_times/1]).
-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
read_write/1, pread_write/1, append/1, exclusive/1]).
-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
@@ -153,7 +155,10 @@ groups() ->
{pos, [], [pos1, pos2, pos3]},
{file_info, [],
[file_info_basic_file, file_info_basic_directory,
- file_info_bad, file_info_times, file_write_file_info,
+ file_info_bad, file_info_times,
+ file_handle_info_basic_file, file_handle_info_basic_directory,
+ file_handle_info_times,
+ file_write_file_info,
file_wfi_helpers]},
{consult, [], [consult1, path_consult]},
{eval, [], [eval1, path_eval]},
@@ -1417,7 +1422,8 @@ file_info_basic_directory(Config) when is_list(Config) ->
{win32, _} ->
test_directory("/", read_write),
test_directory("c:/", read_write),
- test_directory("c:\\", read_write);
+ test_directory("c:\\", read_write),
+ test_directory("\\\\localhost\\c$", read_write);
_ ->
test_directory("/", read)
end,
@@ -1549,6 +1555,180 @@ filter_atime(Atime, Config) ->
Atime
end.
+%% Test read_file_info on I/O devices.
+
+file_handle_info_basic_file(Config) when is_list(Config) ->
+ RootDir = proplists:get_value(priv_dir, Config),
+
+ %% Create a short file.
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_basic_test.fil"),
+ {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ io:put_chars(Fd1, "foo bar"),
+ ok = ?FILE_MODULE:close(Fd1),
+
+ %% Test that the file has the expected attributes.
+ %% The times are tricky, so we will save them to a separate test case.
+
+ {ok, Fd} = ?FILE_MODULE:open(Name, read),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Fd),
+ ok = ?FILE_MODULE:close(Fd),
+
+ {ok, FdRaw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfoRaw} = ?FILE_MODULE:read_file_info(FdRaw),
+ ok = ?FILE_MODULE:close(FdRaw),
+
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo = FileInfoRaw,
+ io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
+ Size = 7,
+ Type = regular,
+ read_write = Access,
+ true = abs(time_dist(filter_atime(AccessTime, Config),
+ filter_atime(ModifyTime,
+ Config))) < 5,
+ all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+
+ [] = flush(),
+ ok.
+
+file_handle_info_basic_directory(Config) when is_list(Config) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+ RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
+
+ %% Test that the RootDir directory has the expected attributes.
+ test_directory_handle(RootDir, read_write),
+
+ %% Note that on Windows file systems,
+ %% "/" or "c:/" are *NOT* directories.
+ %% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were
+ %% directories.
+ case os:type() of
+ {win32, _} ->
+ test_directory_handle("/", read_write),
+ test_directory_handle("c:/", read_write),
+ test_directory_handle("c:\\", read_write),
+ test_directory_handle("\\\\localhost\\c$", read_write);
+ _ ->
+ test_directory_handle("/", read)
+ end,
+ ok.
+
+test_directory_handle(Name, ExpectedAccess) ->
+ {ok, DirFd} = file:open(Name, [read, directory]),
+ try
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd, [raw]),
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo,
+ io:format("Testing directory ~s", [Name]),
+ io:format("Directory size is ~p", [Size]),
+ io:format("Access ~p", [Access]),
+ io:format("Access time ~p; Modify time~p",
+ [AccessTime, ModifyTime]),
+ Type = directory,
+ Access = ExpectedAccess,
+ all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+ [] = flush(),
+ ok
+ after
+ file:close(DirFd)
+ end.
+
+%% Test that the file times behave as they should.
+
+file_handle_info_times(Config) when is_list(Config) ->
+ %% We have to try this twice, since if the test runs across the change
+ %% of a month the time diff calculations will fail. But it won't happen
+ %% if you run it twice in succession.
+ test_server:m_out_of_n(
+ 1,2,
+ fun() -> file_handle_info_int(Config) end),
+ ok.
+
+file_handle_info_int(Config) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+
+ RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
+ io:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_file_info.fil"),
+ {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ io:put_chars(Fd1,"foo"),
+ {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Fd1),
+ ok = ?FILE_MODULE:close(Fd1),
+
+ {ok,Fd1Raw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Fd1Raw),
+ ok = ?FILE_MODULE:close(Fd1Raw),
+
+ %% We assert that everything but the size is the same, on some OSs the
+ %% size may not have been flushed to disc and we do not want to do a
+ %% sync to force it.
+ FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size },
+
+ #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1,
+
+ Now = erlang:localtime(), %???
+ io:format("Now ~p",[Now]),
+ io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]),
+ true = abs(time_dist(filter_atime(Now, Config),
+ filter_atime(AccTime1,
+ Config))) < 8,
+ true = abs(time_dist(Now,ModTime1)) < 8,
+
+ %% Sleep until we can be sure the seconds value has changed.
+ %% Note: FAT-based filesystem (like on Windows 95) have
+ %% a resolution of 2 seconds.
+ timer:sleep(2200),
+
+ %% close the file, and watch the modify date change
+
+ {ok,Fd2} = ?FILE_MODULE:open(Name, read),
+ {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Fd2),
+ ok = ?FILE_MODULE:close(Fd2),
+
+ {ok,Fd2Raw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfo2Raw} = ?FILE_MODULE:read_file_info(Fd2Raw),
+ ok = ?FILE_MODULE:close(Fd2Raw),
+
+ #file_info{size=Size,type=regular,access=Access,
+ atime=AccTime2,mtime=ModTime2} = FileInfo2 = FileInfo2Raw,
+ io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]),
+ true = time_dist(ModTime1,ModTime2) >= 0,
+
+ %% this file is supposed to be binary, so it'd better keep it's size
+ Size = 3,
+ Access = read_write,
+
+ %% Do some directory checking
+
+ {ok,Fd3} = ?FILE_MODULE:open(RootDir, [read, directory]),
+ {ok,FileInfo3} = ?FILE_MODULE:read_file_info(Fd3),
+ ok = ?FILE_MODULE:close(Fd3),
+
+ {ok,Fd3Raw} = ?FILE_MODULE:open(RootDir, [read, directory, raw]),
+ {ok,FileInfo3Raw} = ?FILE_MODULE:read_file_info(Fd3Raw),
+ ok = ?FILE_MODULE:close(Fd3Raw),
+
+ #file_info{size=DSize,type=directory,access=DAccess,
+ atime=AccTime3,mtime=ModTime3} = FileInfo3 = FileInfo3Raw,
+ %% this dir was modified only a few secs ago
+ io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]),
+ true = abs(time_dist(Now,ModTime3)) < 5,
+ DAccess = read_write,
+ io:format("Dir size is ~p",[DSize]),
+
+ [] = flush(),
+ ok.
+
%% Test the write_file_info/2 function.
file_write_file_info(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 421510f9d6..de87bd9472 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -2019,7 +2019,7 @@ recvtclass(_Config) ->
%% platforms - change {unix,_} to false?
%% pktoptions is not supported for IPv4
-recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0});
recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0});
%% Using the option returns einval, so it is not implemented.
recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0});
@@ -2031,7 +2031,7 @@ recvtos_ok({unix,_}, _) -> true;
recvtos_ok(_, _) -> false.
%% pktoptions is not supported for IPv4
-recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0});
recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0});
%% Using the option returns einval, so it is not implemented.
recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0});
@@ -2043,7 +2043,7 @@ recvttl_ok({unix,_}, _) -> true;
recvttl_ok(_, _) -> false.
%% pktoptions is not supported for IPv6
-recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0});
recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0});
recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
%% Using the option returns einval, so it is not implemented.
@@ -2224,18 +2224,19 @@ collect_accepts(N,Tmo) ->
A = millis(),
receive
{accepted,P,Msg} ->
- [{P,Msg}] ++ collect_accepts(N-1,Tmo-(millis() - A))
+ NextN = if N =:= infinity -> N; true -> N - 1 end,
+ [{P,Msg}] ++ collect_accepts(NextN, Tmo - (millis()-A))
after Tmo ->
[]
end.
-define(EXPECT_ACCEPTS(Pattern,N,Timeout),
(fun() ->
- case collect_accepts(if N =:= infinity -> -1; true -> N end,Timeout) of
+ case collect_accepts((N), (Timeout)) of
Pattern ->
ok;
- Other ->
- {error,{unexpected,{Other,process_info(self(),messages)}}}
+ Other__ ->
+ {error,{unexpected,{Other__,process_info(self(),messages)}}}
end
end)()).
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 730886865c..70d8caf478 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -646,7 +646,7 @@ sendtclass(_Config) ->
%% Using the option returns einval, so it is not implemented.
recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0});
%% Using the option returns einval, so it is not implemented.
-recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0});
+recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0});
%% Using the option returns einval, so it is not implemented.
recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
%%
@@ -675,7 +675,7 @@ recvtclass_ok(_, _) -> false.
%% Using the option returns einval, so it is not implemented.
sendtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0});
-sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0});
+sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0});
sendtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
sendtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0});
sendtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0});
@@ -689,7 +689,8 @@ sendttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0});
%% Using the option returns enoprotoopt, so it is not implemented.
sendttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0});
%% Option has no effect
-sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0});
+sendttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
+sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0});
%%
sendttl_ok({unix,_}, _) -> true;
sendttl_ok(_, _) -> false.
@@ -697,6 +698,8 @@ sendttl_ok(_, _) -> false.
%% Using the option returns einval, so it is not implemented.
sendtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0});
sendtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11});
+%% Option has no effect
+sendtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0});
%%
sendtclass_ok({unix,_}, _) -> true;
sendtclass_ok(_, _) -> false.
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 8eab36e308..5bff9cc292 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -2512,8 +2512,10 @@ re_register_name(Config) when is_list(Config) ->
Me = self(),
Pid1 = spawn(fun() -> proc(Me) end),
yes = global:register_name(name, Pid1),
+ wait_for_monitor(Pid1),
Pid2 = spawn(fun() -> proc(Me) end),
_ = global:re_register_name(name, Pid2),
+ wait_for_monitor(Pid2),
Pid2 ! die,
Pid1 ! die,
receive {Pid1, MonitoredBy1} -> [] = MonitoredBy1 end,
@@ -2522,6 +2524,15 @@ re_register_name(Config) when is_list(Config) ->
init_condition(Config),
ok.
+wait_for_monitor(Pid) ->
+ case process_info(Pid, monitored_by) of
+ {monitored_by, []} ->
+ timer:sleep(1),
+ wait_for_monitor(Pid);
+ {monitored_by, [_]} ->
+ ok
+ end.
+
proc(Parent) ->
receive die -> ok end,
{monitored_by, MonitoredBy} = process_info(self(), monitored_by),
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index 298a364a91..173e25c520 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -23,7 +23,8 @@
init_per_group/2,end_per_group/2,
get_columns_and_rows/1, exit_initial/1, job_control_local/1,
job_control_remote/1,
- job_control_remote_noshell/1,ctrl_keys/1]).
+ job_control_remote_noshell/1,ctrl_keys/1,
+ get_columns_and_rows_escript/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% For spawn
@@ -40,7 +41,8 @@ suite() ->
{timetrap,{minutes,3}}].
all() ->
- [get_columns_and_rows, exit_initial, job_control_local,
+ [get_columns_and_rows_escript,get_columns_and_rows,
+ exit_initial, job_control_local,
job_control_remote, job_control_remote_noshell,
ctrl_keys].
@@ -72,6 +74,60 @@ end_per_group(_GroupName, Config) ->
-define(dbg(Data),noop).
-endif.
+string_to_term(Str) ->
+ {ok,Tokens,_EndLine} = erl_scan:string(Str ++ "."),
+ {ok,AbsForm} = erl_parse:parse_exprs(Tokens),
+ {value,Value,_Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()),
+ Value.
+
+run_unbuffer_escript(Rows, Columns, EScript, NoTermStdIn, NoTermStdOut) ->
+ DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"),
+ TmpFile = filename:join(DataDir, "tmp"),
+ ok = file:write_file(TmpFile, <<>>),
+ CommandModifier =
+ case {NoTermStdIn, NoTermStdOut} of
+ {false, false} -> "";
+ {true, false} -> io_lib:format(" < ~s", [TmpFile]);
+ {false, true} -> io_lib:format(" > ~s ; cat ~s", [TmpFile, TmpFile]);
+ {true, true} -> io_lib:format(" > ~s < ~s ; cat ~s", [TmpFile, TmpFile, TmpFile])
+ end,
+ Command = io_lib:format("unbuffer -p bash -c \"stty rows ~p; stty columns ~p; escript ~s ~s\"",
+ [Rows, Columns, EScript, CommandModifier]),
+ %% io:format("Command: ~s ~n", [Command]),
+ Out = os:cmd(Command),
+ %% io:format("Out: ~p ~n", [Out]),
+ string_to_term(Out).
+
+get_columns_and_rows_escript(Config) when is_list(Config) ->
+ ExpectUnbufferInstalled =
+ try
+ "79" = string:trim(os:cmd("unbuffer -p bash -c \"stty columns 79 ; tput cols\"")),
+ true
+ catch
+ _:_ -> false
+ end,
+ case ExpectUnbufferInstalled of
+ false ->
+ {skip,
+ "The unbuffer tool (https://core.tcl-lang.org/expect/index) does not seem to be installed.~n"
+ "On Ubuntu/Debian: \"sudo apt-get install expect\""};
+ true ->
+ DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"),
+ IoColumnsErl = filename:join(DataDir, "io_columns.erl"),
+ IoRowsErl = filename:join(DataDir, "io_rows.erl"),
+ [
+ begin
+ {ok, 42} = run_unbuffer_escript(99, 42, IoColumnsErl, NoTermStdIn, NoTermStdOut),
+ {ok, 99} = run_unbuffer_escript(99, 42, IoRowsErl, NoTermStdIn, NoTermStdOut)
+ end
+ ||
+ {NoTermStdIn, NoTermStdOut} <- [{false, false}, {true, false}, {false, true}]
+ ],
+ {error,enotsup} = run_unbuffer_escript(99, 42, IoRowsErl, true, true),
+ {error,enotsup} = run_unbuffer_escript(99, 42, IoColumnsErl, true, true),
+ ok
+ end.
+
%% Test that the shell can access columns and rows.
get_columns_and_rows(Config) when is_list(Config) ->
case proplists:get_value(default_shell,Config) of
diff --git a/lib/kernel/test/interactive_shell_SUITE_data/.gitignore b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore
new file mode 100644
index 0000000000..1c2f433de1
--- /dev/null
+++ b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore
@@ -0,0 +1 @@
+tmp \ No newline at end of file
diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl
new file mode 100644
index 0000000000..32d0cf25df
--- /dev/null
+++ b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl
@@ -0,0 +1,6 @@
+-module(io_columns).
+
+-export([main/1]).
+
+main(_) ->
+ io:format("~p",[io:columns()]).
diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl
new file mode 100644
index 0000000000..53ceb464b0
--- /dev/null
+++ b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl
@@ -0,0 +1,6 @@
+-module(io_rows).
+
+-export([main/1]).
+
+main(_) ->
+ io:format("~p",[io:rows()]).