aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/file_SUITE.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/test/file_SUITE.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/kernel/test/file_SUITE.erl')
-rw-r--r--lib/kernel/test/file_SUITE.erl3716
1 files changed, 3716 insertions, 0 deletions
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
new file mode 100644
index 0000000000..c645d0f842
--- /dev/null
+++ b/lib/kernel/test/file_SUITE.erl
@@ -0,0 +1,3716 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% This is a developement feature when developing a new file module,
+%% ugly but practical.
+-ifndef(FILE_MODULE).
+-define(FILE_MODULE, file).
+-endif.
+-ifndef(FILE_SUITE).
+-define(FILE_SUITE, file_SUITE).
+-endif.
+-ifndef(FILE_INIT).
+-define(FILE_INIT(Config), Config).
+-endif.
+-ifndef(FILE_FINI).
+-define(FILE_FINI(Config), Config).
+-endif.
+-ifndef(FILE_INIT_PER_TESTCASE).
+-define(FILE_INIT_PER_TESTCASE(Config), Config).
+-endif.
+-ifndef(FILE_FIN_PER_TESTCASE).
+-define(FILE_FIN_PER_TESTCASE(Config), Config).
+-endif.
+
+-module(?FILE_SUITE).
+
+-export([all/1,
+ init/1, fini/1,
+ init_per_testcase/2, fin_per_testcase/2,
+ read_write_file/1, dirs/1, files/1, names/1]).
+-export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1,
+ pos/1, pos1/1, pos2/1]).
+-export([close/1, consult/1, consult1/1, path_consult/1, delete/1]).
+-export([eval/1, eval1/1, path_eval/1, script/1, script1/1, path_script/1,
+ open/1, open1/1,
+ old_modes/1, new_modes/1, path_open/1, open_errors/1]).
+-export([file_info/1, file_info_basic_file/1, file_info_basic_directory/1,
+ file_info_bad/1, file_info_times/1, file_write_file_info/1]).
+-export([rename/1, access/1, truncate/1, sync/1,
+ read_write/1, pread_write/1, append/1]).
+-export([errors/1, e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
+-export([otp_5814/1]).
+
+-export([compression/1, read_not_really_compressed/1,
+ read_compressed_cooked/1, read_compressed_cooked_binary/1,
+ read_cooked_tar_problem/1,
+ write_compressed/1, compress_errors/1, catenated_gzips/1]).
+
+-export([links/1, make_link/1, read_link_info_for_non_link/1, symlinks/1]).
+
+-export([copy/1]).
+
+-export([new_slave/2, old_slave/2, run_test/2]).
+
+-export([delayed_write/1, read_ahead/1, segment_read/1, segment_write/1]).
+
+-export([ipread/1]).
+
+-export([pid2name/1]).
+
+-export([interleaved_read_write/1]).
+
+-export([altname/1]).
+
+-export([large_file/1]).
+
+-export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]).
+
+%% Debug exports
+-export([create_file_slow/2, create_file/2, create_bin/2]).
+-export([verify_file/2, verify_bin/3]).
+-export([bytes/2, iterate/3]).
+
+
+
+-include("test_server.hrl").
+-include_lib("kernel/include/file.hrl").
+
+
+
+all(suite) ->
+ {conf, init,
+ [altname, read_write_file, dirs, files,
+ delete, rename, names, errors,
+ compression, links, copy,
+ delayed_write, read_ahead, segment_read, segment_write,
+ ipread, pid2name, interleaved_read_write,
+ otp_5814, large_file, read_line_1, read_line_2, read_line_3, read_line_4],
+ fini}.
+
+init(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ Priv = ?config(priv_dir, Config),
+ HasAccessTime =
+ case ?FILE_MODULE:read_file_info(Priv) of
+ {ok, #file_info{atime={_, {0, 0, 0}}}} ->
+ %% This is a unfortunately a FAT file system.
+ [no_access_time];
+ {ok, _} ->
+ []
+ end,
+ ?FILE_INIT(HasAccessTime++Config);
+ _ ->
+ ?FILE_INIT(Config)
+ end.
+
+fini(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ os:cmd("subst z: /d");
+ _ ->
+ ok
+ end,
+ ?FILE_FINI(Config).
+
+init_per_testcase(_Func, Config) ->
+ %%error_logger:info_msg("~p:~p *****~n", [?MODULE, _Func]),
+ ?FILE_INIT_PER_TESTCASE(Config).
+
+fin_per_testcase(_Func, Config) ->
+ %% error_logger:info_msg("~p:~p END *****~n", [?MODULE, _Func]),
+ ?FILE_FIN_PER_TESTCASE(Config).
+
+%% Matches a term (the last) against alternatives
+expect(X, _, X) ->
+ X;
+expect(_, X, X) ->
+ X.
+
+expect(X, _, _, X) ->
+ X;
+expect(_, X, _, X) ->
+ X;
+expect(_, _, X, X) ->
+ X.
+
+expect(X, _, _, _, X) ->
+ X;
+expect(_, X, _, _, X) ->
+ X;
+expect(_, _, X, _, X) ->
+ X;
+expect(_, _, _, X, X) ->
+ X.
+
+%% Calculate the time difference
+time_dist({YY, MM, DD, H, M, S}, DT) ->
+ time_dist({{YY, MM, DD}, {H, M, S}}, DT);
+time_dist(DT, {YY, MM, DD, H, M, S}) ->
+ time_dist(DT, {{YY, MM, DD}, {H, M, S}});
+time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) ->
+ calendar:datetime_to_gregorian_seconds(DT2)
+ - calendar:datetime_to_gregorian_seconds(DT1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+read_write_file(suite) -> [];
+read_write_file(doc) -> [];
+read_write_file(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_read_write_file"),
+
+ %% Try writing and reading back some term
+ ?line SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]},
+ ?line ok = ?FILE_MODULE:write_file(Name,term_to_binary(SomeTerm)),
+ ?line {ok,Bin1} = ?FILE_MODULE:read_file(Name),
+ ?line SomeTerm = binary_to_term(Bin1),
+
+ %% Try a "null" term
+ ?line NullTerm = [],
+ ?line ok = ?FILE_MODULE:write_file(Name,term_to_binary(NullTerm)),
+ ?line {ok,Bin2} = ?FILE_MODULE:read_file(Name),
+ ?line NullTerm = binary_to_term(Bin2),
+
+ %% Try some "complicated" types
+ ?line BigNum = 123456789012345678901234567890,
+ ?line ComplTerm = {self(),make_ref(),BigNum,3.14159},
+ ?line ok = ?FILE_MODULE:write_file(Name,term_to_binary(ComplTerm)),
+ ?line {ok,Bin3} = ?FILE_MODULE:read_file(Name),
+ ?line ComplTerm = binary_to_term(Bin3),
+
+ %% Try reading a nonexistent file
+ ?line Name2 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_nonexistent_file"),
+ ?line {error, enoent} = ?FILE_MODULE:read_file(Name2),
+ ?line {error, enoent} = ?FILE_MODULE:read_file(""),
+ ?line {error, enoent} = ?FILE_MODULE:read_file(''),
+
+ % Try writing to a bad filename
+ ?line {error, enoent} =
+ ?FILE_MODULE:write_file("",term_to_binary(NullTerm)),
+
+ % Try writing something else than a binary
+ ?line {error, badarg} = ?FILE_MODULE:write_file(Name,{1,2,3}),
+ ?line {error, badarg} = ?FILE_MODULE:write_file(Name,self()),
+
+ %% Some non-term binaries
+ ?line ok = ?FILE_MODULE:write_file(Name,[]),
+ ?line {ok,Bin4} = ?FILE_MODULE:read_file(Name),
+ ?line 0 = byte_size(Bin4),
+
+ ?line ok = ?FILE_MODULE:write_file(Name,[Bin1,[],[[Bin2]]]),
+ ?line {ok,Bin5} = ?FILE_MODULE:read_file(Name),
+ ?line {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dirs(suite) -> [make_del_dir, cur_dir_0, cur_dir_1].
+
+make_del_dir(suite) -> [];
+make_del_dir(doc) -> [];
+make_del_dir(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_mk-dir"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line {error, eexist} = ?FILE_MODULE:make_dir(NewDir),
+ ?line ok = ?FILE_MODULE:del_dir(NewDir),
+ ?line {error, enoent} = ?FILE_MODULE:del_dir(NewDir),
+
+ %% Check that we get an error when trying to create...
+ %% a deep directory
+ ?line NewDir2 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_mk-dir/foo"),
+ ?line {error, enoent} = ?FILE_MODULE:make_dir(NewDir2),
+ %% a nameless directory
+ ?line {error, enoent} = ?FILE_MODULE:make_dir(""),
+ %% a directory with illegal name
+ ?line {error, badarg} = ?FILE_MODULE:make_dir({1,2,3}),
+
+ %% a directory with illegal name, even if it's a (bad) list
+ ?line {error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]),
+
+ %% Maybe this isn't an error, exactly, but worth mentioning anyway:
+ %% ok = ?FILE_MODULE:make_dir([$f,$o,$o,0,$b,$a,$r])),
+ %% The above line works, and created a directory "./foo"
+ %% More elegant would maybe have been to fail, or to really create
+ %% a directory, but with a name that incorporates the "bar" part of
+ %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same
+ %% dir. But this would slow it down.
+
+ %% Try deleting some bad directories
+ %% Deleting the parent directory to the current, sounds dangerous, huh?
+ %% Don't worry ;-) the parent directory should never be empty, right?
+ ?line {error, eexist} = ?FILE_MODULE:del_dir('..'),
+ ?line {error, enoent} = ?FILE_MODULE:del_dir(""),
+ ?line {error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+cur_dir_0(suite) -> [];
+cur_dir_0(doc) -> [];
+cur_dir_0(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ %% Find out the current dir, and cd to it ;-)
+ ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd(),
+ ?line Dir1 = BaseDir ++ "", %% Check that it's a string
+ ?line ok = ?FILE_MODULE:set_cwd(Dir1),
+
+ %% Make a new dir, and cd to that
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_curdir"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line io:format("cd to ~s",[NewDir]),
+ ?line ok = ?FILE_MODULE:set_cwd(NewDir),
+
+ %% Create a file in the new current directory, and check that it
+ %% really is created there
+ ?line UncommonName = "uncommon.fil",
+ ?line {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write),
+ ?line ok = ?FILE_MODULE:close(Fd),
+ ?line {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."),
+ ?line true = lists:member(UncommonName,NewDirFiles),
+
+ %% Delete the directory and return to the old current directory
+ %% and check that the created file isn't there (too!)
+ ?line expect({error, einval}, {error, eacces},
+ ?FILE_MODULE:del_dir(NewDir)),
+ ?line ?FILE_MODULE:delete(UncommonName),
+ ?line {ok,[]} = ?FILE_MODULE:list_dir("."),
+ ?line ok = ?FILE_MODULE:set_cwd(Dir1),
+ ?line io:format("cd back to ~s",[Dir1]),
+ ?line ok = ?FILE_MODULE:del_dir(NewDir),
+ ?line {error, enoent} = ?FILE_MODULE:set_cwd(NewDir),
+ ?line ok = ?FILE_MODULE:set_cwd(Dir1),
+ ?line io:format("cd back to ~s",[Dir1]),
+ ?line {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."),
+ ?line false = lists:member(UncommonName,OldDirFiles),
+
+ %% Try doing some bad things
+ ?line {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}),
+ ?line {error, enoent} = ?FILE_MODULE:set_cwd(""),
+ ?line {error, enoent} = ?FILE_MODULE:set_cwd(".......a......"),
+ ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd(), %% Still there?
+
+ %% On Windows, there should only be slashes, no backslashes,
+ %% in the return value of get_cwd().
+ %% (The test is harmless on Unix, because filenames usually
+ %% don't contain backslashes.)
+
+ ?line {ok, BaseDir} = ?FILE_MODULE:get_cwd(),
+ ?line false = lists:member($\\, BaseDir),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Tests ?FILE_MODULE:get_cwd/1.
+
+cur_dir_1(suite) -> [];
+cur_dir_1(doc) -> [];
+cur_dir_1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+
+ ?line case os:type() of
+ {unix, _} ->
+ ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
+ vxworks ->
+ ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
+ {win32, _} ->
+ win_cur_dir_1(Config)
+ end,
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+win_cur_dir_1(_Config) ->
+ ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd(),
+
+ %% Get the drive letter from the current directory,
+ %% and try to get current directory for that drive.
+
+ ?line [Drive,$:|_] = BaseDir,
+ ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd([Drive,$:]),
+ io:format("BaseDir = ~s\n", [BaseDir]),
+
+ %% Unfortunately, there is no way to move away from the
+ %% current drive as we can't use the "subst" command from
+ %% a SSH connection. We can't test any more.
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+files(suite) -> [open,pos,file_info,consult,eval,script,truncate,sync].
+
+open(suite) -> [open1,old_modes,new_modes,path_open,close,access,read_write,
+ pread_write,append,open_errors].
+
+open1(suite) -> [];
+open1(doc) -> [];
+open1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_files"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line Name = filename:join(NewDir, "foo1.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,read_write),
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read),
+ ?line Str = "{a,tuple}.\n",
+ ?line io:format(Fd1,Str,[]),
+ ?line {ok,0} = ?FILE_MODULE:position(Fd1,bof),
+ ?line Str = io:get_line(Fd1,''),
+ ?line Str = io:get_line(Fd2,''),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok,0} = ?FILE_MODULE:position(Fd1,bof),
+ ?line ok = ?FILE_MODULE:truncate(Fd1),
+ ?line eof = io:get_line(Fd1,''),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,Fd3} = ?FILE_MODULE:open(Name,read),
+ ?line eof = io:get_line(Fd3,''),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Tests all open modes.
+
+old_modes(suite) -> [];
+old_modes(doc) -> [];
+old_modes(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_old_open_modes"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line Name1 = filename:join(NewDir, "foo1.fil"),
+ ?line Marker = "hello, world",
+
+ %% write
+ ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, write),
+ ?line ok = io:write(Fd1, Marker),
+ ?line ok = io:put_chars(Fd1, ".\n"),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+
+ %% read
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name1, read),
+ ?line {ok, Marker} = io:read(Fd2, prompt),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+
+ %% read_write
+ ?line {ok, Fd3} = ?FILE_MODULE:open(Name1, read_write),
+ ?line {ok, Marker} = io:read(Fd3, prompt),
+ ?line ok = io:write(Fd3, Marker),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+new_modes(suite) -> [];
+new_modes(doc) -> [];
+new_modes(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_new_open_modes"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line Name1 = filename:join(NewDir, "foo1.fil"),
+ ?line Marker = "hello, world",
+
+ %% write
+ ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]),
+ ?line ok = io:write(Fd1, Marker),
+ ?line ok = io:put_chars(Fd1, ".\n"),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+
+ %% read
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name1, [read]),
+ ?line {ok, Marker} = io:read(Fd2, prompt),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+
+ %% read and write
+ ?line {ok, Fd3} = ?FILE_MODULE:open(Name1, [read, write]),
+ ?line {ok, Marker} = io:read(Fd3, prompt),
+ ?line ok = io:write(Fd3, Marker),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+
+ %% read by default
+ ?line {ok, Fd4} = ?FILE_MODULE:open(Name1, []),
+ ?line {ok, Marker} = io:read(Fd4, prompt),
+ ?line ok = ?FILE_MODULE:close(Fd4),
+
+ %% read and binary
+ ?line {ok, Fd5} = ?FILE_MODULE:open(Name1, [read, binary]),
+ ?line {ok, Marker} = io:read(Fd5, prompt),
+ ?line ok = ?FILE_MODULE:close(Fd5),
+
+ %% read, raw
+ ?line {ok, Fd6} = ?FILE_MODULE:open(Name1, [read, raw]),
+ ?line {ok, [$\[]} = ?FILE_MODULE:read(Fd6, 1),
+ ?line ok = ?FILE_MODULE:close(Fd6),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+path_open(suite) -> [];
+path_open(doc) -> [];
+path_open(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_path_open"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line FileName = "path_open.fil",
+ ?line Name = filename:join(RootDir, FileName),
+ ?line {ok,Fd1,_FullName1} =
+ ?FILE_MODULE:path_open(
+ [RootDir,
+ "nosuch1",
+ NewDir],FileName,write),
+ ?line io:format(Fd1,"ABCDEFGH",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+
+ %% locate it in the last dir
+ ?line {ok,Fd2,_FullName2} =
+ ?FILE_MODULE:path_open(
+ ["nosuch1",
+ NewDir,
+ RootDir],FileName,read),
+ ?line {ok,2} =
+ ?FILE_MODULE:position(Fd2,2), "C" = io:get_chars(Fd2,'',1),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ %% Try a failing path
+ ?line {error, enoent} = ?FILE_MODULE:path_open(
+ ["nosuch1",
+ NewDir],FileName,read),
+ %% Check that it's found regardless of path, if an absolute name given
+ ?line {ok,Fd3,_FullPath3} =
+ ?FILE_MODULE:path_open(
+ ["nosuch1",
+ NewDir],Name,read),
+ ?line {ok,2} =
+ ?FILE_MODULE:position(Fd3,2), "C" = io:get_chars(Fd3,'',1),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+close(suite) -> [];
+close(doc) -> [];
+close(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_close.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,read_write),
+ %% Just closing it is no fun, we did that a million times already
+ %% This is a common error, for code written before Erlang 4.3
+ %% bacause then ?FILE_MODULE:open just returned a Pid, and not everyone
+ %% really checked what they got.
+ ?line {'EXIT',_Msg} = (catch ok = ?FILE_MODULE:close({ok,Fd1})),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+
+ %% Try closing one more time
+ ?line Val = ?FILE_MODULE:close(Fd1),
+ ?line io:format("Second close gave: ~p",[Val]),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+access(suite) -> [];
+access(doc) -> [];
+access(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_access.fil"),
+ ?line Str = "ABCDEFGH",
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd1,Str,[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ %% Check that we can't write when in read only mode
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read),
+ ?line case catch io:format(Fd2,"XXXX",[]) of
+ ok ->
+ test_server:fail({format,write});
+ _ ->
+ ok
+ end,
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok,Fd3} = ?FILE_MODULE:open(Name,read),
+ ?line Str = io:get_line(Fd3,''),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Tests ?FILE_MODULE:read/2 and ?FILE_MODULE:write/2.
+
+read_write(suite) -> [];
+read_write(doc) -> [];
+read_write(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_read_write"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line Marker = "hello, world",
+ ?line MarkerB = list_to_binary(Marker),
+
+ %% Plain file.
+ ?line Name1 = filename:join(NewDir, "plain.fil"),
+ ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]),
+ ?line read_write_test(Fd1, Marker, []),
+
+ %% Raw file.
+ ?line Name2 = filename:join(NewDir, "raw.fil"),
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]),
+ ?line read_write_test(Fd2, Marker, []),
+
+ %% Plain binary file.
+ ?line Name3 = filename:join(NewDir, "plain-b.fil"),
+ ?line {ok, Fd3} = ?FILE_MODULE:open(Name3, [read, write, binary]),
+ ?line read_write_test(Fd3, MarkerB, <<>>),
+
+ %% Raw binary file.
+ ?line Name4 = filename:join(NewDir, "raw-b.fil"),
+ ?line {ok, Fd4} = ?FILE_MODULE:open(Name4, [read, write, raw, binary]),
+ ?line read_write_test(Fd4, MarkerB, <<>>),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+read_write_test(File, Marker, Empty) ->
+ ?line ok = ?FILE_MODULE:write(File, Marker),
+ ?line {ok, 0} = ?FILE_MODULE:position(File, 0),
+ ?line {ok, Empty} = ?FILE_MODULE:read(File, 0),
+ ?line {ok, Marker} = ?FILE_MODULE:read(File, 100),
+ ?line eof = ?FILE_MODULE:read(File, 100),
+ ?line {ok, Empty} = ?FILE_MODULE:read(File, 0),
+ ?line ok = ?FILE_MODULE:close(File),
+ ?line [] = flush(),
+ ok.
+
+
+%% Tests ?FILE_MODULE:pread/2 and ?FILE_MODULE:pwrite/2.
+
+pread_write(suite) -> [];
+pread_write(doc) -> [];
+pread_write(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_pread_write"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line List = "hello, world",
+ ?line Bin = list_to_binary(List),
+
+ %% Plain file.
+ ?line Name1 = filename:join(NewDir, "plain.fil"),
+ ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]),
+ ?line pread_write_test(Fd1, List),
+
+ %% Raw file.
+ ?line Name2 = filename:join(NewDir, "raw.fil"),
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]),
+ ?line pread_write_test(Fd2, List),
+
+ %% Plain file. Binary mode.
+ ?line Name3 = filename:join(NewDir, "plain-binary.fil"),
+ ?line {ok, Fd3} = ?FILE_MODULE:open(Name3, [binary, read, write]),
+ ?line pread_write_test(Fd3, Bin),
+
+ %% Raw file. Binary mode.
+ ?line Name4 = filename:join(NewDir, "raw-binary.fil"),
+ ?line {ok, Fd4} = ?FILE_MODULE:open(Name4, [binary, read, write, raw]),
+ ?line pread_write_test(Fd4, Bin),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+pread_write_test(File, Data) ->
+ ?line io:format("~p:pread_write_test(~p,~p)~n", [?MODULE, File, Data]),
+ ?line Size = if is_binary(Data) -> byte_size(Data);
+ is_list(Data) -> length(Data)
+ end,
+ ?line I = Size + 17,
+ ?line ok = ?FILE_MODULE:pwrite(File, 0, Data),
+ Res = ?FILE_MODULE:pread(File, 0, I),
+ ?line {ok, Data} = Res,
+ ?line eof = ?FILE_MODULE:pread(File, I, 1),
+ ?line ok = ?FILE_MODULE:pwrite(File, [{0, Data}, {I, Data}]),
+ ?line {ok, [Data, eof, Data]} =
+ ?FILE_MODULE:pread(File, [{0, Size}, {2*I, 1}, {I, Size}]),
+ ?line Plist = lists:seq(21*I, 0, -I),
+ ?line Pwrite = lists:map(fun(P)->{P,Data}end, Plist),
+ ?line Pread = [{22*I,Size} | lists:map(fun(P)->{P,Size}end, Plist)],
+ ?line Presult = [eof | lists:map(fun(_)->Data end, Plist)],
+ ?line ok = ?FILE_MODULE:pwrite(File, Pwrite),
+ ?line {ok, Presult} = ?FILE_MODULE:pread(File, Pread),
+ ?line ok = ?FILE_MODULE:close(File),
+ ?line [] = flush(),
+ ok.
+
+append(doc) -> "Test appending to a file.";
+append(suite) -> [];
+append(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_append"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+
+ ?line First = "First line\n",
+ ?line Second = "Seond lines comes here\n",
+ ?line Third = "And here is the third line\n",
+
+ %% Write a small text file.
+ ?line Name1 = filename:join(NewDir, "a_file.txt"),
+ ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]),
+ ?line ok = io:format(Fd1, First, []),
+ ?line ok = io:format(Fd1, Second, []),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+
+ %% Open it a again and a append a line to it.
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name1, [append]),
+ ?line ok = io:format(Fd2, Third, []),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+
+ %% Read it back and verify.
+ ?line Expected = list_to_binary([First, Second, Third]),
+ ?line {ok, Expected} = ?FILE_MODULE:read_file(Name1),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+open_errors(suite) -> [];
+open_errors(doc) -> [];
+open_errors(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line DataDir =
+ filename:dirname(
+ filename:join(?config(data_dir, Config), "x")),
+ ?line DataDirSlash = DataDir++"/",
+ ?line {error, E1} = ?FILE_MODULE:open(DataDir, [read]),
+ ?line {error, E2} = ?FILE_MODULE:open(DataDirSlash, [read]),
+ ?line {error, E3} = ?FILE_MODULE:open(DataDir, [write]),
+ ?line {error, E4} = ?FILE_MODULE:open(DataDirSlash, [write]),
+ ?line {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4},
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+pos(suite) -> [pos1,pos2].
+
+pos1(suite) -> [];
+pos1(doc) -> [];
+pos1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_pos1.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd1,"ABCDEFGH",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read),
+
+ %% Start pos is first char
+ ?line io:format("Relative positions"),
+ ?line "A" = io:get_chars(Fd2,'',1),
+ ?line {ok,2} = ?FILE_MODULE:position(Fd2,{cur,1}),
+ ?line "C" = io:get_chars(Fd2,'',1),
+ ?line {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-3}),
+ ?line "A" = io:get_chars(Fd2,'',1),
+ %% Backwards from first char should be an error
+ ?line {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-1}),
+ ?line {error, einval} = ?FILE_MODULE:position(Fd2,{cur,-1}),
+ %% Reset position and move again
+ ?line {ok,0} = ?FILE_MODULE:position(Fd2,0),
+ ?line {ok,2} = ?FILE_MODULE:position(Fd2,{cur,2}),
+ ?line "C" = io:get_chars(Fd2,'',1),
+ %% Go a lot forwards
+ ?line {ok,13} = ?FILE_MODULE:position(Fd2,{cur,10}),
+ ?line eof = io:get_chars(Fd2,'',1),
+
+ %% Try some fixed positions
+ ?line io:format("Fixed positions"),
+ ?line {ok,8} =
+ ?FILE_MODULE:position(Fd2,8), eof = io:get_chars(Fd2,'',1),
+ ?line {ok,8} =
+ ?FILE_MODULE:position(Fd2,cur), eof = io:get_chars(Fd2,'',1),
+ ?line {ok,7} =
+ ?FILE_MODULE:position(Fd2,7), "H" = io:get_chars(Fd2,'',1),
+ ?line {ok,0} =
+ ?FILE_MODULE:position(Fd2,0), "A" = io:get_chars(Fd2,'',1),
+ ?line {ok,3} =
+ ?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1),
+ ?line {ok,12} =
+ ?FILE_MODULE:position(Fd2,12), eof = io:get_chars(Fd2,'',1),
+ ?line {ok,3} =
+ ?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1),
+ %% Try the {bof,X} notation
+ ?line {ok,3} = ?FILE_MODULE:position(Fd2,{bof,3}),
+ ?line "D" = io:get_chars(Fd2,'',1),
+
+ %% Try eof positions
+ ?line io:format("EOF positions"),
+ ?line {ok,8} =
+ ?FILE_MODULE:position(Fd2,{eof,0}), eof=io:get_chars(Fd2,'',1),
+ ?line {ok,7} =
+ ?FILE_MODULE:position(Fd2,{eof,-1}),
+ ?line "H" = io:get_chars(Fd2,'',1),
+ ?line {ok,0} =
+ ?FILE_MODULE:position(Fd2,{eof,-8}), "A"=io:get_chars(Fd2,'',1),
+ ?line {error, einval} = ?FILE_MODULE:position(Fd2,{eof,-9}),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+pos2(suite) -> [];
+pos2(doc) -> [];
+pos2(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_pos2.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd1,"ABCDEFGH",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read),
+ ?line {error, einval} = ?FILE_MODULE:position(Fd2,-1),
+
+ %% Make sure that we still can search after an error.
+ ?line {ok,0} = ?FILE_MODULE:position(Fd2, 0),
+ ?line {ok,3} = ?FILE_MODULE:position(Fd2, {bof,3}),
+ ?line "D" = io:get_chars(Fd2,'',1),
+
+ ?line [] = flush(),
+ ?line io:format("DONE"),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+file_info(suite) -> [file_info_basic_file, file_info_basic_directory,
+ file_info_bad, file_info_times, file_write_file_info].
+
+file_info_basic_file(suite) -> [];
+file_info_basic_file(doc) -> [];
+file_info_basic_file(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir, Config),
+
+ %% Create a short file.
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_basic_test.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ ?line io:put_chars(Fd1, "foo bar"),
+ ?line 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.
+ ?line {ok,#file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime}} =
+ ?FILE_MODULE:read_file_info(Name),
+ ?line io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
+ ?line Size = 7,
+ ?line Type = regular,
+ ?line read_write = Access,
+ ?line true = abs(time_dist(filter_atime(AccessTime, Config),
+ filter_atime(ModifyTime,
+ Config))) < 2,
+ ?line all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+file_info_basic_directory(suite) -> [];
+file_info_basic_directory(doc) -> [];
+file_info_basic_directory(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+ ?line RootDir = filename:join([?config(priv_dir, Config)]),
+
+ %% Test that the RootDir directory has the expected attributes.
+ ?line test_directory(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.
+ ?line case os:type() of
+ {win32, _} ->
+ ?line test_directory("/", read_write),
+ ?line test_directory("c:/", read_write),
+ ?line test_directory("c:\\", read_write);
+ {unix, _} ->
+ ?line test_directory("/", read);
+ vxworks ->
+ %% Check is just done for owner
+ ?line test_directory("/", read_write)
+ end,
+ ?line test_server:timetrap_cancel(Dog).
+
+test_directory(Name, ExpectedAccess) ->
+ ?line {ok,#file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime}} =
+ ?FILE_MODULE:read_file_info(Name),
+ ?line io:format("Testing directory ~s", [Name]),
+ ?line io:format("Directory size is ~p", [Size]),
+ ?line io:format("Access ~p", [Access]),
+ ?line io:format("Access time ~p; Modify time~p",
+ [AccessTime, ModifyTime]),
+ ?line Type = directory,
+ ?line Access = ExpectedAccess,
+ ?line all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+ ?line [] = flush(),
+ ok.
+
+all_integers([{A,B,C}|T]) ->
+ all_integers([A,B,C|T]);
+all_integers([Int|Rest]) when is_integer(Int) ->
+ ?line all_integers(Rest);
+all_integers([]) -> ok.
+
+%% Try something nonexistent.
+
+file_info_bad(suite) -> [];
+file_info_bad(doc) -> [];
+file_info_bad(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = filename:join([?config(priv_dir, Config)]),
+ ?line {error, enoent} =
+ ?FILE_MODULE:read_file_info(
+ filename:join(RootDir,
+ atom_to_list(?MODULE)++ "_nonexistent")),
+ ?line {error, enoent} = ?FILE_MODULE:read_file_info(""),
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Test that the file times behave as they should.
+
+file_info_times(suite) -> [];
+file_info_times(doc) -> [];
+file_info_times(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(60)),
+ %% 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.
+ ?line test_server:m_out_of_n(
+ 1,2,
+ fun() -> ?line file_info_int(Config) end),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+file_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.
+
+ ?line RootDir = filename:join([?config(priv_dir, Config)]),
+ ?line test_server:format("RootDir = ~p", [RootDir]),
+
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_file_info.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:put_chars(Fd1,"foo"),
+
+ %% check that the file got a modify date max a few seconds away from now
+ ?line {ok,#file_info{type=regular,atime=AccTime1,mtime=ModTime1}} =
+ ?FILE_MODULE:read_file_info(Name),
+ ?line Now = erlang:localtime(), %???
+ ?line io:format("Now ~p",[Now]),
+ ?line io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]),
+ ?line true = abs(time_dist(filter_atime(Now, Config),
+ filter_atime(AccTime1,
+ Config))) < 8,
+ ?line 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.
+ ?line test_server:sleep(test_server:seconds(2.2)),
+
+ %% close the file, and watch the modify date change
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,#file_info{size=Size,type=regular,access=Access,
+ atime=AccTime2,mtime=ModTime2}} =
+ ?FILE_MODULE:read_file_info(Name),
+ ?line io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]),
+ ?line true = time_dist(ModTime1,ModTime2) >= 0,
+
+ %% this file is supposed to be binary, so it'd better keep it's size
+ ?line Size = 3,
+ ?line Access = read_write,
+
+ %% Do some directory checking
+ ?line {ok,#file_info{size=DSize,type=directory,access=DAccess,
+ atime=AccTime3,mtime=ModTime3}} =
+ ?FILE_MODULE:read_file_info(RootDir),
+ %% this dir was modified only a few secs ago
+ ?line io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]),
+ ?line true = abs(time_dist(Now,ModTime3)) < 5,
+ ?line DAccess = read_write,
+ ?line io:format("Dir size is ~p",[DSize]),
+
+ ?line [] = flush(),
+ ok.
+
+%% Filter access times, to copy with a deficiency of FAT file systems
+%% (on Windows): The access time is actually only a date.
+
+filter_atime(Atime, Config) ->
+ case lists:member(no_access_time, Config) of
+ true ->
+ case Atime of
+ {Date, _} ->
+ {Date, {0, 0, 0}};
+ {Y, M, D, _, _, _} ->
+ {Y, M, D, 0, 0, 0}
+ end;
+ false ->
+ Atime
+ end.
+
+%% Test the write_file_info/2 function.
+
+file_write_file_info(suite) -> [];
+file_write_file_info(doc) -> [];
+file_write_file_info(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = get_good_directory(Config),
+ ?line test_server:format("RootDir = ~p", [RootDir]),
+
+ %% Set the file to read only AND update the file times at the same time.
+ %% (This used to fail on Windows NT/95 for a local filesystem.)
+ %% Note: Seconds must be even; see note in file_info_times/1.
+
+ ?line Name1 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_write_file_info_ro"),
+ ?line ok = ?FILE_MODULE:write_file(Name1, "hello"),
+ ?line Time = {{1997, 01, 02}, {12, 35, 42}},
+ ?line Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time},
+ ?line ok = ?FILE_MODULE:write_file_info(Name1, Info),
+
+ %% Read back the times.
+
+ ?line {ok, ActualInfo} = ?FILE_MODULE:read_file_info(Name1),
+ ?line #file_info{mode=_Mode, atime=ActAtime, mtime=Time,
+ ctime=ActCtime} = ActualInfo,
+ ?line FilteredAtime = filter_atime(Time, Config),
+ ?line FilteredAtime = filter_atime(ActAtime, Config),
+ ?line case os:type() of
+ {win32, _} ->
+ %% On Windows, "ctime" means creation time and it can
+ %% be set.
+ ActCtime = Time;
+ _ ->
+ ok
+ end,
+ ?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
+
+ %% Make the file writable again.
+
+ ?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}),
+ ?line ok = ?FILE_MODULE:write_file(Name1, "hello again"),
+
+ %% And unwritable.
+ ?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}),
+ ?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
+
+ %% Write the times again.
+ %% Note: Seconds must be even; see note in file_info_times/1.
+
+ ?line NewTime = {{1997, 02, 15}, {13, 18, 20}},
+ ?line NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime},
+ ?line ok = ?FILE_MODULE:write_file_info(Name1, NewInfo),
+ ?line {ok, ActualInfo2} = ?FILE_MODULE:read_file_info(Name1),
+ ?line #file_info{atime=NewActAtime, mtime=NewTime,
+ ctime=NewActCtime} = ActualInfo2,
+ ?line NewFilteredAtime = filter_atime(NewTime, Config),
+ ?line NewFilteredAtime = filter_atime(NewActAtime, Config),
+ ?line case os:type() of
+ {win32, _} -> NewActCtime = NewTime;
+ _ -> ok
+ end,
+
+ %% The file should still be unwritable.
+ ?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
+
+ %% Make the file writeable again, so that we can remove the
+ %% test suites ... :-)
+ ?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Returns a directory on a file system that has correct file times.
+
+get_good_directory(Config) ->
+ ?line ?config(priv_dir, Config).
+
+consult(suite) -> [consult1, path_consult].
+
+consult1(suite) -> [];
+consult1(doc) -> [];
+consult1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_consult.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ %% note that there is no final \n (only a space)
+ ?line io:format(Fd1,
+ "{this,[is,1.0],'journey'}.\n\"into\". (sound). ",
+ []),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,[{this,[is,1.0],journey},"into",sound]} =
+ ?FILE_MODULE:consult(Name),
+
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write),
+ %% note the missing double quote
+ ?line io:format(
+ Fd2,"{this,[is,1.0],'journey'}.\n \"into. (sound). ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {error, {_, _, _} = Msg} = ?FILE_MODULE:consult(Name),
+ ?line io:format("Errmsg: ~p",[Msg]),
+
+ ?line {error, enoent} = ?FILE_MODULE:consult(Name ++ ".nonexistent"),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+path_consult(suite) -> [];
+path_consult(doc) -> [];
+path_consult(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line FileName = atom_to_list(?MODULE)++"_path_consult.fil",
+ ?line Name = filename:join(RootDir, FileName),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd1,"{this,is,a,journey,into,sound}.\n",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ %% File last in path
+ ?line {ok,[{this,is,a,journey,into,sound}],Dir} =
+ ?FILE_MODULE:path_consult(
+ [filename:join(RootDir, "dir1"),
+ filename:join(RootDir, ".."),
+ filename:join(RootDir, "dir2"),
+ RootDir], FileName),
+ ?line true = lists:prefix(RootDir,Dir),
+
+ %% While maybe not an error, it may be worth noting that
+ %% when the full path to a file is given, it's always found
+ %% regardless of the contents of the path
+ ?line {ok,_,_} = ?FILE_MODULE:path_consult(["nosuch1","nosuch2"],Name),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+eval(suite) -> [eval1,path_eval].
+
+eval1(suite) -> [];
+eval1(doc) -> [];
+eval1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_eval.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ %% note that there is no final \n (only a space)
+ ?line io:format(Fd1,"put(evaluated_ok,\ntrue). ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line ok = ?FILE_MODULE:eval(Name),
+ ?line true = get(evaluated_ok),
+
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write),
+ %% note that there is no final \n (only a space)
+ ?line io:format(Fd2,"put(evaluated_ok,\nR). ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line ok = ?FILE_MODULE:eval(
+ Name,
+ erl_eval:add_binding('R', true, erl_eval:new_bindings())),
+ ?line true = get(evaluated_ok),
+
+ ?line {ok,Fd3} = ?FILE_MODULE:open(Name,write),
+ %% garbled
+ ?line io:format(Fd3,"puGARBLED-GARBLED\ntrue). ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ ?line {error, {_, _, _} = Msg} = ?FILE_MODULE:eval(Name),
+ ?line io:format("Errmsg1: ~p",[Msg]),
+
+ ?line {error, enoent} = ?FILE_MODULE:eval(Name ++ ".nonexistent"),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+path_eval(suite) -> [];
+path_eval(doc) -> [];
+path_eval(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line FileName = atom_to_list(?MODULE)++"_path_eval.fil",
+ ?line Name = filename:join(RootDir, FileName),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd1,"put(evaluated_ok,true).\n",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ %% File last in path
+ ?line {ok,Dir} =
+ ?FILE_MODULE:path_eval(
+ [filename:join(RootDir, "dir1"),
+ filename:join(RootDir, ".."),
+ filename:join(RootDir, "dir2"),
+ RootDir],FileName),
+ ?line true = get(evaluated_ok),
+ ?line true = lists:prefix(RootDir,Dir),
+
+ %% While maybe not an error, it may be worth noting that
+ %% when the full path to a file is given, it's always found
+ %% regardless of the contents of the path
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd2,"put(evaluated_ok,R).\n",[]),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok,_} = ?FILE_MODULE:path_eval(
+ ["nosuch1","nosuch2"],
+ Name,
+ erl_eval:add_binding('R', true, erl_eval:new_bindings())),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+script(suite) -> [script1,path_script].
+
+script1(suite) -> [];
+script1(doc) -> "";
+script1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_script.fil"),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ %% note that there is no final \n (only a space)
+ ?line io:format(Fd1,"A = 11,\nB = 6,\nA+B. ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,17} = ?FILE_MODULE:script(Name),
+
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write),
+ %% note that there is no final \n (only a space)
+ ?line io:format(Fd2,"A = 11,\nA+B. ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok,17} = ?FILE_MODULE:script(
+ Name,
+ erl_eval:add_binding('B', 6, erl_eval:new_bindings())),
+
+ ?line {ok,Fd3} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd3,"A = 11,\nB = six,\nA+B. ",[]),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ ?line {error, {_, _, _} = Msg} = ?FILE_MODULE:script(Name),
+ ?line io:format("Errmsg1: ~p",[Msg]),
+
+ ?line {error, enoent} = ?FILE_MODULE:script(Name ++ ".nonexistent"),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+path_script(suite) -> [];
+path_script(doc) -> [];
+path_script(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line FileName = atom_to_list(?MODULE)++"_path_script.fil",
+ ?line Name = filename:join(RootDir, FileName),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd1,"A = 11,\nB = 6,\nA+B.\n",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ %% File last in path
+ ?line {ok, 17, Dir} =
+ ?FILE_MODULE:path_script(
+ [filename:join(RootDir, "dir1"),
+ filename:join(RootDir, ".."),
+ filename:join(RootDir, "dir2"),
+ RootDir],FileName),
+ ?line true = lists:prefix(RootDir,Dir),
+
+ %% While maybe not an error, it may be worth noting that
+ %% when the full path to a file is given, it's always found
+ %% regardless of the contents of the path
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write),
+ ?line io:format(Fd2,"A = 11,\nA+B.",[]),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok, 17, Dir} =
+ ?FILE_MODULE:path_script(
+ ["nosuch1","nosuch2"],
+ Name,
+ erl_eval:add_binding('B', 6, erl_eval:new_bindings())),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+
+truncate(suite) -> [];
+truncate(doc) -> [];
+truncate(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_truncate.fil"),
+
+ %% Create a file with some data.
+ ?line MyData = "0123456789abcdefghijklmnopqrstuvxyz",
+ ?line ok = ?FILE_MODULE:write_file(Name, MyData),
+
+ %% Truncate the file to 10 characters.
+ ?line {ok, Fd} = ?FILE_MODULE:open(Name, read_write),
+ ?line {ok, 10} = ?FILE_MODULE:position(Fd, 10),
+ ?line ok = ?FILE_MODULE:truncate(Fd),
+ ?line ok = ?FILE_MODULE:close(Fd),
+
+ %% Read back the file and check that it has been truncated.
+ ?line Expected = list_to_binary("0123456789"),
+ ?line {ok, Expected} = ?FILE_MODULE:read_file(Name),
+
+ %% Open the file read only and verify that it is not possible to
+ %% truncate it, OTP-1960
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name, read),
+ ?line {ok, 5} = ?FILE_MODULE:position(Fd2, 5),
+ ?line {error, _} = ?FILE_MODULE:truncate(Fd2),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+sync(suite) -> [];
+sync(doc) -> "Tests that ?FILE_MODULE:sync/1 at least doesn't crash.";
+sync(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Sync = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_sync.fil"),
+
+ %% Raw open.
+ ?line {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]),
+ ?line ok = ?FILE_MODULE:sync(Fd),
+ ?line ok = ?FILE_MODULE:close(Fd),
+
+ %% Ordinary open.
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]),
+ ?line ok = ?FILE_MODULE:sync(Fd2),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+delete(suite) -> [];
+delete(doc) -> [];
+delete(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_delete.fil"),
+ ?line {ok, Fd1} = ?FILE_MODULE:open(Name, write),
+ ?line io:format(Fd1,"ok.\n",[]),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ %% Check that the file is readable
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Name, read),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line ok = ?FILE_MODULE:delete(Name),
+ %% Check that the file is not readable anymore
+ ?line {error, _} = ?FILE_MODULE:open(Name, read),
+ %% Try deleting a nonexistent file
+ ?line {error, enoent} = ?FILE_MODULE:delete(Name),
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+rename(suite) ->[];
+rename(doc) ->[];
+rename(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line FileName1 = atom_to_list(?MODULE)++"_rename.fil",
+ ?line FileName2 = atom_to_list(?MODULE)++"_rename.ful",
+ ?line Name1 = filename:join(RootDir, FileName1),
+ ?line Name2 = filename:join(RootDir, FileName2),
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name1,write),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ %% Rename, and check that id really changed name
+ ?line ok = ?FILE_MODULE:rename(Name1,Name2),
+ ?line {error, _} = ?FILE_MODULE:open(Name1,read),
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name2,read),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ %% Try renaming something to itself
+ ?line ok = ?FILE_MODULE:rename(Name2,Name2),
+ %% Try renaming something that doesn't exist
+ ?line {error, enoent} = ?FILE_MODULE:rename(Name1,Name2),
+ %% Try renaming to something else than a string
+ ?line {error, badarg} = ?FILE_MODULE:rename(Name1,{foo,bar}),
+
+ %% Move between directories
+ ?line DirName1 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_rename_dir"),
+ ?line DirName2 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_second_rename_dir"),
+ ?line Name1foo = filename:join(DirName1, "foo.fil"),
+ ?line Name2foo = filename:join(DirName2, "foo.fil"),
+ ?line Name2bar = filename:join(DirName2, "bar.dir"),
+ ?line ok = ?FILE_MODULE:make_dir(DirName1),
+ %% The name has to include the full file name, path in not enough
+ ?line expect({error, eisdir}, {error, eexist},
+ ?FILE_MODULE:rename(Name2,DirName1)),
+ ?line ok = ?FILE_MODULE:rename(Name2, Name1foo),
+ %% Now rename the directory
+ ?line ok = ?FILE_MODULE:rename(DirName1,DirName2),
+ %% And check that the file is there now
+ ?line {ok,Fd3} = ?FILE_MODULE:open(Name2foo, read),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ %% Try some dirty things now: move the directory into itself
+ ?line {error, Msg1} = ?FILE_MODULE:rename(DirName2, Name2bar),
+ ?line io:format("Errmsg1: ~p",[Msg1]),
+ %% move dir into a file in itself
+ ?line {error, Msg2} = ?FILE_MODULE:rename(DirName2, Name2foo),
+ ?line io:format("Errmsg2: ~p",[Msg2]),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+names(suite) -> [];
+names(doc) -> [];
+names(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line FileName = "foo1.fil",
+ ?line Name1 = filename:join(RootDir, FileName),
+ ?line Name2 = [RootDir,"/","foo1",".","fil"],
+ ?line Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il],
+ ?line Name4 = list_to_atom(Name1),
+ ?line {ok,Fd0} = ?FILE_MODULE:open(Name1,write),
+ ?line ok = ?FILE_MODULE:close(Fd0),
+
+ %% Try some file names
+ ?line {ok,Fd1} = ?FILE_MODULE:open(Name1,read),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line {ok,Fd2f} = ?FILE_MODULE:open(lists:flatten(Name2),read),
+ ?line ok = ?FILE_MODULE:close(Fd2f),
+ ?line {ok,Fd2} = ?FILE_MODULE:open(Name2,read),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok,Fd3} = ?FILE_MODULE:open(Name3,read),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ ?line {ok,Fd4} = ?FILE_MODULE:open(Name4,read),
+ ?line ok = ?FILE_MODULE:close(Fd4),
+
+ %% Try some path names
+ ?line Path1 = RootDir,
+ ?line Path2 = [RootDir],
+ ?line Path3 = ['',[],[RootDir,[[]]]],
+ ?line Path4 = list_to_atom(Path1),
+ ?line {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read),
+ ?line ok = ?FILE_MODULE:close(Fd11),
+ ?line {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read),
+ ?line ok = ?FILE_MODULE:close(Fd12),
+ ?line {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read),
+ ?line ok = ?FILE_MODULE:close(Fd13),
+ ?line {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read),
+ ?line ok = ?FILE_MODULE:close(Fd14),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+errors(suite) -> [e_delete, e_rename, e_make_dir, e_del_dir].
+
+e_delete(suite) -> [];
+e_delete(doc) -> [];
+e_delete(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_delete"),
+ ?line ok = ?FILE_MODULE:make_dir(Base),
+
+ %% Delete a non-existing file.
+ ?line {error, enoent} =
+ ?FILE_MODULE:delete(filename:join(Base, "non_existing")),
+
+ %% Delete a directory.
+ ?line {error, eperm} = ?FILE_MODULE:delete(Base),
+
+ %% Use a path-name with a non-directory component.
+ ?line Afile = filename:join(Base, "a_file"),
+ ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ ?line {error, E} =
+ expect({error, enotdir}, {error, enoent},
+ ?FILE_MODULE:delete(filename:join(Afile, "another_file"))),
+ ?line io:format("Result: ~p~n", [E]),
+
+ %% No permission.
+ ?line case os:type() of
+ {unix, _} ->
+ ?line ?FILE_MODULE:write_file_info(
+ Base, #file_info {mode=0}),
+ ?line {error, eacces} = ?FILE_MODULE:delete(Afile),
+ ?line ?FILE_MODULE:write_file_info(
+ Base, #file_info {mode=8#600});
+ {win32, _} ->
+ %% Remove a character device.
+ ?line {error, eacces} = ?FILE_MODULE:delete("nul");
+ vxworks ->
+ ok
+ end,
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%% FreeBSD gives EEXIST when renaming a file to an empty dir, although the
+%%% manual page can be interpreted as saying that EISDIR should be given.
+%%% (What about FreeBSD? We store our nightly build results on a FreeBSD
+%%% file system, that's what.)
+
+e_rename(suite) -> [];
+e_rename(doc) -> [];
+e_rename(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ {comment, "Windriver: dosFs must be fixed first!"};
+ _ ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_rename"),
+ ?line ok = ?FILE_MODULE:make_dir(Base),
+
+ %% Create an empty directory.
+ ?line EmptyDir = filename:join(Base, "empty_dir"),
+ ?line ok = ?FILE_MODULE:make_dir(EmptyDir),
+
+ %% Create a non-empty directory.
+ ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
+ ?line ok = ?FILE_MODULE:make_dir(NonEmptyDir),
+ ?line ok = ?FILE_MODULE:write_file(
+ filename:join(NonEmptyDir, "a_file"),
+ "hello\n"),
+
+ %% Create another non-empty directory.
+ ?line ADirectory = filename:join(Base, "a_directory"),
+ ?line ok = ?FILE_MODULE:make_dir(ADirectory),
+ ?line ok = ?FILE_MODULE:write_file(
+ filename:join(ADirectory, "a_file"),
+ "howdy\n\n"),
+
+ %% Create a data file.
+ ?line File = filename:join(Base, "just_a_file"),
+ ?line ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"),
+
+ %% Move an existing directory to a non-empty directory.
+ ?line {error, eexist} =
+ ?FILE_MODULE:rename(ADirectory, NonEmptyDir),
+
+ %% Move a root directory.
+ ?line {error, einval} = ?FILE_MODULE:rename("/", "arne"),
+
+ %% Move Base into Base/new_name.
+ ?line {error, einval} =
+ ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")),
+
+ %% Overwrite a directory with a file.
+ ?line expect({error, eexist}, %FreeBSD (?)
+ {error, eisdir},
+ ?FILE_MODULE:rename(File, EmptyDir)),
+ ?line expect({error, eexist}, %FreeBSD (?)
+ {error, eisdir},
+ ?FILE_MODULE:rename(File, NonEmptyDir)),
+
+ %% Move a non-existing file.
+ ?line NonExistingFile =
+ filename:join(Base, "non_existing_file"),
+ ?line {error, enoent} =
+ ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir),
+
+ %% Overwrite a file with a directory.
+ ?line expect({error, eexist}, %FreeBSD (?)
+ {error, enotdir},
+ ?FILE_MODULE:rename(ADirectory, File)),
+
+ %% Move a file to another filesystem.
+ %% XXX - This test case is bogus. We cannot be guaranteed that
+ %% the source and destination are on
+ %% different filesystems.
+ %%
+ %% XXX - Gross hack!
+ ?line Comment =
+ case os:type() of
+ {unix, _} ->
+ OtherFs = "/tmp",
+ ?line NameOnOtherFs =
+ filename:join(OtherFs, filename:basename(File)),
+ ?line {ok, Com} =
+ case ?FILE_MODULE:rename(File, NameOnOtherFs) of
+ {error, exdev} ->
+ %% The file could be in
+ %% the same filesystem!
+ {ok, ok};
+ ok ->
+ {ok, {comment,
+ "Moving between filesystems "
+ "suceeded, files are probably "
+ "in the same filesystem!"}};
+ {error, eperm} ->
+ {ok, {comment, "SBS! You don't "
+ "have the permission to do "
+ "this test!"}};
+ Else ->
+ Else
+ end,
+ Com;
+ {win32, _} ->
+ %% At least Windows NT can
+ %% successfully move a file to
+ %% another drive.
+ ok
+ end,
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ Comment
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+e_make_dir(suite) -> [];
+e_make_dir(doc) -> [];
+e_make_dir(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_make_dir"),
+ ?line ok = ?FILE_MODULE:make_dir(Base),
+
+ %% A component of the path does not exist.
+ ?line {error, enoent} =
+ ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])),
+
+ %% Use a path-name with a non-directory component.
+ ?line Afile = filename:join(Base, "a_directory"),
+ ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ ?line case ?FILE_MODULE:make_dir(
+ filename:join(Afile, "another_directory")) of
+ {error, enotdir} -> io:format("Result: enotdir");
+ {error, enoent} -> io:format("Result: enoent")
+ end,
+
+ %% No permission (on Unix only).
+ case os:type() of
+ {unix, _} ->
+ ?line ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
+ ?line {error, eacces} =
+ ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
+ ?line ?FILE_MODULE:write_file_info(
+ Base, #file_info {mode=8#600});
+ {win32, _} ->
+ ok;
+ vxworks ->
+ ok
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+e_del_dir(suite) -> [];
+e_del_dir(doc) -> [];
+e_del_dir(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")),
+ ?line io:format("Base: ~p", [Base]),
+ ?line ok = ?FILE_MODULE:make_dir(Base),
+
+ %% Delete a non-existent directory.
+ ?line {error, enoent} =
+ ?FILE_MODULE:del_dir(filename:join(Base, "non_existing")),
+
+ %% Use a path-name with a non-directory component.
+ ?line Afile = filename:join(Base, "a_directory"),
+ ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ ?line {error, E1} =
+ expect({error, enotdir}, {error, enoent},
+ ?FILE_MODULE:del_dir(
+ filename:join(Afile, "another_directory"))),
+ ?line io:format("Result: ~p", [E1]),
+
+ %% Delete a non-empty directory.
+ ?line {error, E2} =
+ expect({error, enotempty}, {error, eexist}, {error, eacces},
+ ?FILE_MODULE:del_dir(Base)),
+ ?line io:format("Result: ~p", [E2]),
+
+ %% Remove the current directory.
+ ?line {error, E3} =
+ expect({error, einval},
+ {error, eperm}, % Linux and DUX
+ {error, eacces},
+ {error, ebusy},
+ ?FILE_MODULE:del_dir(".")),
+ ?line io:format("Result: ~p", [E3]),
+
+ %% No permission.
+ case os:type() of
+ {unix, _} ->
+ ?line ADirectory = filename:join(Base, "no_perm"),
+ ?line ok = ?FILE_MODULE:make_dir(ADirectory),
+ ?line ?FILE_MODULE:write_file_info(
+ Base, #file_info {mode=0}),
+ ?line {error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
+ ?line ?FILE_MODULE:write_file_info(
+ Base, #file_info {mode=8#600});
+ {win32, _} ->
+ ok;
+ vxworks ->
+ ok
+ end,
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+compression(suite) ->
+ [read_compressed_cooked, read_compressed_cooked_binary,
+ read_cooked_tar_problem,
+ read_not_really_compressed,
+ write_compressed, compress_errors,
+ catenated_gzips].
+
+%% Trying reading and positioning from a compressed file.
+
+read_compressed_cooked(Config) when is_list(Config) ->
+ ?line Data = ?config(data_dir, Config),
+ ?line Real = filename:join(Data, "realmen.html.gz"),
+ ?line {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed]),
+ ?line try_read_file_list(Fd).
+
+read_compressed_cooked_binary(Config) when is_list(Config) ->
+ ?line Data = ?config(data_dir, Config),
+ ?line Real = filename:join(Data, "realmen.html.gz"),
+ ?line {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed,binary]),
+ ?line try_read_file_binary(Fd).
+
+%% Trying reading and positioning from an uncompressed file,
+%% but with the compressed flag given.
+
+read_not_really_compressed(Config) when is_list(Config) ->
+ ?line Data = ?config(data_dir, Config),
+ ?line Priv = ?config(priv_dir, Config),
+
+ %% The file realmen.html might have got CRs added (by WinZip).
+ %% Remove them, or the file positions will not be correct.
+
+ ?line Real = filename:join(Data, "realmen.html"),
+ ?line RealPriv = filename:join(Priv,
+ atom_to_list(?MODULE)++"_realmen.html"),
+ ?line {ok, RealDataBin} = ?FILE_MODULE:read_file(Real),
+ ?line RealData = remove_crs(binary_to_list(RealDataBin), []),
+ ?line ok = ?FILE_MODULE:write_file(RealPriv, RealData),
+ ?line {ok, Fd} = ?FILE_MODULE:open(RealPriv, [read, compressed]),
+ ?line try_read_file_list(Fd).
+
+remove_crs([$\r|Rest], Result) ->
+ remove_crs(Rest, Result);
+remove_crs([C|Rest], Result) ->
+ remove_crs(Rest, [C|Result]);
+remove_crs([], Result) ->
+ lists:reverse(Result).
+
+try_read_file_list(Fd) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+
+ %% Seek to the current position (nothing should happen).
+
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd, 0),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}),
+
+ %% Read a few lines from a compressed file.
+
+ ?line ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n",
+ ?line ShouldBe = io:get_line(Fd, ''),
+
+ %% Now seek forward.
+
+ ?line {ok, 381} = ?FILE_MODULE:position(Fd, 381),
+ ?line Back = "Back in the good old days -- the \"Golden Era\" " ++
+ "of computers, it was\n",
+ ?line Back = io:get_line(Fd, ''),
+
+ %% Try to search forward relative to the current position.
+
+ ?line {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}),
+ ?line RealPos = 4273,
+ ?line {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}),
+ ?line RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n",
+ ?line RealProg = io:get_line(Fd, ''),
+
+ %% Seek backward.
+
+ ?line AfterTitle = length("<TITLE>"),
+ ?line {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle),
+ ?line Title = "Real Programmers Don't Use PASCAL</TITLE>\n",
+ ?line Title = io:get_line(Fd, ''),
+
+ %% Done.
+
+ ?line ?FILE_MODULE:close(Fd),
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+try_read_file_binary(Fd) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+
+ %% Seek to the current position (nothing should happen).
+
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd, 0),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}),
+
+ %% Read a few lines from a compressed file.
+
+ ?line ShouldBe = <<"<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n">>,
+ ?line ShouldBe = io:get_line(Fd, ''),
+
+ %% Now seek forward.
+
+ ?line {ok, 381} = ?FILE_MODULE:position(Fd, 381),
+ ?line Back = <<"Back in the good old days -- the \"Golden Era\" "
+ "of computers, it was\n">>,
+ ?line Back = io:get_line(Fd, ''),
+
+ %% Try to search forward relative to the current position.
+
+ ?line {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}),
+ ?line RealPos = 4273,
+ ?line {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}),
+ ?line RealProg = <<"<LI> Real Programmers aren't afraid to use GOTOs.\n">>,
+ ?line RealProg = io:get_line(Fd, ''),
+
+ %% Seek backward.
+
+ ?line AfterTitle = length("<TITLE>"),
+ ?line {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle),
+ ?line Title = <<"Real Programmers Don't Use PASCAL</TITLE>\n">>,
+ ?line Title = io:get_line(Fd, ''),
+
+ %% Done.
+
+ ?line ?FILE_MODULE:close(Fd),
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+read_cooked_tar_problem(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+
+ ?line Data = ?config(data_dir, Config),
+ ?line ProblemFile = filename:join(Data, "cooked_tar_problem.tar.gz"),
+ ?line {ok,Fd} = ?FILE_MODULE:open(ProblemFile, [read,compressed,binary]),
+
+ ?line {ok,34304} = file:position(Fd, 34304),
+ ?line {ok,Bin} = file:read(Fd, 512),
+ ?line 512 = byte_size(Bin),
+
+ ?line {ok,34304+512+1024} = file:position(Fd, {cur,1024}),
+
+ ?line ok = file:close(Fd),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+write_compressed(suite) -> [];
+write_compressed(doc) -> [];
+write_compressed(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line Priv = ?config(priv_dir, Config),
+ ?line MyFile = filename:join(Priv,
+ atom_to_list(?MODULE)++"_test.gz"),
+
+ %% Write a file.
+
+ ?line {ok, Fd} = ?FILE_MODULE:open(MyFile, [write, compressed]),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd, 0),
+ ?line Prefix = "hello\n",
+ ?line End = "end\n",
+ ?line ok = io:put_chars(Fd, Prefix),
+ ?line {ok, 143} = ?FILE_MODULE:position(Fd, 143),
+ ?line ok = io:put_chars(Fd, End),
+ ?line ok = ?FILE_MODULE:close(Fd),
+
+ %% Read the file and verify the contents.
+
+ ?line {ok, Fd1} = ?FILE_MODULE:open(MyFile, [read, compressed]),
+ ?line Prefix = io:get_line(Fd1, ''),
+ ?line Second = lists:duplicate(143-length(Prefix), 0) ++ End,
+ ?line Second = io:get_line(Fd1, ''),
+ ?line ok = ?FILE_MODULE:close(Fd1),
+
+ %% Verify succesful compression by uncompressing the file
+ %% using zlib:gunzip/1.
+
+ ?line {ok,Contents} = file:read_file(MyFile),
+ ?line <<"hello\n",0:137/unit:8,"end\n">> = zlib:gunzip(Contents),
+
+ %% Ensure that the file is compressed.
+
+ TotalSize = 143 + length(End),
+ case ?FILE_MODULE:read_file_info(MyFile) of
+ {ok, #file_info{size=Size}} when Size < TotalSize ->
+ ok;
+ {ok, #file_info{size=Size}} when Size == TotalSize ->
+ test_server:fail(file_not_compressed)
+ end,
+
+ %% Write again to ensure that the file is truncated.
+
+ ?line {ok, Fd2} = ?FILE_MODULE:open(MyFile, [write, compressed]),
+ ?line NewString = "aaaaaaaaaaa",
+ ?line ok = io:put_chars(Fd2, NewString),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ ?line {ok, Fd3} = ?FILE_MODULE:open(MyFile, [read, compressed]),
+ ?line {ok, NewString} = ?FILE_MODULE:read(Fd3, 1024),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+
+ %% Done.
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+catenated_gzips(Config) when is_list(Config) ->
+ ?line Priv = ?config(priv_dir, Config),
+ ?line MyFile = filename:join(Priv, ?MODULE_STRING++"_test.gz"),
+
+ First = "Hello, all good men going to search parties. ",
+ Second = "Now I really need your help.",
+ All = iolist_to_binary([First|Second]),
+ ?line Cat = [zlib:gzip(First),zlib:gzip(Second)],
+
+ ?line ok = file:write_file(MyFile, Cat),
+
+ ?line {ok,Fd} = file:open(MyFile, [read,compressed,binary]),
+ ?line {ok,All} = file:read(Fd, 100000),
+ ?line ok = file:close(Fd),
+
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+compress_errors(suite) -> [];
+compress_errors(doc) -> [];
+compress_errors(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line DataDir =
+ filename:dirname(
+ filename:join(?config(data_dir, Config), "x")),
+ ?line DataDirSlash = DataDir++"/",
+ ?line {error, enoent} = ?FILE_MODULE:open("non_existing__",
+ [compressed, read]),
+ ?line {error, einval} = ?FILE_MODULE:open("non_existing__",
+ [compressed, read, write]),
+ ?line {error, einval} = ?FILE_MODULE:open("non_existing__",
+ [compressed, read, append]),
+ ?line {error, einval} = ?FILE_MODULE:open("non_existing__",
+ [compressed, write, append]),
+ ?line {error, E1} = ?FILE_MODULE:open(DataDir, [compressed, read]),
+ ?line {error, E2} = ?FILE_MODULE:open(DataDirSlash, [compressed, read]),
+ ?line {error, E3} = ?FILE_MODULE:open(DataDir, [compressed, write]),
+ ?line {error, E4} = ?FILE_MODULE:open(DataDirSlash, [compressed, write]),
+ ?line {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4},
+
+ %% Read a corrupted .gz file.
+
+ ?line Corrupted = filename:join(DataDir, "corrupted.gz"),
+ ?line {ok, Fd} = ?FILE_MODULE:open(Corrupted, [read, compressed]),
+ ?line {error, eio} = ?FILE_MODULE:read(Fd, 100),
+ ?line ?FILE_MODULE:close(Fd),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+altname(doc) ->
+ "Test the file:altname/1 function";
+altname(suite) ->
+ [];
+altname(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ "long alternative path name with spaces"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line Name = filename:join(NewDir, "a_file_with_long_name"),
+ ?line ShortName = filename:join(NewDir, "short"),
+ ?line NonexName = filename:join(NewDir, "nonexistent"),
+ ?line ok = ?FILE_MODULE:write_file(Name, "some contents\n"),
+ ?line ok = ?FILE_MODULE:write_file(ShortName, "some contents\n"),
+ ?line Result =
+ case ?FILE_MODULE:altname(NewDir) of
+ {error, enotsup} ->
+ {skipped, "Altname not supported on this platform"};
+ {ok, "LONGAL~1"} ->
+ ?line {ok, "A_FILE~1"} = ?FILE_MODULE:altname(Name),
+ ?line {ok, "C:/"} = ?FILE_MODULE:altname("C:/"),
+ ?line {ok, "C:\\"} = ?FILE_MODULE:altname("C:\\"),
+ ?line {error,enoent} = ?FILE_MODULE:altname(NonexName),
+ ?line {ok, "short"} = ?FILE_MODULE:altname(ShortName),
+ ok
+ end,
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ Result.
+
+links(doc) -> "Test the link functions.";
+links(suite) -> [make_link, read_link_info_for_non_link, symlinks].
+
+make_link(doc) -> "Test creating a hard link.";
+make_link(suite) -> [];
+make_link(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_make_link"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+
+ ?line Name = filename:join(NewDir, "a_file"),
+ ?line ok = ?FILE_MODULE:write_file(Name, "some contents\n"),
+
+ ?line Alias = filename:join(NewDir, "an_alias"),
+ ?line Result =
+ case ?FILE_MODULE:make_link(Name, Alias) of
+ {error, enotsup} ->
+ {skipped, "Links not supported on this platform"};
+ ok ->
+ %% Note: We take the opportunity to test
+ %% ?FILE_MODULE:read_link_info/1,
+ %% which should in behave exactly as
+ %% ?FILE_MODULE:read_file_info/1
+ %% since they are not used on symbolic links.
+
+ ?line {ok, Info} = ?FILE_MODULE:read_link_info(Name),
+ ?line {ok, Info} = ?FILE_MODULE:read_link_info(Alias),
+ ?line #file_info{links = 2, type = regular} = Info,
+ ?line {error, eexist} =
+ ?FILE_MODULE:make_link(Name, Alias),
+ ok
+ end,
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ Result.
+
+read_link_info_for_non_link(doc) ->
+ "Test that reading link info for an ordinary file or directory works "
+ "(on all platforms).";
+read_link_info_for_non_link(suite) -> [];
+read_link_info_for_non_link(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+
+ ?line {ok, #file_info{type=directory}} =
+ ?FILE_MODULE:read_link_info("."),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+symlinks(doc) -> "Test operations on symbolic links (for Unix).";
+symlinks(suite) -> [];
+symlinks(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_symlinks"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+
+ ?line Name = filename:join(NewDir, "a_plain_file"),
+ ?line ok = ?FILE_MODULE:write_file(Name, "some stupid content\n"),
+
+ ?line Alias = filename:join(NewDir, "a_symlink_alias"),
+ ?line Result =
+ case ?FILE_MODULE:make_symlink(Name, Alias) of
+ {error, enotsup} ->
+ {skipped, "Links not supported on this platform"};
+ ok ->
+ ?line {ok, Info1} = ?FILE_MODULE:read_file_info(Name),
+ ?line {ok, Info1} = ?FILE_MODULE:read_file_info(Alias),
+ ?line {ok, Info1} = ?FILE_MODULE:read_link_info(Name),
+ ?line #file_info{links = 1, type = regular} = Info1,
+
+ ?line {ok, Info2} = ?FILE_MODULE:read_link_info(Alias),
+ ?line #file_info{links=1, type=symlink} = Info2,
+ ?line {ok, Name} = ?FILE_MODULE:read_link(Alias),
+ ok
+ end,
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ Result.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+copy(doc) -> [];
+copy(suite) -> [];
+copy(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ %% Create a text file.
+ ?line Name1 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_1.txt"),
+ ?line Line = "The quick brown fox jumps over a lazy dog. 0123456789\n",
+ ?line Len = length(Line),
+ ?line {ok, Handle1} = ?FILE_MODULE:open(Name1, [write]),
+ ?line {_, Size1} =
+ iterate({0, 0},
+ done,
+ fun({_, S}) when S >= 128*1024 ->
+ done;
+ ({N, S}) ->
+ H = integer_to_list(N),
+ ok = ?FILE_MODULE:write(Handle1, [H, " ", Line]),
+ {N + 1, S + length(H) + 1 + Len}
+ end),
+ ?line ?FILE_MODULE:close(Handle1),
+ %% Make a copy
+ ?line Name2 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_2.txt"),
+ ?line {ok, Size1} = ?FILE_MODULE:copy(Name1, Name2),
+ %% Concatenate 1
+ ?line Name3 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_3.txt"),
+ ?line {ok, Handle3} = ?FILE_MODULE:open(Name3, [raw, write, binary]),
+ ?line {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle3),
+ ?line {ok, Handle2} = ?FILE_MODULE:open(Name2, [read, binary]),
+ ?line {ok, Size1} = ?FILE_MODULE:copy(Handle2, Handle3),
+ ?line ok = ?FILE_MODULE:close(Handle3),
+ ?line ok = ?FILE_MODULE:close(Handle2),
+ %% Concatenate 2
+ ?line Name4 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_4.txt"),
+ ?line {ok, Handle4} = ?FILE_MODULE:open(Name4, [write, binary]),
+ ?line {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle4),
+ ?line {ok, Handle5} = ?FILE_MODULE:open(Name2, [raw, read, binary]),
+ ?line {ok, Size1} = ?FILE_MODULE:copy(Handle5, Handle4),
+ ?line ok = ?FILE_MODULE:close(Handle5),
+ ?line ok = ?FILE_MODULE:close(Handle4),
+ %% %% Just for test of the test
+ %% ?line {ok, Handle2q} = ?FILE_MODULE:open(Name2, [write, append]),
+ %% ?line ok = ?FILE_MODULE:write(Handle2q, "q"),
+ %% ?line ok = ?FILE_MODULE:close(Handle2q),
+ %% Compare the files
+ ?line {ok, Handle1a} = ?FILE_MODULE:open(Name1, [raw, read]),
+ ?line {ok, Handle2a} = ?FILE_MODULE:open(Name2, [raw, read]),
+ ?line true = stream_cmp(fd_stream_factory([Handle1a]),
+ fd_stream_factory([Handle2a])),
+ ?line {ok, 0} = ?FILE_MODULE:position(Handle1a, 0),
+ ?line {ok, 0} = ?FILE_MODULE:position(Handle2a, 0),
+ ?line {ok, Handle3a} = ?FILE_MODULE:open(Name3, [raw, read]),
+ ?line true = stream_cmp(fd_stream_factory([Handle1a, Handle2a]),
+ fd_stream_factory([Handle2a])),
+ ?line ok = ?FILE_MODULE:close(Handle1a),
+ ?line ok = ?FILE_MODULE:close(Handle2a),
+ ?line ok = ?FILE_MODULE:close(Handle3a),
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+
+fd_stream_factory([]) ->
+ [];
+fd_stream_factory([Fd | T] = L) ->
+ fun() ->
+ case ?FILE_MODULE:read(Fd, 8192) of
+ {ok, Data} when is_binary(Data) ->
+ binary_to_list(Data) ++ fd_stream_factory(L);
+ {ok, Data} when is_list(Data) ->
+ Data ++ fd_stream_factory(L);
+ eof ->
+ fd_stream_factory(T);
+ {error, _} = Error ->
+ Error
+ end
+ end.
+
+
+
+stream_cmp(F1, F2) when is_function(F1), is_function(F2) ->
+ stream_cmp(F1(), F2());
+stream_cmp(F, X) when is_function(F) ->
+ stream_cmp(F(), X);
+stream_cmp(X, F) when is_function(F) ->
+ stream_cmp(X, F());
+stream_cmp({error, _} = Error, _) ->
+ Error;
+stream_cmp(_, {error, _} = Error) ->
+ Error;
+stream_cmp([], []) ->
+ true;
+stream_cmp([], [_|_]) ->
+ false;
+stream_cmp([_|_], []) ->
+ false;
+stream_cmp([H | T1], [H | T2]) ->
+ stream_cmp(T1, T2).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Test the get_cwd(), open(), and copy() file server calls.
+new_slave(_RootDir, Cwd) ->
+ ?line L = "qwertyuiopasdfghjklzxcvbnm",
+ ?line N = length(L),
+ ?line {ok, Cwd} = ?FILE_MODULE:get_cwd(),
+ ?line {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase
+ ?line {ok, FD1} = ?FILE_MODULE:open("file1.txt", write),
+ ?line ok = ?FILE_MODULE:close(FD1),
+ ?line {ok, FD2} = ?FILE_MODULE:open("file1.txt",
+ [write, append,
+ binary, compressed,
+ delayed_write,
+ {delayed_write, 0, 0},
+ read_ahead,
+ {read_ahead, 0}]),
+ ?line ok = ?FILE_MODULE:write(FD2, L),
+ ?line ok = ?FILE_MODULE:close(FD2),
+ ?line {ok, N2} = ?FILE_MODULE:copy("file1.txt", "file2.txt"),
+ ?line io:format("Size ~p, compressed ~p.~n", [N, N2]),
+ ?line {ok, FD3} = ?FILE_MODULE:open("file2.txt",
+ [binary, compressed]),
+ %% The file_io_server will translate the binary into a list
+ ?line {ok, L} = ?FILE_MODULE:read(FD3, N+1),
+ ?line ok = ?FILE_MODULE:close(FD3),
+ %%
+ ?line ok = ?FILE_MODULE:delete("file1.txt"),
+ ?line ok = ?FILE_MODULE:delete("file2.txt"),
+ ?line [] = flush(),
+ ok.
+
+
+%% Test the get_cwd() and open() file server calls.
+old_slave(_RootDir, Cwd) ->
+ ?line L = "qwertyuiopasdfghjklzxcvbnm",
+ ?line N = length(L),
+ ?line {ok, Cwd} = ?FILE_MODULE:get_cwd(),
+ ?line {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase
+ ?line {ok, FD1} = ?FILE_MODULE:open("file1.txt", write),
+ ?line ok = ?FILE_MODULE:close(FD1),
+ ?line {ok, FD2} = ?FILE_MODULE:open("file1.txt",
+ [write, binary, compressed]),
+ ?line ok = ?FILE_MODULE:write(FD2, L),
+ ?line ok = ?FILE_MODULE:close(FD2),
+ ?line {ok, FD3} = ?FILE_MODULE:open("file1.txt", [write, append]),
+ ?line ok = ?FILE_MODULE:close(FD3),
+ ?line {ok, FD4} = ?FILE_MODULE:open("file1.txt",
+ [binary, compressed]),
+ %% The file_io_server will translate the binary into a list
+ ?line {ok, L} = ?FILE_MODULE:read(FD4, N+1),
+ ?line ok = ?FILE_MODULE:close(FD4),
+ %%
+ ?line ok = ?FILE_MODULE:delete("file1.txt"),
+ ?line [] = flush(),
+ ok.
+
+run_test(Test, Args) ->
+ ?line case (catch apply(?MODULE, Test, Args)) of
+ {'EXIT', _} = Exit ->
+ {done, Exit, get(test_server_loc)};
+ Result ->
+ {done, Result}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+delayed_write(suite) ->
+ [];
+delayed_write(doc) ->
+ ["Tests the file open option {delayed_write, Size, Delay}"];
+
+delayed_write(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(20)),
+ %%
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line File = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_delayed_write.txt"),
+ ?line Data1 = "asdfghjkl",
+ ?line Data2 = "qwertyuio",
+ ?line Data3 = "zxcvbnm,.",
+ ?line Size = length(Data1),
+ ?line Size = length(Data2),
+ ?line Size = length(Data3),
+ ?line Data1Data1 = Data1++Data1,
+ ?line Data1Data1Data1 = Data1Data1++Data1,
+ ?line Data1Data1Data1Data1 = Data1Data1++Data1Data1,
+ %%
+ %% Test caching and normal close of non-raw file
+ ?line {ok, Fd1} =
+ ?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 2000}]),
+ ?line ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Fd2} = ?FILE_MODULE:open(File, [read]),
+ ?line case os:type() of
+ vxworks ->
+ io:format("Line ~p skipped on vxworks", [?LINE]);
+ _ ->
+ ?line eof = ?FILE_MODULE:read(Fd2, 1)
+ end,
+ ?line ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1),
+ ?line ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?line ?t:sleep(3000), % Wait until data flush on timeout
+ ?line {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1),
+ ?line ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?line ok = ?FILE_MODULE:close(Fd1), % Data flush on close
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ %%
+ %% Test implicit close through exit by file owning process,
+ %% raw file, default parameters.
+ ?line Parent = self(),
+ ?line Fun =
+ fun () ->
+ Child = self(),
+ Test =
+ fun () ->
+ ?line {ok, Fd} =
+ ?FILE_MODULE:open(File,
+ [raw, write,
+ delayed_write]),
+ ?line ok = ?FILE_MODULE:write(Fd, Data1),
+ ?line Parent ! {Child, wrote},
+ ?line receive
+ {Parent, continue, Reason} ->
+ {ok, Reason}
+ end
+ end,
+ case (catch Test()) of
+ {ok, Reason} ->
+ exit(Reason);
+ Unknown ->
+ exit({Unknown, get(test_server_loc)})
+ end
+ end,
+ ?line Child1 = spawn(Fun),
+ ?line Mref1 = erlang:monitor(process, Child1),
+ ?line receive
+ {Child1, wrote} ->
+ ok;
+ {'DOWN', Mref1, _, _, _} = Down1a ->
+ ?t:fail(Down1a)
+ end,
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Fd3} = ?FILE_MODULE:open(File, [read]),
+ ?line case os:type() of
+ vxworks ->
+ io:format("Line ~p skipped on vxworks", [?LINE]);
+ _ ->
+ ?line eof = ?FILE_MODULE:read(Fd3, 1)
+ end,
+ ?line Child1 ! {Parent, continue, normal},
+ ?line receive
+ {'DOWN', Mref1, process, Child1, normal} ->
+ ok;
+ {'DOWN', Mref1, _, _, _} = Down1b ->
+ ?t:fail(Down1b)
+ end,
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ %%
+ %% The same again, but this time with reason 'kill'.
+ ?line Child2 = spawn(Fun),
+ ?line Mref2 = erlang:monitor(process, Child2),
+ ?line receive
+ {Child2, wrote} ->
+ ok;
+ {'DOWN', Mref2, _, _, _} = Down2a ->
+ ?t:fail(Down2a)
+ end,
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Fd4} = ?FILE_MODULE:open(File, [read]),
+ ?line case os:type() of
+ vxworks ->
+ io:format("Line ~p skipped on vxworks", [?LINE]);
+ _ ->
+ ?line eof = ?FILE_MODULE:read(Fd4, 1)
+ end,
+ ?line Child2 ! {Parent, continue, kill},
+ ?line receive
+ {'DOWN', Mref2, process, Child2, kill} ->
+ ok;
+ {'DOWN', Mref2, _, _, _} = Down2b ->
+ ?t:fail(Down2b)
+ end,
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line eof = ?FILE_MODULE:pread(Fd4, bof, 1),
+ ?line ok = ?FILE_MODULE:close(Fd4),
+ %%
+ %% Test if file position works with delayed_write
+ ?line {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write,
+ delayed_write]),
+ ?line ok = ?FILE_MODULE:truncate(Fd5),
+ ?line ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ ?line ok = ?FILE_MODULE:write(Fd5, [Data3]),
+ ?line {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ ?line Data3Data2 = Data3++Data2,
+ ?line {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1),
+ ?line ok = ?FILE_MODULE:close(Fd5),
+ %%
+ ?line [] = flush(),
+ ?line ?t:timetrap_cancel(Dog),
+ ?line case os:type() of
+ vxworks ->
+ {comment, "Some lines skipped on vxworks"};
+ _ ->
+ ok
+ end.
+
+
+pid2name(doc) -> "Tests file:pid2name/1.";
+pid2name(suite) -> [];
+pid2name(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = test_server:temp_name(
+ filename:join(RootDir, "pid2name_")),
+ ?line Name1 = [Base, '.txt'],
+ ?line Name2 = Base ++ ".txt",
+ %%
+ ?line {ok, Pid} = file:open(Name1, [write]),
+ ?line {ok, Name2} = file:pid2name(Pid),
+ ?line undefined = file:pid2name(self()),
+ ?line ok = file:close(Pid),
+ ?line test_server:sleep(1000),
+ ?line false = is_process_alive(Pid),
+ ?line undefined = file:pid2name(Pid),
+ %%
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+read_ahead(suite) ->
+ [];
+read_ahead(doc) ->
+ ["Tests the file open option {read_ahead, Size}"];
+
+read_ahead(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(20)),
+ %%
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line File = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_read_ahead.txt"),
+ ?line Data1 = "asdfghjkl", % Must be
+ ?line Data2 = "qwertyuio", % same
+ ?line Data3 = "zxcvbnm,.", % length
+ ?line Size = length(Data1),
+ ?line Size = length(Data2),
+ ?line Size = length(Data3),
+ %%
+ %% Test caching of normal non-raw file
+ ?line {ok, Fd1} = ?FILE_MODULE:open(File, [write]),
+ ?line ok = ?FILE_MODULE:write(Fd1, [Data1|Data1]),
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Fd2} = ?FILE_MODULE:open(File, [read, {read_ahead, 2*Size}]),
+ ?line {ok, Data1} = ?FILE_MODULE:read(Fd2, Size),
+ ?line ok = ?FILE_MODULE:pwrite(Fd1, Size, Data2),
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), % Will read cached data
+ ?line Data2Data2Data2 = Data2++Data2++Data2,
+ ?line ok = ?FILE_MODULE:pwrite(Fd1, eof, Data2Data2Data2),
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Data2Data2Data2} =
+ ?FILE_MODULE:read(Fd2, 3*Size), % Read more than cache buffer
+ ?line ok = ?FILE_MODULE:close(Fd1),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+ %% Test caching of raw file and default parameters
+ ?line {ok, Fd3} = ?FILE_MODULE:open(File, [raw, write]),
+ ?line ok = ?FILE_MODULE:write(Fd3, [Data1|Data1]),
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Fd4} = ?FILE_MODULE:open(File, [raw, read, read_ahead]),
+ ?line {ok, Data1} = ?FILE_MODULE:read(Fd4, Size),
+ ?line ok = ?FILE_MODULE:pwrite(Fd3, Size, Data2),
+ ?line ?t:sleep(1000), % Just in case the file system is slow
+ ?line {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), % Will read cached data
+ ?line ok = ?FILE_MODULE:close(Fd3),
+ ?line ok = ?FILE_MODULE:close(Fd4),
+ %% Test if the file position works in combination with read_ahead
+ ?line {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, read_ahead]),
+ ?line ok = ?FILE_MODULE:truncate(Fd5),
+ ?line ok = ?FILE_MODULE:write(Fd5, [Data1,Data1|Data3]),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ ?line {ok, Data1} = ?FILE_MODULE:read(Fd5, Size),
+ ?line ok = ?FILE_MODULE:write(Fd5, Data2),
+ ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ ?line Data1Data2Data3 = Data1++Data2++Data3,
+ ?line {ok, Data1Data2Data3} = ?FILE_MODULE:read(Fd5, 3*Size+1),
+ ?line ok = ?FILE_MODULE:close(Fd5),
+ %%
+ ?line [] = flush(),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+segment_read(suite) ->
+ [];
+segment_read(doc) ->
+ ["Tests the segmenting of large reads"];
+segment_read(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(60)),
+ %%
+ ?line Name = filename:join(?config(priv_dir, Config),
+ ?MODULE_STRING ++ "_segment_read"),
+ ?line SegSize = 256*1024,
+ ?line SegCnt = SegSize div 4,
+ ?line Cnt = 4 * SegCnt,
+ ?line ok = create_file(Name, Cnt),
+ %%
+ %% read_file/1
+ %%
+ ?line {ok, Bin} = ?FILE_MODULE:read_file(Name),
+ ?line true = verify_bin(Bin, 0, Cnt),
+ %%
+ %% read/2
+ %%
+ %% Not segmented
+ ?line {ok, FD1} = ?FILE_MODULE:open(Name, [read, raw, binary]),
+ ?line {ok, B1a} = ?FILE_MODULE:read(FD1, SegSize),
+ ?line {ok, B1b} = ?FILE_MODULE:read(FD1, SegSize),
+ ?line {ok, B1c} = ?FILE_MODULE:read(FD1, SegSize),
+ ?line {ok, B1d} = ?FILE_MODULE:read(FD1, SegSize),
+ ?line ok = ?FILE_MODULE:close(FD1),
+ ?line true = verify_bin(B1a, 0*SegCnt, SegCnt),
+ ?line true = verify_bin(B1b, 1*SegCnt, SegCnt),
+ ?line true = verify_bin(B1c, 2*SegCnt, SegCnt),
+ ?line true = verify_bin(B1d, 3*SegCnt, SegCnt),
+ %%
+ %% Segmented
+ ?line {ok, FD2} = ?FILE_MODULE:open(Name, [read, raw, binary]),
+ ?line {ok, B2a} = ?FILE_MODULE:read(FD2, 1*SegSize),
+ ?line {ok, B2b} = ?FILE_MODULE:read(FD2, 2*SegSize),
+ ?line {ok, B2c} = ?FILE_MODULE:read(FD2, 2*SegSize),
+ ?line ok = ?FILE_MODULE:close(FD2),
+ ?line true = verify_bin(B2a, 0*SegCnt, 1*SegCnt),
+ ?line true = verify_bin(B2b, 1*SegCnt, 2*SegCnt),
+ ?line true = verify_bin(B2c, 3*SegCnt, 1*SegCnt),
+ %%
+ %% pread/3
+ %%
+ ?line {ok, FD3} = ?FILE_MODULE:open(Name, [read, raw, binary]),
+ %%
+ %% Not segmented
+ ?line {ok, B3d} = ?FILE_MODULE:pread(FD3, 3*SegSize, SegSize),
+ ?line {ok, B3c} = ?FILE_MODULE:pread(FD3, 2*SegSize, SegSize),
+ ?line {ok, B3b} = ?FILE_MODULE:pread(FD3, 1*SegSize, SegSize),
+ ?line {ok, B3a} = ?FILE_MODULE:pread(FD3, 0*SegSize, SegSize),
+ ?line true = verify_bin(B3a, 0*SegCnt, SegCnt),
+ ?line true = verify_bin(B3b, 1*SegCnt, SegCnt),
+ ?line true = verify_bin(B3c, 2*SegCnt, SegCnt),
+ ?line true = verify_bin(B3d, 3*SegCnt, SegCnt),
+ %%
+ %% Segmented
+ ?line {ok, B3g} = ?FILE_MODULE:pread(FD3, 3*SegSize, 2*SegSize),
+ ?line {ok, B3f} = ?FILE_MODULE:pread(FD3, 1*SegSize, 2*SegSize),
+ ?line {ok, B3e} = ?FILE_MODULE:pread(FD3, 0*SegSize, 1*SegSize),
+ ?line true = verify_bin(B3e, 0*SegCnt, 1*SegCnt),
+ ?line true = verify_bin(B3f, 1*SegCnt, 2*SegCnt),
+ ?line true = verify_bin(B3g, 3*SegCnt, 1*SegCnt),
+ %%
+ ?line ok = ?FILE_MODULE:close(FD3),
+ %%
+ %% pread/2
+ %%
+ ?line {ok, FD5} = ?FILE_MODULE:open(Name, [read, raw, binary]),
+ %%
+ %% +---+---+---+---+
+ %% | 4 | 3 | 2 | 1 |
+ %% +---+---+---+---+
+ %% < ^ >
+ ?line {ok, [B5d, B5c, B5b, B5a]} =
+ ?FILE_MODULE:pread(FD5, [{3*SegSize, SegSize},
+ {2*SegSize, SegSize},
+ {1*SegSize, SegSize},
+ {0*SegSize, SegSize}]),
+ ?line true = verify_bin(B5a, 0*SegCnt, SegCnt),
+ ?line true = verify_bin(B5b, 1*SegCnt, SegCnt),
+ ?line true = verify_bin(B5c, 2*SegCnt, SegCnt),
+ ?line true = verify_bin(B5d, 3*SegCnt, SegCnt),
+ %%
+ %% +---+-------+-------+
+ %% | 3 | 2 | 1 |
+ %% +---+-------+-------+
+ %% < ^ ^ >
+ ?line {ok, [B5g, B5f, B5e]} =
+ ?FILE_MODULE:pread(FD5, [{3*SegSize, 2*SegSize},
+ {1*SegSize, 2*SegSize},
+ {0*SegSize, 1*SegSize}]),
+ ?line true = verify_bin(B5e, 0*SegCnt, 1*SegCnt),
+ ?line true = verify_bin(B5f, 1*SegCnt, 2*SegCnt),
+ ?line true = verify_bin(B5g, 3*SegCnt, 1*SegCnt),
+ %%
+ %%
+ %% +-------+-----------+
+ %% | 2 | 1 |
+ %% +-------+-----------+
+ %% < ^ ^ >
+ ?line {ok, [B5i, B5h]} =
+ ?FILE_MODULE:pread(FD5, [{2*SegSize, 3*SegSize},
+ {0*SegSize, 2*SegSize}]),
+ ?line true = verify_bin(B5h, 0*SegCnt, 2*SegCnt),
+ ?line true = verify_bin(B5i, 2*SegCnt, 2*SegCnt),
+ %%
+ %% +-------+---+---+
+ %% | 3 | 2 | 1 |
+ %% +-------+---+---+
+ %% < ^ ^ >
+ ?line {ok, [B5l, B5k, B5j]} =
+ ?FILE_MODULE:pread(FD5, [{3*SegSize, 1*SegSize},
+ {2*SegSize, 1*SegSize},
+ {0*SegSize, 2*SegSize}]),
+ ?line true = verify_bin(B5j, 0*SegCnt, 2*SegCnt),
+ ?line true = verify_bin(B5k, 2*SegCnt, 1*SegCnt),
+ ?line true = verify_bin(B5l, 3*SegCnt, 1*SegCnt),
+ %%
+ %% Real time response time test.
+ %%
+ Req = lists:flatten(lists:duplicate(17,
+ [{2*SegSize, 2*SegSize},
+ {0*SegSize, 2*SegSize}])),
+ ?line {{ok, _}, Comment} =
+ response_analysis(?FILE_MODULE, pread, [FD5, Req]),
+ ?line ok = ?FILE_MODULE:close(FD5),
+ %%
+ ?line [] = flush(),
+ ?line ?t:timetrap_cancel(Dog),
+ {comment, Comment}.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+segment_write(suite) ->
+ [];
+segment_write(doc) ->
+ ["Tests the segmenting of large writes"];
+segment_write(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(60)),
+ %%
+ ?line Name = filename:join(?config(priv_dir, Config),
+ ?MODULE_STRING ++ "_segment_write"),
+ ?line SegSize = 256*1024,
+ ?line SegCnt = SegSize div 4,
+ ?line Cnt = 4 * SegCnt,
+ ?line Bin = create_bin(0, Cnt),
+ %%
+ %% write/2
+ %%
+ %% Not segmented
+ ?line {ok, FD1} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 0*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 1*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 2*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 3*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:close(FD1),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% Segmented
+ ?line {ok, FD2} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ ?line ok = ?FILE_MODULE:write(FD2, subbin(Bin, 0*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:write(FD2, subbin(Bin, 1*SegSize, 2*SegSize)),
+ ?line ok = ?FILE_MODULE:write(FD2, subbin(Bin, 3*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:close(FD2),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +---+---+---+---+
+ %% | | | | |
+ %% +---+---+---+---+
+ %% < ^ >
+ ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize),
+ subbin(Bin, 1*SegSize, 1*SegSize),
+ subbin(Bin, 2*SegSize, 1*SegSize),
+ subbin(Bin, 3*SegSize, 1*SegSize)]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +---+-------+---+
+ %% | | | |
+ %% +---+-------+---+
+ %% < ^ ^ >
+ ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize),
+ subbin(Bin, 1*SegSize, 2*SegSize),
+ subbin(Bin, 3*SegSize, 1*SegSize)]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +-------+-------+
+ %% | | |
+ %% +-------+-------+
+ %% < ^ ^ >
+ ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize),
+ subbin(Bin, 2*SegSize, 2*SegSize)]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +-------+---+---+
+ %% | | | |
+ %% +-------+---+---+
+ %% < ^ ^ >
+ ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize),
+ subbin(Bin, 2*SegSize, 1*SegSize),
+ subbin(Bin, 3*SegSize, 1*SegSize)]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% pwrite/3
+ %%
+ %% Not segmented
+ ?line {ok, FD3} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ ?line ok = ?FILE_MODULE:pwrite(FD3, 3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:pwrite(FD3, 2*SegSize,
+ subbin(Bin, 2*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:pwrite(FD3, 1*SegSize,
+ subbin(Bin, 1*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:pwrite(FD3, 0*SegSize,
+ subbin(Bin, 0*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:close(FD3),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% Segmented
+ ?line {ok, FD4} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ ?line ok = ?FILE_MODULE:pwrite(FD4, 3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:pwrite(FD4, 1*SegSize,
+ subbin(Bin, 1*SegSize, 2*SegSize)),
+ ?line ok = ?FILE_MODULE:pwrite(FD4, 0*SegSize,
+ subbin(Bin, 0*SegSize, 1*SegSize)),
+ ?line ok = ?FILE_MODULE:close(FD4),
+ ?line true = verify_file(Name, Cnt),
+
+
+
+ %%
+ %% pwrite/2
+ %%
+ %% Not segmented
+ ?line {ok, FD5} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ ?line ok = ?FILE_MODULE:pwrite(FD5, [{3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)}]),
+ ?line ok = ?FILE_MODULE:pwrite(FD5, [{2*SegSize,
+ subbin(Bin, 2*SegSize, 1*SegSize)}]),
+ ?line ok = ?FILE_MODULE:pwrite(FD5, [{1*SegSize,
+ subbin(Bin, 1*SegSize, 1*SegSize)}]),
+ ?line ok = ?FILE_MODULE:pwrite(FD5, [{0*SegSize,
+ subbin(Bin, 0*SegSize, 1*SegSize)}]),
+ ?line ok = ?FILE_MODULE:close(FD5),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% Segmented
+ ?line {ok, FD6} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ ?line ok = ?FILE_MODULE:pwrite(FD6, [{3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)}]),
+ ?line ok = ?FILE_MODULE:pwrite(FD6, [{1*SegSize,
+ subbin(Bin, 1*SegSize, 2*SegSize)}]),
+ ?line ok = ?FILE_MODULE:pwrite(FD6, [{0*SegSize,
+ subbin(Bin, 0*SegSize, 1*SegSize)}]),
+ ?line ok = ?FILE_MODULE:close(FD6),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +---+---+---+---+
+ %% | 4 | 3 | 2 | 1 |
+ %% +---+---+---+---+
+ %% < ^ >
+ ?line ok = pwrite_file(Name, [{3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)},
+ {2*SegSize,
+ subbin(Bin, 2*SegSize, 1*SegSize)},
+ {1*SegSize,
+ subbin(Bin, 1*SegSize, 1*SegSize)},
+ {0*SegSize,
+ subbin(Bin, 0*SegSize, 1*SegSize)}]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +---+-------+---+
+ %% | 3 | 2 | 1 |
+ %% +---+-------+---+
+ %% < ^ ^ >
+ ?line ok = pwrite_file(Name, [{3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)},
+ {1*SegSize,
+ subbin(Bin, 1*SegSize, 2*SegSize)},
+ {0*SegSize,
+ subbin(Bin, 0*SegSize, 1*SegSize)}]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +-------+-------+
+ %% | 2 | 1 |
+ %% +-------+-------+
+ %% < ^ ^ >
+ ?line ok = pwrite_file(Name, [{2*SegSize,
+ subbin(Bin, 2*SegSize, 2*SegSize)},
+ {0*SegSize,
+ subbin(Bin, 0*SegSize, 2*SegSize)}]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% +-------+---+---+
+ %% | 3 | 2 | 1 |
+ %% +-------+---+---+
+ %% < ^ ^ >
+ ?line ok = pwrite_file(Name, [{3*SegSize,
+ subbin(Bin, 3*SegSize, 1*SegSize)},
+ {2*SegSize,
+ subbin(Bin, 2*SegSize, 1*SegSize)},
+ {0*SegSize,
+ subbin(Bin, 0*SegSize, 2*SegSize)}]),
+ ?line true = verify_file(Name, Cnt),
+ %%
+ %% Real time response time test.
+ %%
+ ?line {ok, FD7} = ?FILE_MODULE:open(Name, [write, raw, binary]),
+ Req = lists:flatten(lists:duplicate(17,
+ [{2*SegSize,
+ subbin(Bin, 2*SegSize, 2*SegSize)},
+ {0*SegSize,
+ subbin(Bin, 0*SegSize, 2*SegSize)}])),
+ ?line {ok, Comment} =
+ response_analysis(?FILE_MODULE, pwrite, [FD7, Req]),
+ ?line ok = ?FILE_MODULE:close(FD7),
+ %%
+ ?line [] = flush(),
+ ?line ?t:timetrap_cancel(Dog),
+ {comment, Comment}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ipread(suite) ->
+ [];
+ipread(doc) ->
+ ["Test Dets special indirect pread"];
+ipread(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(30)),
+ %%
+ ?line Dir = ?config(priv_dir, Config),
+ ?line ok = ipread_int(Dir, [raw, binary]),
+ ?line ok = ipread_int(Dir, [raw]),
+ ?line ok = ipread_int(Dir, [binary]),
+ ?line ok = ipread_int(Dir, []),
+ ?line ok = ipread_int(Dir, [ram, binary]),
+ ?line ok = ipread_int(Dir, [ram]),
+ %%
+ ?line [] = flush(),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+ipread_int(Dir, ModeList) ->
+ ?line Name =
+ filename:join(Dir,
+ lists:flatten([?MODULE_STRING, "_ipread",
+ lists:map(fun (X) ->
+ ["_", atom_to_list(X)]
+ end,
+ ModeList)])),
+ ?line io:format("ipread_int<~p, ~p>~n", [Name, ModeList]),
+ ?line {Conv, Sizeof} =
+ case lists:member(binary, ModeList) of
+ true ->
+ {fun (Bin) when is_binary(Bin) -> Bin;
+ (List) when is_list(List) -> list_to_binary(List)
+ end,
+ {erlang, size}};
+ false ->
+ {fun (Bin) when is_binary(Bin) -> binary_to_list(Bin);
+ (List) when is_list(List) -> List
+ end,
+ {erlang, length}}
+ end,
+ ?line Pos = 4711,
+ ?line Data = Conv("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG"),
+ ?line Size = Sizeof(Data),
+ ?line Init = Conv(" "),
+ ?line SizeInit = Sizeof(Init),
+ ?line Head = Conv(<<Size:32/big-unsigned, Pos:32/big-unsigned>>),
+ ?line Filler = Conv(bytes($ , Pos-SizeInit-Sizeof(Head))),
+ ?line Size1 = Size+1,
+ ?line SizePos = Size+Pos,
+ %%
+ ?line {ok, FD} = ?FILE_MODULE:open(Name, [write, read | ModeList]),
+ ?line ok = ?FILE_MODULE:truncate(FD),
+ ?line ok = ?FILE_MODULE:write(FD, Init),
+ ?line ok = ?FILE_MODULE:write(FD, Head),
+ ?line ok = ?FILE_MODULE:write(FD, Filler),
+ ?line ok = ?FILE_MODULE:write(FD, Data),
+ %% Correct read
+ ?line {ok, {Size, Pos, Data}} =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, infinity),
+ %% Invalid header - size > max
+ ?line eof =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size-1),
+ %% Data block protudes over eof
+ ?line ok =
+ ?FILE_MODULE:pwrite(FD, SizeInit,
+ <<Size1:32/big-unsigned,
+ Pos:32/big-unsigned>>),
+ ?line {ok, {Size1, Pos, Data}} =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size1),
+ %% Data block outside file
+ ?line ok =
+ ?FILE_MODULE:pwrite(FD, SizeInit,
+ <<Size:32/big-unsigned,
+ SizePos:32/big-unsigned>>),
+ ?line {ok, {Size, SizePos, eof}} =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size),
+ %% Zero size
+ ?line ok =
+ ?FILE_MODULE:pwrite(FD, SizeInit,
+ <<0:32/big-unsigned,
+ Pos:32/big-unsigned>>),
+ ?line {ok, {0, Pos, eof}} =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size),
+ %% Invalid header - protudes over eof
+ ?line eof =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD,
+ Pos+Size-(Sizeof(Head)-1),
+ infinity),
+ %% Header not even in file
+ ?line eof =
+ ?FILE_MODULE:ipread_s32bu_p32bu(FD, Pos+Size, infinity),
+ %%
+ ?line ok = ?FILE_MODULE:close(FD),
+ ok.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+interleaved_read_write(suite) ->
+ [];
+interleaved_read_write(doc) ->
+ ["Tests interleaved read and writes"];
+interleaved_read_write(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(30)),
+ %%
+ ?line Dir = ?config(priv_dir, Config),
+ ?line File =
+ filename:join(Dir, ?MODULE_STRING++"interleaved_read_write.txt"),
+ ?line {ok,F1} = ?FILE_MODULE:open(File, [write]),
+ ?line ok = ?FILE_MODULE:write(F1, "data---r1."), % 10 chars each
+ ?line ok = ?FILE_MODULE:write(F1, "data---r2."),
+ ?line ok = ?FILE_MODULE:write(F1, "data---r3."),
+ ?line ok = ?FILE_MODULE:close(F1),
+ ?line {ok,F2} = ?FILE_MODULE:open(File, [read, write]),
+ ?line {ok, "data---r1."} = ?FILE_MODULE:read(F2, 10),
+ ?line ok = ?FILE_MODULE:write(F2, "data---w2."),
+ ?line ok = ?FILE_MODULE:close(F2),
+ ?line {ok,F3} = ?FILE_MODULE:open(File, [read]),
+ ?line {ok, "data---r1."} = ?FILE_MODULE:read(F3, 10),
+ ?line {ok, "data---w2."} = ?FILE_MODULE:read(F3, 10),
+ ?line {ok, "data---r3."} = ?FILE_MODULE:read(F3, 10),
+ ?line eof = ?FILE_MODULE:read(F3, 1),
+ ?line ok = ?FILE_MODULE:close(F2),
+ %%
+ ?line [] = flush(),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+otp_5814(suite) ->
+ [];
+otp_5814(doc) ->
+ ["OTP-5814. eval/consult/script return correct line numbers"];
+otp_5814(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(10)),
+ PrivDir = ?config(priv_dir, Config),
+ File = filename:join(PrivDir, "otp_5814"),
+ Path = [PrivDir],
+ ?line ok = file:write_file(File, <<"{a,b,c}.
+ a.
+ b.
+ c.
+ {d,e,
+ [}.">>),
+ ?line {error, {6,erl_parse,_}} = file:eval(File),
+ ?line {error, {6,erl_parse,_}} = file:consult(File),
+ ?line {error, {6,erl_parse,_}} = file:path_consult(Path, File),
+ ?line {error, {6,erl_parse,_}} = file:path_eval(Path, File),
+ ?line {error, {6,erl_parse,_}} = file:script(File),
+ ?line {error, {6,erl_parse,_}} = file:path_script(Path, File),
+
+ ?line ok = file:write_file(File, <<>>),
+ ?line {error, {1,file,undefined_script}} = file:path_script(Path, File),
+
+ %% The error is not propagated...
+ ?line ok = file:write_file(File, <<"a.
+ b.
+ 1/0.">>),
+ ?line {error, {3, file, {error, badarith, _}}} = file:eval(File),
+
+ ?line ok = file:write_file(File, <<"erlang:raise(throw, apa, []).">>),
+ ?line {error, {1, file, {throw, apa, _}}} = file:eval(File),
+
+ file:delete(File),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+large_file(suite) ->
+ [];
+large_file(doc) ->
+ ["Tests positioning in large files (> 4G)"];
+large_file(Config) when is_list(Config) ->
+ case {os:type(),os:version()} of
+ {{win32,nt},_} ->
+ do_large_file(Config);
+ {{unix,sunos},{A,B,C}}
+ when A == 5, B == 5, C >= 1; A == 5, B >= 6; A >= 6 ->
+ do_large_file(Config);
+ {{unix,Unix},_} when Unix =:= linux; Unix =:= darwin ->
+ N = unix_free(Config),
+ io:format("Free: ~w KByte~n", [N]),
+ if N < 5 * (1 bsl 20) ->
+ %% Less than 5 GByte free
+ {skipped,"Less than 5 GByte free"};
+ true ->
+ do_large_file(Config)
+ end;
+ _ ->
+ {skipped,"Only supported on Win32, Linux, or SunOS >= 5.5.1"}
+ end.
+
+unix_free(Config) ->
+ Cmd = ["df -k '",?config(priv_dir, Config),"'"],
+ DF0 = os:cmd(Cmd),
+ io:format("$ ~s~n~s", [Cmd,DF0]),
+ [$\n|DF1] = lists:dropwhile(fun ($\n) -> false; (_) -> true end, DF0),
+ {ok,[N],_} = io_lib:fread(" ~*s ~d", DF1),
+ N.
+
+do_large_file(Config) ->
+ ?line Watchdog = ?t:timetrap(?t:minutes(4)),
+ %%
+ ?line Name = filename:join(?config(priv_dir, Config),
+ ?MODULE_STRING ++ "_large_file"),
+ ?line Tester = self(),
+ Deleter =
+ spawn(
+ fun() ->
+ Mref = erlang:monitor(process, Tester),
+ receive
+ {'DOWN',Mref,_,_,_} -> ok;
+ {Tester,done} -> ok
+ end,
+ ?FILE_MODULE:delete(Name)
+ end),
+ %%
+ ?line S = "1234567890",
+ L = length(S),
+ R = lists:reverse(S),
+ P = 1 bsl 32,
+ Ss = lists:sort(S),
+ Rs = lists:reverse(Ss),
+ ?line {ok,F} = ?FILE_MODULE:open(Name, [raw,read,write]),
+ ?line ok = ?FILE_MODULE:write(F, S),
+ ?line {ok,P} = ?FILE_MODULE:position(F, P),
+ ?line ok = ?FILE_MODULE:write(F, R),
+ ?line {ok,0} = ?FILE_MODULE:position(F, bof),
+ ?line {ok,S} = ?FILE_MODULE:read(F, L),
+ ?line {ok,P} = ?FILE_MODULE:position(F, {eof,-L}),
+ ?line {ok,R} = ?FILE_MODULE:read(F, L+1),
+ ?line {ok,S} = ?FILE_MODULE:pread(F, 0, L),
+ ?line {ok,R} = ?FILE_MODULE:pread(F, P, L+1),
+ ?line ok = ?FILE_MODULE:pwrite(F, 0, Ss),
+ ?line ok = ?FILE_MODULE:pwrite(F, P, Rs),
+ ?line {ok,0} = ?FILE_MODULE:position(F, bof),
+ ?line {ok,Ss} = ?FILE_MODULE:read(F, L),
+ ?line {ok,P} = ?FILE_MODULE:position(F, {eof,-L}),
+ ?line {ok,Rs} = ?FILE_MODULE:read(F, L+1),
+ ?line ok = ?FILE_MODULE:close(F),
+ %%
+ ?line Mref = erlang:monitor(process, Deleter),
+ ?line Deleter ! {Tester,done},
+ ?line receive {'DOWN',Mref,_,_,_} -> ok end,
+ %%
+ ?line ?t:timetrap_cancel(Watchdog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+response_analysis(Module, Function, Arguments) ->
+ Parent = self(),
+ ?line erlang:yield(), % Schedule out before test
+ ?line Child =
+ spawn_link(
+ fun () ->
+ receive {Parent, start, Ts} -> ok end,
+ Stat =
+ iterate(response_stat(response_stat(init, Ts),
+ erlang:now()),
+ done,
+ fun (S) ->
+ erlang:yield(),
+ receive
+ {Parent, stop} ->
+ done
+ after 0 ->
+ response_stat(S, erlang:now())
+ end
+ end),
+ Parent ! {self(), stopped, response_stat(Stat, erlang:now())}
+ end),
+ ?line Child ! {Parent, start, erlang:now()},
+ ?line Result = apply(Module, Function, Arguments),
+ ?line Child ! {Parent, stop},
+ ?line {N, Sum, _, M, Max} = receive {Child, stopped, X} -> X end,
+ ?line Mean_ms = (0.001*Sum) / (N-1),
+ ?line Max_ms = 0.001 * Max,
+ ?line Comment =
+ lists:flatten(
+ io_lib:format(
+ "Scheduling interval: Mean = ~.3f ms, "
+ ++"Max = ~.3f ms for no ~p of ~p.~n",
+ [Mean_ms, Max_ms, M, (N-1)])),
+ ?line {Result, Comment}.
+
+
+
+response_stat(init, Ts) ->
+ {0, 0, Ts, 0, 0};
+response_stat({N, Sum, {A1, B1, C1}, M, Max}, {A2, B2, C2} = Ts) ->
+ D = C2-C1 + 1000000*((B2-B1) + 1000000*(A2-A1)),
+ if D > Max ->
+ {N+1, Sum+D, Ts, N, D};
+ true ->
+ {N+1, Sum+D, Ts, M, Max}
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+%% This function is kept just for benchmarking reasons.
+%% create_file/2 below is some 44 times faster.
+
+create_file_slow(Name, N) when is_integer(N), N >= 0 ->
+ ?line {ok, FD} =
+ ?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]),
+ ?line ok = create_file_slow(FD, 0, N),
+ ?line ok = ?FILE_MODULE:close(FD),
+ ok.
+
+create_file_slow(_FD, M, M) ->
+ ok;
+create_file_slow(FD, M, N) ->
+ ok = ?FILE_MODULE:write(FD, <<M:32/unsigned>>),
+ create_file_slow(FD, M+1, N).
+
+
+
+%% Creates a file 'Name' containing 'N' unsigned 32 bit integers
+%% from 0 to N-1.
+
+create_file(Name, N) when is_integer(N), N >= 0 ->
+ ?line {ok, FD} =
+ ?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]),
+ ?line ok = create_file(FD, 0, N),
+ ?line ok = ?FILE_MODULE:close(FD),
+ ok.
+
+create_file(_FD, M, M) ->
+ ok;
+create_file(FD, M, N) when M + 1024 =< N ->
+ create_file(FD, M, M + 1024, []),
+ create_file(FD, M + 1024, N);
+create_file(FD, M, N) ->
+ create_file(FD, M, N, []).
+
+create_file(FD, M, M, R) ->
+ ok = ?FILE_MODULE:write(FD, R);
+create_file(FD, M, N0, R) when M + 8 =< N0 ->
+ N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4,
+ N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8,
+ create_file(FD, M, N8,
+ [<<N8:32/unsigned, N7:32/unsigned,
+ N6:32/unsigned, N5:32/unsigned,
+ N4:32/unsigned, N3:32/unsigned,
+ N2:32/unsigned, N1:32/unsigned>> | R]);
+create_file(FD, M, N0, R) ->
+ N1 = N0-1,
+ create_file(FD, M, N1, [<<N1:32/unsigned>> | R]).
+
+
+
+create_bin(M, N) when is_integer(M), is_integer(N), N >= 0, M >= 0 ->
+ create_bin(M, M+N, []).
+
+create_bin(N, N, R) ->
+ list_to_binary(R);
+create_bin(M, N0, R) when M+8 =< N0 ->
+ N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4,
+ N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8,
+ create_bin(M, N8,
+ [<<N8:32/unsigned, N7:32/unsigned,
+ N6:32/unsigned, N5:32/unsigned,
+ N4:32/unsigned, N3:32/unsigned,
+ N2:32/unsigned, N1:32/unsigned>> | R]);
+create_bin(M, N0, R) ->
+ N1 = N0-1,
+ create_bin(M, N1, [<<N1:32/unsigned>> | R]).
+
+
+
+
+verify_bin(<<>>, _, 0) ->
+ true;
+verify_bin(<<>>, _, _) ->
+ false;
+verify_bin(Bin, N, Cnt) ->
+ N0 = N + 0, N1 = N + 1, N2 = N + 2, N3 = N + 3,
+ N4 = N + 4, N5 = N + 5, N6 = N + 6, N7 = N + 7,
+ case Bin of
+ <<N0:32/unsigned, N1:32/unsigned, N2:32/unsigned, N3:32/unsigned,
+ N4:32/unsigned, N5:32/unsigned, N6:32/unsigned, N7:32/unsigned,
+ B/binary>> ->
+ verify_bin(B, N+8, Cnt-8);
+ <<N:32/unsigned, B/binary>> ->
+ verify_bin(B, N+1, Cnt-1);
+ _ ->
+ false
+ end.
+
+
+
+verify_file(Name, N) when is_integer(N), N >= 0 ->
+ case ?FILE_MODULE:open(Name, [raw, read, binary]) of
+ {ok, FD} ->
+ Result = verify_file(FD, 0, 64*1024, N),
+ ok = ?FILE_MODULE:close(FD),
+ Result;
+ Error ->
+ Error
+ end.
+
+verify_file(FD, N, _, N) ->
+ case ?FILE_MODULE:read(FD, 1) of
+ eof ->
+ true;
+ {ok, _} ->
+ false
+ end;
+verify_file(FD, M, Cnt, N) when M+Cnt =< N ->
+ case ?FILE_MODULE:read(FD, 4*Cnt) of
+ {ok, Bin} ->
+ case verify_bin(Bin, M, Cnt) of
+ true ->
+ verify_file(FD, M+Cnt, Cnt, N);
+ false ->
+ false
+ end;
+ _ ->
+ false
+ end;
+verify_file(FD, M, _Cnt, N) ->
+ verify_file(FD, M, N-M, N).
+
+
+
+subbin(Bin, M, N) ->
+ <<_:M/binary, B:N/binary, _/binary>> = Bin,
+ B.
+
+
+
+write_file(Name, Data) ->
+ case ?FILE_MODULE:open(Name, [raw, write, binary]) of
+ {ok, FD} ->
+ Result = ?FILE_MODULE:write(FD, Data),
+ case {Result, ?FILE_MODULE:close(FD)} of
+ {ok, R} -> R;
+ _ -> Result
+ end;
+ Error ->
+ Error
+ end.
+
+pwrite_file(Name, Data) ->
+ case ?FILE_MODULE:open(Name, [raw, write, binary]) of
+ {ok, FD} ->
+ Result = ?FILE_MODULE:pwrite(FD, Data),
+ case {Result, ?FILE_MODULE:close(FD)} of
+ {ok, R} -> R;
+ _ -> Result
+ end;
+ Error ->
+ Error
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Read_line tests
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+read_line_testdata(PrivDir) ->
+ All0 = [{fun read_line_create0/1,"Testdata1.txt",5,10},
+ {fun read_line_create1/1,"Testdata2.txt",401,802},
+ {fun read_line_create2/1,"Testdata3.txt",1,2},
+ {fun read_line_create3/1,"Testdata4.txt",601,fail},
+ {fun read_line_create4/1,"Testdata5.txt",601,1002},
+ {fun read_line_create5/1,"Testdata6.txt",601,1202},
+ {fun read_line_create6/1,"Testdata7.txt",601,1202},
+ {fun read_line_create7/1,"Testdata8.txt",4001,8002}],
+ [ {A,filename:join([PrivDir,B]),C,D} || {A,B,C,D} <- All0 ].
+
+read_line_create_files(TestData) ->
+ [ Function(File) || {Function,File,_,_} <- TestData ].
+
+read_line_remove_files(TestData) ->
+ [ file:delete(File) || {Function,File,_,_} <- TestData ].
+
+read_line_1(suite) ->
+ [];
+read_line_1(doc) ->
+ ["read_line with prim_file"];
+read_line_1(Config) when is_list(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line All = read_line_testdata(PrivDir),
+ ?line read_line_create_files(All),
+ ?line [ begin
+ io:format("read_line_all: ~s~n",[File]),
+ {X,_} = read_line_all(File),
+ true
+ end || {_,File,X,_} <- All ],
+ ?line [ begin
+ io:format("read_line_all_alternating: ~s~n",[File]),
+ {Y,_} = read_line_all_alternating(File),
+ true
+ end || {_,File,_,Y} <- All , Y =/= fail],
+ ?line [ begin
+ io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
+ {'EXIT',_} = (catch read_line_all_alternating(File)),
+ true
+ end || {_,File,_,Y} <- All , Y =:= fail],
+ ?line read_line_remove_files(All),
+ ok.
+read_line_2(suite) ->
+ [];
+read_line_2(doc) ->
+ ["read_line with file"];
+read_line_2(Config) when is_list(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line All = read_line_testdata(PrivDir),
+ ?line read_line_create_files(All),
+ ?line [ begin
+ io:format("read_line_all: ~s~n",[File]),
+ {X,_} = read_line_all2(File),
+ true
+ end || {_,File,X,_} <- All ],
+ ?line [ begin
+ io:format("read_line_all_alternating: ~s~n",[File]),
+ {Y,_} = read_line_all_alternating2(File),
+ true
+ end || {_,File,_,Y} <- All , Y =/= fail],
+ ?line [ begin
+ io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
+ {'EXIT',_} = (catch read_line_all_alternating2(File)),
+ true
+ end || {_,File,_,Y} <- All , Y =:= fail],
+ ?line read_line_remove_files(All),
+ ok.
+read_line_3(suite) ->
+ [];
+read_line_3(doc) ->
+ ["read_line with raw file"];
+read_line_3(Config) when is_list(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line All = read_line_testdata(PrivDir),
+ ?line read_line_create_files(All),
+ ?line [ begin
+ io:format("read_line_all: ~s~n",[File]),
+ {X,_} = read_line_all3(File),
+ true
+ end || {_,File,X,_} <- All ],
+ ?line [ begin
+ io:format("read_line_all_alternating: ~s~n",[File]),
+ {Y,_} = read_line_all_alternating3(File),
+ true
+ end || {_,File,_,Y} <- All , Y =/= fail],
+ ?line [ begin
+ io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
+ {'EXIT',_} = (catch read_line_all_alternating3(File)),
+ true
+ end || {_,File,_,Y} <- All , Y =:= fail],
+ ?line read_line_remove_files(All),
+ ok.
+read_line_4(suite) ->
+ [];
+read_line_4(doc) ->
+ ["read_line with raw buffered file"];
+read_line_4(Config) when is_list(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line All = read_line_testdata(PrivDir),
+ ?line read_line_create_files(All),
+ ?line [ begin
+ io:format("read_line_all: ~s~n",[File]),
+ {X,_} = read_line_all4(File),
+ true
+ end || {_,File,X,_} <- All ],
+ ?line [ begin
+ io:format("read_line_all_alternating: ~s~n",[File]),
+ {Y,_} = read_line_all_alternating4(File),
+ true
+ end || {_,File,_,Y} <- All , Y =/= fail],
+ ?line [ begin
+ io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
+ {'EXIT',_} = (catch read_line_all_alternating4(File)),
+ true
+ end || {_,File,_,Y} <- All , Y =:= fail],
+ ?line read_line_remove_files(All),
+ ok.
+
+rl_lines() ->
+ [ <<"hej">>,<<"hopp">>,<<"i">>,<<"lingon\rskogen">>].
+
+read_line_create0(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>),
+ file:close(F).
+read_line_create1(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,100)],
+ file:close(F).
+read_line_create2(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ [ file:write(F,[R]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,200)],
+ file:write(F,<<"\r\n">>),
+ file:close(F).
+
+read_line_create3(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ file:write(F,<<"\r\n">>),
+ file:write(F,<<"\r\n">>),
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,100)],
+ file:close(F).
+
+read_line_create4(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ file:write(F,<<"\n">>),
+ file:write(F,<<"\n">>),
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,100)],
+ file:close(F).
+
+read_line_create5(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ file:write(F,<<"i\n">>),
+ file:write(F,<<"i\n">>),
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,100)],
+ file:close(F).
+
+read_line_create6(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ file:write(F,<<"i\r\n">>),
+ file:write(F,<<"i\r\n">>),
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,100)],
+ file:close(F).
+read_line_create7(Filename) ->
+ {ok,F} = file:open(Filename,[write]),
+ L = rl_lines(),
+ [ begin
+ [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
+ file:write(F,<<"Inget radslut\r">>)
+ end || _ <- lists:seq(1,1000)],
+ file:close(F).
+
+read_line_all(Filename) ->
+ {ok,F} = prim_file:open(Filename,[read,binary]),
+ X=read_rl_lines(F),
+ prim_file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+
+read_line_all2(Filename) ->
+ {ok,F} = file:open(Filename,[read,binary]),
+ X=read_rl_lines2(F),
+ file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+
+read_line_all3(Filename) ->
+ {ok,F} = file:open(Filename,[read,binary,raw]),
+ X=read_rl_lines2(F),
+ file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+read_line_all4(Filename) ->
+ {ok,F} = file:open(Filename,[read,binary,raw,{read_ahead,8192}]),
+ X=read_rl_lines2(F),
+ file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+
+read_rl_lines(F) ->
+ case prim_file:read_line(F) of
+ eof ->
+ [];
+ {error,X} ->
+ {error,X};
+ List ->
+ [List | read_rl_lines(F)]
+ end.
+
+read_rl_lines2(F) ->
+ case file:read_line(F) of
+ eof ->
+ [];
+ {error,X} ->
+ {error,X};
+ List ->
+ [List | read_rl_lines2(F)]
+ end.
+
+read_line_all_alternating(Filename) ->
+ {ok,F} = prim_file:open(Filename,[read,binary]),
+ X=read_rl_lines(F,true),
+ prim_file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+
+read_line_all_alternating2(Filename) ->
+ {ok,F} = file:open(Filename,[read,binary]),
+ X=read_rl_lines2(F,true),
+ file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+read_line_all_alternating3(Filename) ->
+ {ok,F} = file:open(Filename,[read,binary,raw]),
+ X=read_rl_lines2(F,true),
+ file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+read_line_all_alternating4(Filename) ->
+ {ok,F} = file:open(Filename,[read,binary,raw,{read_ahead,8192}]),
+ X=read_rl_lines2(F,true),
+ file:close(F),
+ Bin = list_to_binary([B || {ok,B} <- X]),
+ Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
+ "\r\n","\n",[global,{return,binary}]),
+ {length(X),Bin}.
+
+read_rl_lines(F,Alternate) ->
+ case begin
+ case Alternate of
+ true -> prim_file:read(F,1);
+ false -> prim_file:read_line(F)
+ end
+ end of
+ eof ->
+ [];
+ {error,X} ->
+ {error,X};
+ List ->
+ [List | read_rl_lines(F,not Alternate)]
+ end.
+read_rl_lines2(F,Alternate) ->
+ case begin
+ case Alternate of
+ true -> file:read(F,1);
+ false -> file:read_line(F)
+ end
+ end of
+ eof ->
+ [];
+ {error,X} ->
+ {error,X};
+ List ->
+ [List | read_rl_lines2(F,not Alternate)]
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+bytes(B, N)
+ when is_integer(B), 0 =< B, B =< 255, is_integer(N), N > 2, N band 1 == 0 ->
+ [bytes(B, N bsr 1), bytes(B, N bsr 1)];
+bytes(B, 0)
+ when is_integer(B), 0 =< B, B =< 255 ->
+ [];
+bytes(B, 2)
+ when is_integer(B), 0 =< B, B =< 255 ->
+ [B, B];
+bytes(B, N)
+ when is_integer(B), 0 =< B, B =< 255, is_integer(N), N > 0 ->
+ [B, bytes(B, N-1)].
+
+
+%% A simple loop construct.
+%%
+%% Calls 'Fun' with argument 'Start' first and then repeatedly with
+%% its returned value (state) until 'Fun' returns 'Stop'. Then
+%% the last state value that was not 'Stop' is returned.
+
+iterate(Start, Done, Fun) when is_function(Fun) ->
+ iterate(Start, Done, Fun, Start).
+
+iterate(Done, Done, _Fun, I) ->
+ I;
+iterate(I, Done, Fun, _) ->
+ iterate(Fun(I), Done, Fun, I).
+
+
+
+flush() ->
+ flush([]).
+
+flush(Msgs) ->
+ receive
+ Msg ->
+ flush([Msg | Msgs])
+ after 0 ->
+ lists:reverse(Msgs)
+ end.