aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/prim_file_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test/prim_file_SUITE.erl')
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl1810
1 files changed, 1810 insertions, 0 deletions
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
new file mode 100644
index 0000000000..860aeecbf4
--- /dev/null
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -0,0 +1,1810 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-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%
+%%
+-module(prim_file_SUITE).
+-export([all/1,
+ init/1, fini/1,
+ read_write_file/1, dirs/1, files/1]).
+-export([cur_dir_0a/1, cur_dir_0b/1,
+ cur_dir_1a/1, cur_dir_1b/1,
+ make_del_dir_a/1, make_del_dir_b/1,
+ pos/1, pos1/1, pos2/1]).
+-export([close/1,
+ delete_a/1, delete_b/1]).
+-export([open/1, open1/1, modes/1]).
+-export([file_info/1,
+ file_info_basic_file_a/1, file_info_basic_file_b/1,
+ file_info_basic_directory_a/1, file_info_basic_directory_b/1,
+ file_info_bad_a/1, file_info_bad_b/1,
+ file_info_times_a/1, file_info_times_b/1,
+ file_write_file_info_a/1, file_write_file_info_b/1]).
+-export([rename_a/1, rename_b/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([compression/1, read_not_really_compressed/1,
+ read_compressed/1, write_compressed/1,
+ compress_errors/1]).
+
+-export([links/1,
+ make_link_a/1, make_link_b/1,
+ read_link_info_for_non_link/1,
+ symlinks_a/1, symlinks_b/1,
+ list_dir_limit/1]).
+
+-include("test_server.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-define(PRIM_FILE, prim_file).
+
+%% Calls ?PRIM_FILE:F with arguments A and an optional handle H
+%% as first argument, unless the handle is [], i.e no handle.
+%% This is a macro to give the compiler and thereby
+%% the cross reference tool the possibility to interprete
+%% the call, since M, F, A (or [H | A]) can all be known at
+%% compile time.
+-define(PRIM_FILE_call(F, H, A),
+ case H of
+ [] -> apply(?PRIM_FILE, F, A);
+ _ -> apply(?PRIM_FILE, F, [H | A])
+ end).
+
+all(suite) -> {req, [kernel],
+ {conf, init,
+ [read_write_file, dirs, files,
+ delete_a, delete_b, rename_a, rename_b, errors,
+ compression, links, list_dir_limit],
+ fini}}.
+
+init(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ Priv = ?config(priv_dir, Config),
+ HasAccessTime =
+ case file: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,
+ HasAccessTime++Config;
+ _ ->
+ Config
+ end.
+
+fini(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ os:cmd("subst z: /d");
+ _ ->
+ ok
+ end,
+ 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 = ?PRIM_FILE:write_file(Name,term_to_binary(SomeTerm)),
+ ?line {ok,Bin1} = ?PRIM_FILE:read_file(Name),
+ ?line SomeTerm = binary_to_term(Bin1),
+
+ %% Try a "null" term
+ ?line NullTerm = [],
+ ?line ok = ?PRIM_FILE:write_file(Name,term_to_binary(NullTerm)),
+ ?line {ok,Bin2} = ?PRIM_FILE: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 = ?PRIM_FILE:write_file(Name,term_to_binary(ComplTerm)),
+ ?line {ok,Bin3} = ?PRIM_FILE: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} = ?PRIM_FILE:read_file(Name2),
+ ?line {error, enoent} = ?PRIM_FILE:read_file(""),
+
+ % Try writing to a bad filename
+ ?line {error, enoent} =
+ ?PRIM_FILE:write_file("",term_to_binary(NullTerm)),
+
+ % Try writing something else than a binary
+ ?line {error, badarg} = ?PRIM_FILE:write_file(Name,{1,2,3}),
+ ?line {error, badarg} = ?PRIM_FILE:write_file(Name,self()),
+
+ %% Some non-term binaries
+ ?line ok = ?PRIM_FILE:write_file(Name,[]),
+ ?line {ok,Bin4} = ?PRIM_FILE:read_file(Name),
+ ?line 0 = byte_size(Bin4),
+
+ ?line ok = ?PRIM_FILE:write_file(Name,[Bin1,[],[[Bin2]]]),
+ ?line {ok,Bin5} = ?PRIM_FILE:read_file(Name),
+ ?line {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dirs(suite) -> [make_del_dir_a, make_del_dir_b,
+ cur_dir_0a, cur_dir_0b,
+ cur_dir_1a, cur_dir_1b].
+
+make_del_dir_a(suite) -> [];
+make_del_dir_a(doc) -> [];
+make_del_dir_a(Config) when is_list(Config) ->
+ make_del_dir(Config, [], "_a").
+
+make_del_dir_b(suite) -> [];
+make_del_dir_b(doc) -> [];
+make_del_dir_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = make_del_dir(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ %% Just to make sure the state of the server makes a difference
+ ?line {error, einval} = ?PRIM_FILE_call(get_cwd, Handle, []),
+ Result.
+
+make_del_dir(Config, Handle, Suffix) ->
+ ?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"++Suffix),
+ ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
+ ?line {error, eexist} = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
+ ?line ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]),
+ ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [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} = ?PRIM_FILE_call(make_dir, Handle, [NewDir2]),
+ %% a nameless directory
+ ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [""]),
+ %% a directory with illegal name
+ ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, ['mk-dir']),
+
+ %% a directory with illegal name, even if it's a (bad) list
+ ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, [[1,2,3,{}]]),
+
+ %% Maybe this isn't an error, exactly, but worth mentioning anyway:
+ %% ok = ?PRIM_FILE: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} = ?PRIM_FILE_call(del_dir, Handle, [".."]),
+ ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]),
+ ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+cur_dir_0a(suite) -> [];
+cur_dir_0a(doc) -> [];
+cur_dir_0a(Config) when is_list(Config) ->
+ cur_dir_0(Config, []).
+
+cur_dir_0b(suite) -> [];
+cur_dir_0b(doc) -> [];
+cur_dir_0b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = cur_dir_0(Config, Handle),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+cur_dir_0(Config, Handle) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ %% Find out the current dir, and cd to it ;-)
+ ?line {ok,BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
+ ?line Dir1 = BaseDir ++ "", %% Check that it's a string
+ ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ ?line DirName = atom_to_list(?MODULE) ++
+ case Handle of
+ [] ->
+ "_curdir";
+ _ ->
+ "_curdir_h"
+ end,
+
+ %% Make a new dir, and cd to that
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir, DirName),
+ ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
+ ?line io:format("cd to ~s",[NewDir]),
+ ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+
+ %% Create a file in the new current directory, and check that it
+ %% really is created there
+ ?line UncommonName = "uncommon.fil",
+ ?line {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]),
+ ?line ok = ?PRIM_FILE:close(Fd),
+ ?line {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ ?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}, {error, eexist},
+ ?PRIM_FILE_call(del_dir, Handle, [NewDir])),
+ ?line ?PRIM_FILE_call(delete, Handle, [UncommonName]),
+ ?line {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ ?line io:format("cd back to ~s",[Dir1]),
+ ?line ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]),
+ ?line {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+ ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ ?line io:format("cd back to ~s",[Dir1]),
+ ?line {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ ?line false = lists:member(UncommonName,OldDirFiles),
+
+ %% Try doing some bad things
+ ?line {error, badarg} =
+ ?PRIM_FILE_call(set_cwd, Handle, [{foo,bar}]),
+ ?line {error, enoent} =
+ ?PRIM_FILE_call(set_cwd, Handle, [""]),
+ ?line {error, enoent} =
+ ?PRIM_FILE_call(set_cwd, Handle, [".......a......"]),
+ ?line {ok,BaseDir} =
+ ?PRIM_FILE_call(get_cwd, Handle, []), %% 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} = ?PRIM_FILE_call(get_cwd, Handle, []),
+ ?line false = lists:member($\\, BaseDir),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Tests ?PRIM_FILE:get_cwd/1.
+
+cur_dir_1a(suite) -> [];
+cur_dir_1a(doc) -> [];
+cur_dir_1a(Config) when is_list(Config) ->
+ cur_dir_1(Config, []).
+
+cur_dir_1b(suite) -> [];
+cur_dir_1b(doc) -> [];
+cur_dir_1b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = cur_dir_1(Config, Handle),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+cur_dir_1(Config, Handle) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+
+ ?line case os:type() of
+ {unix, _} ->
+ ?line {error, enotsup} =
+ ?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
+ vxworks ->
+ ?line {error, enotsup} =
+ ?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
+ {win32, _} ->
+ win_cur_dir_1(Config, Handle)
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+win_cur_dir_1(_Config, Handle) ->
+ ?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
+
+ %% Get the drive letter from the current directory,
+ %% and try to get current directory for that drive.
+
+ ?line [Drive, $:|_] = BaseDir,
+ ?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, [[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. Too bad.
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+files(suite) -> [open,pos,file_info,truncate,sync].
+
+open(suite) -> [open1,modes,close,access,read_write,
+ pread_write,append].
+
+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 = ?PRIM_FILE:make_dir(NewDir),
+ ?line Name = filename:join(NewDir, "foo1.fil"),
+ ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [read, write]),
+ ?line {ok,Fd2} = ?PRIM_FILE:open(Name, [read]),
+ ?line Str = "{a,tuple}.\n",
+ ?line Length = length(Str),
+ ?line ?PRIM_FILE:write(Fd1,Str),
+ ?line {ok,0} = ?PRIM_FILE:position(Fd1,bof),
+ ?line {ok, Str} = ?PRIM_FILE:read(Fd1,Length),
+ ?line {ok, Str} = ?PRIM_FILE:read(Fd2,Length),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+ ?line {ok,0} = ?PRIM_FILE:position(Fd1,bof),
+ ?line ok = ?PRIM_FILE:truncate(Fd1),
+ ?line eof = ?PRIM_FILE:read(Fd1,Length),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+ ?line {ok,Fd3} = ?PRIM_FILE:open(Name, [read]),
+ ?line eof = ?PRIM_FILE:read(Fd3,Length),
+ ?line ok = ?PRIM_FILE:close(Fd3),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Tests all open modes.
+
+modes(suite) -> [];
+modes(doc) -> [];
+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)
+ ++"_open_modes"),
+ ?line ok = ?PRIM_FILE:make_dir(NewDir),
+ ?line Name1 = filename:join(NewDir, "foo1.fil"),
+ ?line Marker = "hello, world",
+ ?line Length = length(Marker),
+
+ %% write
+ ?line {ok, Fd1} = ?PRIM_FILE:open(Name1, [write]),
+ ?line ok = ?PRIM_FILE:write(Fd1, Marker),
+ ?line ok = ?PRIM_FILE:write(Fd1, ".\n"),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+
+ %% read
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name1, [read]),
+ ?line {ok, Marker} = ?PRIM_FILE:read(Fd2, Length),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+
+ %% read and write
+ ?line {ok, Fd3} = ?PRIM_FILE:open(Name1, [read, write]),
+ ?line {ok, Marker} = ?PRIM_FILE:read(Fd3, Length),
+ ?line ok = ?PRIM_FILE:write(Fd3, Marker),
+ ?line ok = ?PRIM_FILE:close(Fd3),
+
+ %% read by default
+ ?line {ok, Fd4} = ?PRIM_FILE:open(Name1, []),
+ ?line {ok, Marker} = ?PRIM_FILE:read(Fd4, Length),
+ ?line ok = ?PRIM_FILE:close(Fd4),
+
+ %% read and binary
+ ?line BinaryMarker = list_to_binary(Marker),
+ ?line {ok, Fd5} = ?PRIM_FILE:open(Name1, [read, binary]),
+ ?line {ok, BinaryMarker} = ?PRIM_FILE:read(Fd5, Length),
+ ?line ok = ?PRIM_FILE:close(Fd5),
+
+ ?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} = ?PRIM_FILE: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 ?PRIM_FILE:open just returned a Pid, and not everyone
+ %% really checked what they got.
+ ?line {'EXIT',_Msg} = (catch ok = ?PRIM_FILE:close({ok,Fd1})),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+
+ %% Try closing one more time
+ ?line Val = ?PRIM_FILE:close(Fd1),
+ ?line io:format("Second close gave: ~p", [Val]),
+
+ ?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} = ?PRIM_FILE:open(Name, [write]),
+ ?line ?PRIM_FILE:write(Fd1,Str),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+ %% Check that we can't write when in read only mode
+ ?line {ok,Fd2} = ?PRIM_FILE:open(Name, [read]),
+ ?line case catch ?PRIM_FILE:write(Fd2,"XXXX") of
+ ok ->
+ test_server:fail({access,write});
+ _ ->
+ ok
+ end,
+ ?line ok = ?PRIM_FILE:close(Fd2),
+ ?line {ok, Fd3} = ?PRIM_FILE:open(Name, [read]),
+ ?line {ok, Str} = ?PRIM_FILE:read(Fd3,length(Str)),
+ ?line ok = ?PRIM_FILE:close(Fd3),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Tests ?PRIM_FILE:read/2 and ?PRIM_FILE: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 = ?PRIM_FILE:make_dir(NewDir),
+
+ %% Raw file.
+ ?line Name = filename:join(NewDir, "raw.fil"),
+ ?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
+ ?line read_write_test(Fd),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+read_write_test(File) ->
+ ?line Marker = "hello, world",
+ ?line ok = ?PRIM_FILE:write(File, Marker),
+ ?line {ok, 0} = ?PRIM_FILE:position(File, 0),
+ ?line {ok, Marker} = ?PRIM_FILE:read(File, 100),
+ ?line eof = ?PRIM_FILE:read(File, 100),
+ ?line ok = ?PRIM_FILE:close(File),
+ ok.
+
+
+%% Tests ?PRIM_FILE:pread/2 and ?PRIM_FILE: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 = ?PRIM_FILE:make_dir(NewDir),
+
+ %% Raw file.
+ ?line Name = filename:join(NewDir, "raw.fil"),
+ ?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
+ ?line pread_write_test(Fd),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+pread_write_test(File) ->
+ ?line Marker = "hello, world",
+ ?line Len = length(Marker),
+ ?line ok = ?PRIM_FILE:write(File, Marker),
+ ?line {ok, Marker} = ?PRIM_FILE:pread(File, 0, 100),
+ ?line eof = ?PRIM_FILE:pread(File, 100, 1),
+ ?line ok = ?PRIM_FILE:pwrite(File, Len, Marker),
+ ?line {ok, Marker} = ?PRIM_FILE:pread(File, Len, 100),
+ ?line eof = ?PRIM_FILE:pread(File, 100, 1),
+ ?line MM = Marker ++ Marker,
+ ?line {ok, MM} = ?PRIM_FILE:pread(File, 0, 100),
+ ?line ok = ?PRIM_FILE:close(File),
+ 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 = ?PRIM_FILE: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} = ?PRIM_FILE:open(Name1, [write]),
+ ?line ok = ?PRIM_FILE:write(Fd1, First),
+ ?line ok = ?PRIM_FILE:write(Fd1, Second),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+
+ %% Open it a again and a append a line to it.
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name1, [append]),
+ ?line ok = ?PRIM_FILE:write(Fd2, Third),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+
+ %% Read it back and verify.
+ ?line Expected = list_to_binary([First, Second, Third]),
+ ?line {ok, Expected} = ?PRIM_FILE:read_file(Name1),
+
+ ?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} = ?PRIM_FILE:open(Name, [write]),
+ ?line ?PRIM_FILE:write(Fd1,"ABCDEFGH"),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]),
+
+ %% Start pos is first char
+ ?line io:format("Relative positions"),
+ ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,1}),
+ ?line {ok, "C"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd2,{cur,-3}),
+ ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1),
+ %% Backwards from first char should be an error
+ ?line {ok,0} = ?PRIM_FILE:position(Fd2,{cur,-1}),
+ ?line {error, einval} = ?PRIM_FILE:position(Fd2,{cur,-1}),
+ %% Reset position and move again
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd2,0),
+ ?line {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,2}),
+ ?line {ok, "C"} = ?PRIM_FILE:read(Fd2,1),
+ %% Go a lot forwards
+ ?line {ok, 13} = ?PRIM_FILE:position(Fd2,{cur,10}),
+ ?line eof = ?PRIM_FILE:read(Fd2,1),
+
+ %% Try some fixed positions
+ ?line io:format("Fixed positions"),
+ ?line {ok, 8} = ?PRIM_FILE:position(Fd2,8),
+ ?line eof = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 8} = ?PRIM_FILE:position(Fd2,cur),
+ ?line eof = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 7} = ?PRIM_FILE:position(Fd2,7),
+ ?line {ok, "H"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd2,0),
+ ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 3} = ?PRIM_FILE:position(Fd2,3),
+ ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 12} = ?PRIM_FILE:position(Fd2,12),
+ ?line eof = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 3} = ?PRIM_FILE:position(Fd2,3),
+ ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1),
+ %% Try the {bof,X} notation
+ ?line {ok, 3} = ?PRIM_FILE:position(Fd2,{bof,3}),
+ ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1),
+
+ %% Try eof positions
+ ?line io:format("EOF positions"),
+ ?line {ok, 8} = ?PRIM_FILE:position(Fd2,{eof,0}),
+ ?line eof = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 7} = ?PRIM_FILE:position(Fd2,{eof,-1}),
+ ?line {ok, "H"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd2,{eof,-8}),
+ ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1),
+ ?line {error, einval} = ?PRIM_FILE:position(Fd2,{eof,-9}),
+ ?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} = ?PRIM_FILE:open(Name, [write]),
+ ?line ?PRIM_FILE:write(Fd1,"ABCDEFGH"),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]),
+ ?line {error, einval} = ?PRIM_FILE:position(Fd2,-1),
+
+ %% Make sure that we still can search after an error.
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd2, 0),
+ ?line {ok, 3} = ?PRIM_FILE:position(Fd2, {bof,3}),
+ ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1),
+
+ ?line io:format("DONE"),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+file_info(suite) -> [file_info_basic_file_a, file_info_basic_file_b,
+ file_info_basic_directory_a,
+ file_info_basic_directory_b,
+ file_info_bad_a, file_info_bad_b,
+ file_info_times_a, file_info_times_b,
+ file_write_file_info_a, file_write_file_info_b].
+
+file_info_basic_file_a(suite) -> [];
+file_info_basic_file_a(doc) -> [];
+file_info_basic_file_a(Config) when is_list(Config) ->
+ file_info_basic_file(Config, [], "_a").
+
+file_info_basic_file_b(suite) -> [];
+file_info_basic_file_b(doc) -> [];
+file_info_basic_file_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = file_info_basic_file(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+file_info_basic_file(Config, Handle, Suffix) ->
+ ?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"++Suffix++".fil"),
+ ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [write]),
+ ?line ?PRIM_FILE:write(Fd1, "foo bar"),
+ ?line ok = ?PRIM_FILE: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, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]),
+ ?line #file_info{size = Size, type = Type, access = Access,
+ atime = AccessTime, mtime = ModifyTime} =
+ FileInfo,
+ ?line io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
+ ?line Size = 7,
+ ?line Type = regular,
+ ?line Access = read_write,
+ ?line true = abs(time_dist(filter_atime(AccessTime, Config),
+ filter_atime(ModifyTime,
+ Config))) < 2,
+ ?line {AD, AT} = AccessTime,
+ ?line all_integers(tuple_to_list(AD) ++ tuple_to_list(AT)),
+ ?line {MD, MT} = ModifyTime,
+ ?line all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+file_info_basic_directory_a(suite) -> [];
+file_info_basic_directory_a(doc) -> [];
+file_info_basic_directory_a(Config) when is_list(Config) ->
+ file_info_basic_directory(Config, []).
+
+file_info_basic_directory_b(suite) -> [];
+file_info_basic_directory_b(doc) -> [];
+file_info_basic_directory_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = file_info_basic_directory(Config, Handle),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+file_info_basic_directory(Config, Handle) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?PRIM_FILE:read_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, Handle),
+
+ %% Note that on Windows file systems, "/" or "c:/" are *NOT* directories.
+ %% Therefore, test that ?PRIM_FILE:read_file_info/1 behaves
+ %% as if they were directories.
+ ?line case os:type() of
+ {win32, _} ->
+ ?line test_directory("/", read_write, Handle),
+ ?line test_directory("c:/", read_write, Handle),
+ ?line test_directory("c:\\", read_write, Handle);
+ {unix, _} ->
+ ?line test_directory("/", read, Handle);
+ vxworks ->
+ %% Check is just done for owner
+ ?line test_directory("/", read_write, Handle)
+ end,
+ ?line test_server:timetrap_cancel(Dog).
+
+test_directory(Name, ExpectedAccess, Handle) ->
+ ?line {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]),
+ ?line #file_info{size = Size, type = Type, access = Access,
+ atime = AccessTime, mtime = ModifyTime} =
+ FileInfo,
+ ?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 {AD, AT} = AccessTime,
+ ?line all_integers(tuple_to_list(AD) ++ tuple_to_list(AT)),
+ ?line {MD, MT} = ModifyTime,
+ ?line all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)),
+ ok.
+
+all_integers([Int|Rest]) when is_integer(Int) ->
+ ?line all_integers(Rest);
+all_integers([]) ->
+ ok.
+
+%% Try something nonexistent.
+
+file_info_bad_a(suite) -> [];
+file_info_bad_a(doc) -> [];
+file_info_bad_a(Config) when is_list(Config) ->
+ file_info_bad(Config, []).
+
+file_info_bad_b(suite) -> [];
+file_info_bad_b(doc) -> [];
+file_info_bad_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = file_info_bad(Config, Handle),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+file_info_bad(Config, Handle) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = filename:join([?config(priv_dir, Config)]),
+ ?line {error, enoent} =
+ ?PRIM_FILE_call(
+ read_file_info, Handle,
+ [filename:join(RootDir,
+ atom_to_list(?MODULE)++"_nonexistent")]),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Test that the file times behave as they should.
+
+file_info_times_a(suite) -> [];
+file_info_times_a(doc) -> [];
+file_info_times_a(Config) when is_list(Config) ->
+ file_info_times(Config, [], "_a").
+
+file_info_times_b(suite) -> [];
+file_info_times_b(doc) -> [];
+file_info_times_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = file_info_times(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+file_info_times(Config, Handle, Suffix) ->
+ ?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, Handle, Suffix) end),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+file_info_int(Config, Handle, Suffix) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?PRIM_FILE:read_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"++Suffix++".fil"),
+ ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [write]),
+ ?line ?PRIM_FILE:write(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}} =
+ ?PRIM_FILE_call(read_file_info, Handle, [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 = ?PRIM_FILE:close(Fd1),
+ ?line {ok, #file_info{size = Size, type = regular, access = Access,
+ atime = AccTime2, mtime = ModTime2}} =
+ ?PRIM_FILE_call(read_file_info, Handle, [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}} =
+ ?PRIM_FILE_call(read_file_info, Handle, [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]),
+ ok.
+
+%% Filter access times, to cope 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_a(suite) -> [];
+file_write_file_info_a(doc) -> [];
+file_write_file_info_a(Config) when is_list(Config) ->
+ file_write_file_info(Config, [], "_a").
+
+file_write_file_info_b(suite) -> [];
+file_write_file_info_b(doc) -> [];
+file_write_file_info_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = file_write_file_info(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+file_write_file_info(Config, Handle, Suffix) ->
+ ?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 Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_write_file_info_ro"++Suffix),
+ ?line ok = ?PRIM_FILE:write_file(Name, "hello"),
+ ?line Time = {{1997, 01, 02}, {12, 35, 42}},
+ ?line Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time},
+ ?line ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, Info]),
+
+ %% Read back the times.
+
+ ?line {ok, ActualInfo} =
+ ?PRIM_FILE_call(read_file_info, Handle, [Name]),
+ ?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} = ?PRIM_FILE:write_file(Name, "hello again"),
+
+ %% Make the file writable again.
+
+ ?line ?PRIM_FILE_call(write_file_info, Handle,
+ [Name, #file_info{mode=8#600}]),
+ ?line ok = ?PRIM_FILE:write_file(Name, "hello again"),
+
+ %% And unwritable.
+ ?line ?PRIM_FILE_call(write_file_info, Handle,
+ [Name, #file_info{mode=8#400}]),
+ ?line {error, eacces} = ?PRIM_FILE:write_file(Name, "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 = ?PRIM_FILE_call(write_file_info, Handle, [Name, NewInfo]),
+ ?line {ok, ActualInfo2} =
+ ?PRIM_FILE_call(read_file_info, Handle, [Name]),
+ ?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} = ?PRIM_FILE:write_file(Name, "hello again"),
+
+ %% Make the file writeable again, so that we can remove the
+ %% test suites ... :-)
+ ?line ?PRIM_FILE_call(write_file_info, Handle,
+ [Name, #file_info{mode=8#600}]),
+ ?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).
+
+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 = ?PRIM_FILE:write_file(Name, MyData),
+
+ %% Truncate the file to 10 characters.
+ ?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
+ ?line {ok, 10} = ?PRIM_FILE:position(Fd, 10),
+ ?line ok = ?PRIM_FILE:truncate(Fd),
+ ?line ok = ?PRIM_FILE:close(Fd),
+
+ %% Read back the file and check that it has been truncated.
+ ?line Expected = list_to_binary("0123456789"),
+ ?line {ok, Expected} = ?PRIM_FILE:read_file(Name),
+
+ %% Open the file read only and verify that it is not possible to
+ %% truncate it, OTP-1960
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]),
+ ?line {ok, 5} = ?PRIM_FILE:position(Fd2, 5),
+ ?line {error, _} = ?PRIM_FILE:truncate(Fd2),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+sync(suite) -> [];
+sync(doc) -> "Tests that ?PRIM_FILE: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} = ?PRIM_FILE:open(Sync, [write]),
+ ?line ok = ?PRIM_FILE:sync(Fd),
+ ?line ok = ?PRIM_FILE:close(Fd),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+delete_a(suite) -> [];
+delete_a(doc) -> [];
+delete_a(Config) when is_list(Config) ->
+ delete(Config, [], "_a").
+
+delete_b(suite) -> [];
+delete_b(doc) -> [];
+delete_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = delete(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+delete(Config, Handle, Suffix) ->
+ ?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"++Suffix++".fil"),
+ ?line {ok, Fd1} = ?PRIM_FILE:open(Name, [write]),
+ ?line ?PRIM_FILE:write(Fd1,"ok.\n"),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+ %% Check that the file is readable
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+ ?line ok = ?PRIM_FILE_call(delete, Handle, [Name]),
+ %% Check that the file is not readable anymore
+ ?line {error, _} = ?PRIM_FILE:open(Name, [read]),
+ %% Try deleting a nonexistent file
+ ?line {error, enoent} = ?PRIM_FILE_call(delete, Handle, [Name]),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+rename_a(suite) ->[];
+rename_a(doc) ->[];
+rename_a(Config) when is_list(Config) ->
+ rename(Config, [], "_a").
+
+rename_b(suite) ->[];
+rename_b(doc) ->[];
+rename_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = rename(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+rename(Config, Handle, Suffix) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line FileName1 = atom_to_list(?MODULE)++"_rename"++Suffix++".fil",
+ ?line FileName2 = atom_to_list(?MODULE)++"_rename"++Suffix++".ful",
+ ?line Name1 = filename:join(RootDir, FileName1),
+ ?line Name2 = filename:join(RootDir, FileName2),
+ ?line {ok,Fd1} = ?PRIM_FILE:open(Name1, [write]),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+ %% Rename, and check that it really changed name
+ ?line ok = ?PRIM_FILE_call(rename, Handle, [Name1, Name2]),
+ ?line {error, _} = ?PRIM_FILE:open(Name1, [read]),
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Name2, [read]),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+ %% Try renaming something to itself
+ ?line ok = ?PRIM_FILE_call(rename, Handle, [Name2, Name2]),
+ %% Try renaming something that doesn't exist
+ ?line {error, enoent} =
+ ?PRIM_FILE_call(rename, Handle, [Name1, Name2]),
+ %% Try renaming to something else than a string
+ ?line {error, badarg} =
+ ?PRIM_FILE_call(rename, Handle, [Name1, foobar]),
+
+ %% Move between directories
+ ?line DirName1 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_rename_dir"++Suffix),
+ ?line DirName2 = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_second_rename_dir"++Suffix),
+ ?line Name1foo = filename:join(DirName1, "foo.fil"),
+ ?line Name2foo = filename:join(DirName2, "foo.fil"),
+ ?line Name2bar = filename:join(DirName2, "bar.dir"),
+ ?line ok = ?PRIM_FILE:make_dir(DirName1),
+ %% The name has to include the full file name, path is not enough
+ ?line expect(
+ {error, eexist}, {error, eisdir},
+ ?PRIM_FILE_call(rename, Handle, [Name2, DirName1])),
+ ?line ok =
+ ?PRIM_FILE_call(rename, Handle, [Name2, Name1foo]),
+ %% Now rename the directory
+ ?line ok = ?PRIM_FILE_call(rename, Handle, [DirName1, DirName2]),
+ %% And check that the file is there now
+ ?line {ok,Fd3} = ?PRIM_FILE:open(Name2foo, [read]),
+ ?line ok = ?PRIM_FILE:close(Fd3),
+ %% Try some dirty things now: move the directory into itself
+ ?line {error, Msg1} =
+ ?PRIM_FILE_call(rename, Handle, [DirName2, Name2bar]),
+ ?line io:format("Errmsg1: ~p",[Msg1]),
+ %% move dir into a file in itself
+ ?line {error, Msg2} =
+ ?PRIM_FILE_call(rename, Handle, [DirName2, Name2foo]),
+ ?line io:format("Errmsg2: ~p",[Msg2]),
+
+ ?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 = ?PRIM_FILE:make_dir(Base),
+
+ %% Delete a non-existing file.
+ ?line {error, enoent} =
+ ?PRIM_FILE:delete(filename:join(Base, "non_existing")),
+
+ %% Delete a directory.
+ ?line {error, eperm} = ?PRIM_FILE:delete(Base),
+
+ %% Use a path-name with a non-directory component.
+ ?line Afile = filename:join(Base, "a_file"),
+ ?line ok = ?PRIM_FILE:write_file(Afile, "hello\n"),
+ ?line {error, E} =
+ expect(
+ {error, enotdir}, {error, enoent},
+ ?PRIM_FILE:delete(filename:join(Afile, "another_file"))),
+ ?line io:format("Result: ~p~n", [E]),
+
+ %% No permission.
+ ?line case os:type() of
+ {unix, _} ->
+ ?line ?PRIM_FILE:write_file_info(
+ Base, #file_info {mode=0}),
+ ?line {error, eacces} = ?PRIM_FILE:delete(Afile),
+ ?line ?PRIM_FILE:write_file_info(
+ Base, #file_info {mode=8#600});
+ {win32, _} ->
+ %% Remove a character device.
+ ?line {error, eacces} = ?PRIM_FILE:delete("nul");
+ vxworks ->
+ ok
+ end,
+
+ ?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 = ?PRIM_FILE:make_dir(Base),
+
+ %% Create an empty directory.
+ ?line EmptyDir = filename:join(Base, "empty_dir"),
+ ?line ok = ?PRIM_FILE:make_dir(EmptyDir),
+
+ %% Create a non-empty directory.
+ ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
+ ?line ok = ?PRIM_FILE:make_dir(NonEmptyDir),
+ ?line ok = ?PRIM_FILE:write_file(
+ filename:join(NonEmptyDir, "a_file"),
+ "hello\n"),
+
+ %% Create another non-empty directory.
+ ?line ADirectory = filename:join(Base, "a_directory"),
+ ?line ok = ?PRIM_FILE:make_dir(ADirectory),
+ ?line ok = ?PRIM_FILE:write_file(
+ filename:join(ADirectory, "a_file"),
+ "howdy\n\n"),
+
+ %% Create a data file.
+ ?line File = filename:join(Base, "just_a_file"),
+ ?line ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"),
+
+ %% Move an existing directory to a non-empty directory.
+ ?line {error, eexist} =
+ ?PRIM_FILE:rename(ADirectory, NonEmptyDir),
+
+ %% Move a root directory.
+ ?line {error, einval} = ?PRIM_FILE:rename("/", "arne"),
+
+ %% Move Base into Base/new_name.
+ ?line {error, einval} =
+ ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")),
+
+ %% Overwrite a directory with a file.
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, eisdir},
+ ?PRIM_FILE:rename(File, EmptyDir)),
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, eisdir},
+ ?PRIM_FILE:rename(File, NonEmptyDir)),
+
+ %% Move a non-existing file.
+ ?line NonExistingFile = filename:join(
+ Base, "non_existing_file"),
+ ?line {error, enoent} =
+ ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir),
+
+ %% Overwrite a file with a directory.
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, enotdir},
+ ?PRIM_FILE: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 ?PRIM_FILE: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 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 = ?PRIM_FILE:make_dir(Base),
+
+ %% A component of the path does not exist.
+ ?line {error, enoent} =
+ ?PRIM_FILE: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 = ?PRIM_FILE:write_file(Afile, "hello\n"),
+ ?line case ?PRIM_FILE: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 ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}),
+ ?line {error, eacces} =
+ ?PRIM_FILE:make_dir(filename:join(Base, "xxxx")),
+ ?line
+ ?PRIM_FILE: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 = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_del_dir"),
+ ?line io:format("Base: ~p", [Base]),
+ ?line ok = ?PRIM_FILE:make_dir(Base),
+
+ %% Delete a non-existent directory.
+ ?line {error, enoent} =
+ ?PRIM_FILE: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 = ?PRIM_FILE:write_file(Afile, "hello\n"),
+ ?line {error, E1} =
+ expect({error, enotdir}, {error, enoent},
+ ?PRIM_FILE:del_dir(
+ filename:join(Afile, "another_directory"))),
+ ?line io:format("Result: ~p", [E1]),
+
+ %% Delete a non-empty directory.
+ %% Delete a non-empty directory.
+ ?line {error, E2} =
+ expect({error, enotempty}, {error, eexist}, {error, eacces},
+ ?PRIM_FILE: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},
+ ?PRIM_FILE:del_dir(".")),
+ ?line io:format("Result: ~p", [E3]),
+
+ %% No permission.
+ case os:type() of
+ {unix, _} ->
+ ?line ADirectory = filename:join(Base, "no_perm"),
+ ?line ok = ?PRIM_FILE:make_dir(ADirectory),
+ ?line ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}),
+ ?line {error, eacces} = ?PRIM_FILE:del_dir(ADirectory),
+ ?line ?PRIM_FILE:write_file_info(
+ Base, #file_info {mode=8#600});
+ {win32, _} ->
+ ok;
+ vxworks ->
+ ok
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+compression(suite) -> [read_compressed, read_not_really_compressed,
+ write_compressed, compress_errors].
+
+%% Trying reading and positioning from a compressed file.
+
+read_compressed(suite) -> [];
+read_compressed(doc) -> [];
+read_compressed(Config) when is_list(Config) ->
+ ?line Data = ?config(data_dir, Config),
+ ?line Real = filename:join(Data, "realmen.html.gz"),
+ ?line {ok, Fd} = ?PRIM_FILE:open(Real, [read, compressed]),
+ ?line try_read_file(Fd).
+
+%% Trying reading and positioning from an uncompressed file,
+%% but with the compressed flag given.
+
+read_not_really_compressed(suite) -> [];
+read_not_really_compressed(doc) -> [];
+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} = ?PRIM_FILE:read_file(Real),
+ ?line RealData = remove_crs(binary_to_list(RealDataBin), []),
+ ?line ok = ?PRIM_FILE:write_file(RealPriv, RealData),
+ ?line {ok, Fd} = ?PRIM_FILE:open(RealPriv, [read, compressed]),
+ ?line try_read_file(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(Fd) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+
+ %% Seek to the current position (nothing should happen).
+
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd, 0),
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd, {cur, 0}),
+
+ %% Read a few lines from a compressed file.
+
+ ?line ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n",
+ ?line {ok, ShouldBe} = ?PRIM_FILE:read(Fd, length(ShouldBe)),
+
+ %% Now seek forward.
+
+ ?line {ok, 381} = ?PRIM_FILE:position(Fd, 381),
+ ?line Back = "Back in the good old days -- the \"Golden Era\" " ++
+ "of computers, it was\n",
+ ?line {ok, Back} = ?PRIM_FILE:read(Fd, length(Back)),
+
+ %% Try to search forward relative to the current position.
+
+ ?line {ok, CurPos} = ?PRIM_FILE:position(Fd, {cur, 0}),
+ ?line RealPos = 4273,
+ ?line {ok, RealPos} = ?PRIM_FILE:position(Fd, {cur, RealPos-CurPos}),
+ ?line RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n",
+ ?line {ok, RealProg} = ?PRIM_FILE:read(Fd, length(RealProg)),
+
+ %% Seek backward.
+
+ ?line AfterTitle = length("<TITLE>"),
+ ?line {ok, AfterTitle} = ?PRIM_FILE:position(Fd, AfterTitle),
+ ?line Title = "Real Programmers Don't Use PASCAL</TITLE>\n",
+ ?line {ok, Title} = ?PRIM_FILE:read(Fd, length(Title)),
+
+ %% Done.
+
+ ?line ?PRIM_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} = ?PRIM_FILE:open(MyFile, [write, compressed]),
+ ?line {ok, 0} = ?PRIM_FILE:position(Fd, 0),
+ ?line Prefix = "hello\n",
+ ?line End = "end\n",
+ ?line ok = ?PRIM_FILE:write(Fd, Prefix),
+ ?line {ok, 143} = ?PRIM_FILE:position(Fd, 143),
+ ?line ok = ?PRIM_FILE:write(Fd, End),
+ ?line ok = ?PRIM_FILE:close(Fd),
+
+ %% Read the file and verify the contents.
+
+ ?line {ok, Fd1} = ?PRIM_FILE:open(MyFile, [read, compressed]),
+ ?line {ok, Prefix} = ?PRIM_FILE:read(Fd1, length(Prefix)),
+ ?line Second = lists:duplicate(143-length(Prefix), 0) ++ End,
+ ?line {ok, Second} = ?PRIM_FILE:read(Fd1, length(Second)),
+ ?line ok = ?PRIM_FILE:close(Fd1),
+
+ %% Ensure that the file is compressed.
+
+ TotalSize = 143 + length(End),
+ case ?PRIM_FILE: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} = ?PRIM_FILE:open(MyFile, [write, compressed]),
+ ?line NewString = "aaaaaaaaaaa",
+ ?line ok = ?PRIM_FILE:write(Fd2, NewString),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+ ?line {ok, Fd3} = ?PRIM_FILE:open(MyFile, [read, compressed]),
+ ?line {ok, NewString} = ?PRIM_FILE:read(Fd3, 1024),
+ ?line ok = ?PRIM_FILE:close(Fd3),
+
+ %% Done.
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+compress_errors(suite) -> [];
+compress_errors(doc) -> [];
+compress_errors(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line Data = ?config(data_dir, Config),
+ ?line {error, enoent} = ?PRIM_FILE:open("non_existing__",
+ [compressed, read]),
+ ?line {error, einval} = ?PRIM_FILE:open("non_existing__",
+ [compressed, read, write]),
+
+ %% Read a corrupted .gz file.
+
+ ?line Corrupted = filename:join(Data, "corrupted.gz"),
+ ?line {ok, Fd} = ?PRIM_FILE:open(Corrupted, [read, compressed]),
+ ?line {error, eio} = ?PRIM_FILE:read(Fd, 100),
+ ?line ?PRIM_FILE:close(Fd),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+links(doc) -> "Test the link functions.";
+links(suite) ->
+ [make_link_a, make_link_b,
+ read_link_info_for_non_link,
+ symlinks_a, symlinks_b].
+
+make_link_a(doc) -> "Test creating a hard link.";
+make_link_a(suite) -> [];
+make_link_a(Config) when is_list(Config) ->
+ make_link(Config, [], "_a").
+
+make_link_b(doc) -> "Test creating a hard link.";
+make_link_b(suite) -> [];
+make_link_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = make_link(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+make_link(Config, Handle, Suffix) ->
+ ?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"++Suffix),
+ ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
+
+ ?line Name = filename:join(NewDir, "a_file"),
+ ?line ok = ?PRIM_FILE:write_file(Name, "some contents\n"),
+
+ ?line Alias = filename:join(NewDir, "an_alias"),
+ ?line Result =
+ case ?PRIM_FILE_call(make_link, Handle, [Name, Alias]) of
+ {error, enotsup} ->
+ {skipped, "Links not supported on this platform"};
+ ok ->
+ %% Note: We take the opportunity to test
+ %% ?PRIM_FILE:read_link_info/1,
+ %% which should in behave exactly as
+ %% ?PRIM_FILE:read_file_info/1
+ %% since they are not used on symbolic links.
+
+ ?line {ok, Info} =
+ ?PRIM_FILE_call(read_link_info, Handle, [Name]),
+ ?line {ok, Info} =
+ ?PRIM_FILE_call(read_link_info, Handle, [Alias]),
+ ?line #file_info{links = 2, type = regular} = Info,
+ ?line {error, eexist} =
+ ?PRIM_FILE_call(make_link, Handle, [Name, Alias]),
+ ok
+ end,
+
+ ?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}} = ?PRIM_FILE:read_link_info("."),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+symlinks_a(doc) -> "Test operations on symbolic links (for Unix).";
+symlinks_a(suite) -> [];
+symlinks_a(Config) when is_list(Config) ->
+ symlinks(Config, [], "_a").
+
+symlinks_b(doc) -> "Test operations on symbolic links (for Unix).";
+symlinks_b(suite) -> [];
+symlinks_b(Config) when is_list(Config) ->
+ ?line {ok, Handle} = ?PRIM_FILE:start(),
+ Result = symlinks(Config, Handle, "_b"),
+ ?line ok = ?PRIM_FILE:stop(Handle),
+ Result.
+
+symlinks(Config, Handle, Suffix) ->
+ ?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_symlink"++Suffix),
+ ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
+
+ ?line Name = filename:join(NewDir, "a_plain_file"),
+ ?line ok = ?PRIM_FILE:write_file(Name, "some stupid content\n"),
+
+ ?line Alias = filename:join(NewDir, "a_symlink_alias"),
+ ?line Result =
+ case ?PRIM_FILE_call(make_symlink, Handle, [Name, Alias]) of
+ {error, enotsup} ->
+ {skipped, "Links not supported on this platform"};
+ ok ->
+ ?line {ok, Info1} =
+ ?PRIM_FILE_call(read_file_info, Handle, [Name]),
+ ?line {ok, Info1} =
+ ?PRIM_FILE_call(read_file_info, Handle, [Alias]),
+ ?line {ok, Info1} =
+ ?PRIM_FILE_call(read_link_info, Handle, [Name]),
+ ?line #file_info{links = 1, type = regular} = Info1,
+
+ ?line {ok, Info2} =
+ ?PRIM_FILE_call(read_link_info, Handle, [Alias]),
+ ?line #file_info{links=1, type=symlink} = Info2,
+ ?line {ok, Name} =
+ ?PRIM_FILE_call(read_link, Handle, [Alias]),
+ ok
+ end,
+
+ ?line test_server:timetrap_cancel(Dog),
+ Result.
+
+%% Creates as many files as possible during a certain time,
+%% periodically calls list_dir/2 to check if it works,
+%% then deletes all files.
+
+list_dir_limit(doc) ->
+ "Tests if large directories can be read";
+list_dir_limit(suite) ->
+ [];
+list_dir_limit(Config) when is_list(Config) ->
+ ?line MaxTime = 120,
+ ?line MaxNumber = 20000,
+ ?line Dog = test_server:timetrap(
+ test_server:seconds(2*MaxTime + MaxTime)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_list_dir_limit"),
+ ?line {ok, Handle1} = ?PRIM_FILE:start(),
+ ?line ok = ?PRIM_FILE_call(make_dir, Handle1, [NewDir]),
+ Ref = erlang:start_timer(MaxTime*1000, self(), []),
+ ?line Result = list_dir_limit_loop(NewDir, Handle1, Ref, MaxNumber, 0),
+ ?line Time = case erlang:cancel_timer(Ref) of
+ false -> MaxTime;
+ T -> MaxTime - (T div 1000)
+ end,
+ ?line Number = case Result of
+ {ok, N} -> N;
+ {error, _Reason, N} -> N;
+ _ -> 0
+ end,
+ ?line {ok, Handle2} = ?PRIM_FILE:start(),
+ ?line list_dir_limit_cleanup(NewDir, Handle2, Number, 0),
+ ?line ok = ?PRIM_FILE:stop(Handle1),
+ ?line ok = ?PRIM_FILE:stop(Handle2),
+ ?line {ok, Number} = Result,
+ ?line test_server:timetrap_cancel(Dog),
+ {comment,
+ "Created " ++ integer_to_list(Number) ++ " files in "
+ ++ integer_to_list(Time) ++ " seconds."}.
+
+list_dir_limit_loop(Dir, Handle, _Ref, N, Cnt) when Cnt >= N ->
+ list_dir_check(Dir, Handle, Cnt);
+list_dir_limit_loop(Dir, Handle, Ref, N, Cnt) ->
+ receive
+ {timeout, Ref, []} ->
+ list_dir_check(Dir, Handle, Cnt)
+ after 0 ->
+ Name = integer_to_list(Cnt),
+ case ?PRIM_FILE:write_file(filename:join(Dir, Name), Name) of
+ ok ->
+ Next = Cnt + 1,
+ case Cnt rem 100 of
+ 0 ->
+ case list_dir_check(Dir, Handle, Next) of
+ {ok, Next} ->
+ list_dir_limit_loop(
+ Dir, Handle, Ref, N, Next);
+ Other ->
+ Other
+ end;
+ _ ->
+ list_dir_limit_loop(Dir, Handle, Ref, N, Next)
+ end;
+ {error, Reason} ->
+ {error, Reason, Cnt}
+ end
+ end.
+
+list_dir_check(Dir, Handle, Cnt) ->
+ case ?PRIM_FILE:list_dir(Handle, Dir) of
+ {ok, ListDir} ->
+ case length(ListDir) of
+ Cnt ->
+ {ok, Cnt};
+ X ->
+ {error,
+ {wrong_nof_files, X, ?LINE},
+ Cnt}
+ end;
+ {error, Reason} ->
+ {error, Reason, Cnt}
+ end.
+
+%% Deletes N files while ignoring errors, then continues deleting
+%% as long as they exist.
+
+list_dir_limit_cleanup(Dir, Handle, N, Cnt) when Cnt >= N ->
+ Name = integer_to_list(Cnt),
+ case ?PRIM_FILE:delete(Handle, filename:join(Dir, Name)) of
+ ok ->
+ list_dir_limit_cleanup(Dir, Handle, N, Cnt+1);
+ _ ->
+ ok
+ end;
+list_dir_limit_cleanup(Dir, Handle, N, Cnt) ->
+ Name = integer_to_list(Cnt),
+ ?PRIM_FILE:delete(Handle, filename:join(Dir, Name)),
+ list_dir_limit_cleanup(Dir, Handle, N, Cnt+1).
+