1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% This is a development feature when developing a new file module, 22%% ugly but practical. 23-ifndef(FILE_MODULE). 24-define(FILE_MODULE, file). 25-endif. 26-ifndef(FILE_SUITE). 27-define(FILE_SUITE, file_SUITE). 28-endif. 29-ifndef(FILE_INIT). 30-define(FILE_INIT(Config), Config). 31-endif. 32-ifndef(FILE_FINI). 33-define(FILE_FINI(Config), Config). 34-endif. 35-ifndef(FILE_INIT_PER_TESTCASE). 36-define(FILE_INIT_PER_TESTCASE(Config), Config). 37-endif. 38-ifndef(FILE_FIN_PER_TESTCASE). 39-define(FILE_FIN_PER_TESTCASE(Config), Config). 40-endif. 41 42-define(PRIM_FILE, prim_file). 43 44-module(?FILE_SUITE). 45 46-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 47 init_per_group/2,end_per_group/2, 48 init_per_testcase/2, end_per_testcase/2, 49 read_write_file/1, names/1]). 50-export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1, 51 list_dir/1,list_dir_error/1, 52 untranslatable_names/1, untranslatable_names_error/1, 53 pos1/1, pos2/1, pos3/1]). 54-export([close/1, consult1/1, path_consult/1, delete/1]). 55-export([ eval1/1, path_eval/1, script1/1, path_script/1, 56 open1/1, 57 old_modes/1, new_modes/1, path_open/1, open_errors/1]). 58-export([ file_info_basic_file/1, file_info_basic_directory/1, 59 file_info_bad/1, file_info_times/1, file_write_file_info/1, 60 file_wfi_helpers/1]). 61-export([rename/1, access/1, truncate/1, datasync/1, sync/1, 62 read_write/1, pread_write/1, append/1, exclusive/1]). 63-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). 64-export([otp_5814/1, otp_10852/1]). 65 66-export([ read_not_really_compressed/1, 67 read_compressed_cooked/1, read_compressed_cooked_binary/1, 68 read_cooked_tar_problem/1, 69 write_compressed/1, compress_errors/1, catenated_gzips/1, 70 compress_async_crash/1]). 71 72-export([ make_link/1, read_link_info_for_non_link/1, symlinks/1]). 73 74-export([copy/1]). 75 76-export([new_slave/2, old_slave/2, run_test/2]). 77 78-export([delayed_write/1, read_ahead/1, segment_read/1, segment_write/1]). 79 80-export([ipread/1]). 81 82-export([pid2name/1]). 83 84-export([interleaved_read_write/1]). 85 86-export([unicode/1]). 87-export([altname/1]). 88 89-export([large_file/0, large_file/1, large_write/0, large_write/1]). 90 91-export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]). 92 93-export([advise/1]). 94 95-export([allocate/1]). 96 97-export([allocate_file_size/1]). 98 99-export([standard_io/1,mini_server/1]). 100 101-export([old_io_protocol/1]). 102 103-export([unicode_mode/1]). 104 105-export([volume_relative_paths/1,unc_paths/1]). 106 107-export([tiny_writes/1, tiny_writes_delayed/1, 108 large_writes/1, large_writes_delayed/1, 109 tiny_reads/1, tiny_reads_ahead/1]). 110 111%% Debug exports 112-export([create_file_slow/2, create_file/2, create_bin/2]). 113-export([verify_file/2, verify_bin/3]). 114-export([bytes/2, iterate/3]). 115 116 117%% System probe functions that might be handy to check from the shell 118-export([disc_free/1, memsize/0]). 119 120-include_lib("common_test/include/ct.hrl"). 121-include_lib("common_test/include/ct_event.hrl"). 122 123-include_lib("kernel/include/file.hrl"). 124 125-define(THROW_ERROR(RES), throw({fail, ?LINE, RES})). 126 127 128suite() -> 129 [{ct_hooks,[ts_install_cth]}, 130 {timetrap,{minutes,1}}]. 131 132all() -> 133 [unicode, altname, read_write_file, {group, dirs}, 134 {group, files}, delete, rename, names, volume_relative_paths, unc_paths, 135 {group, errors}, {group, compression}, {group, links}, copy, 136 delayed_write, read_ahead, segment_read, segment_write, 137 ipread, pid2name, interleaved_read_write, otp_5814, otp_10852, 138 large_file, large_write, read_line_1, read_line_2, read_line_3, 139 read_line_4, standard_io, old_io_protocol, 140 unicode_mode, {group, bench} 141 ]. 142 143groups() -> 144 [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1, 145 list_dir, list_dir_error, untranslatable_names, 146 untranslatable_names_error]}, 147 {files, [], 148 [{group, open}, {group, pos}, {group, file_info}, 149 {group, consult}, {group, eval}, {group, script}, 150 truncate, sync, datasync, advise, allocate, allocate_file_size]}, 151 {open, [], 152 [open1, old_modes, new_modes, path_open, close, access, 153 read_write, pread_write, append, open_errors, 154 exclusive]}, 155 {pos, [], [pos1, pos2, pos3]}, 156 {file_info, [], 157 [file_info_basic_file, file_info_basic_directory, 158 file_info_bad, file_info_times, file_write_file_info, 159 file_wfi_helpers]}, 160 {consult, [], [consult1, path_consult]}, 161 {eval, [], [eval1, path_eval]}, 162 {script, [], [script1, path_script]}, 163 {errors, [], 164 [e_delete, e_rename, e_make_dir, e_del_dir]}, 165 {compression, [], 166 [read_compressed_cooked, read_compressed_cooked_binary, 167 read_cooked_tar_problem, read_not_really_compressed, 168 write_compressed, compress_errors, catenated_gzips, 169 compress_async_crash]}, 170 {links, [], 171 [make_link, read_link_info_for_non_link, symlinks]}, 172 {bench, [], 173 [tiny_writes, tiny_writes_delayed, 174 large_writes, large_writes_delayed, 175 tiny_reads, tiny_reads_ahead]}]. 176 177init_per_group(_GroupName, Config) -> 178 Config. 179 180end_per_group(bench, Config) -> 181 ScratchDir = proplists:get_value(priv_dir, Config), 182 file:delete(filename:join(ScratchDir, "benchmark_scratch_file")), 183 Config; 184end_per_group(_GroupName, Config) -> 185 Config. 186 187 188init_per_suite(Config) when is_list(Config) -> 189 SaslConfig = case application:start(sasl) of 190 {error, {already_started, sasl}} -> 191 []; 192 ok -> 193 [{sasl,started}] 194 end, 195 application:start(os_mon), 196 197 case os:type() of 198 {win32, _} -> 199 Priv = proplists:get_value(priv_dir, Config), 200 HasAccessTime = 201 case ?FILE_MODULE:read_file_info(Priv) of 202 {ok, #file_info{atime={_, {0, 0, 0}}}} -> 203 %% This is a unfortunately a FAT file system. 204 [no_access_time]; 205 {ok, _} -> 206 [] 207 end, 208 ?FILE_INIT(HasAccessTime++Config++SaslConfig); 209 _ -> 210 ?FILE_INIT(Config++SaslConfig) 211 end. 212 213end_per_suite(Config) when is_list(Config) -> 214 case os:type() of 215 {win32, _} -> 216 os:cmd("subst z: /d"); 217 _ -> 218 ok 219 end, 220 221 application:stop(os_mon), 222 case proplists:get_value(sasl, Config) of 223 started -> 224 application:stop(sasl); 225 _Else -> 226 ok 227 end, 228 ?FILE_FINI(Config). 229 230init_per_testcase(_Func, Config) -> 231 %%error_logger:info_msg("~p:~p *****~n", [?MODULE, _Func]), 232 ?FILE_INIT_PER_TESTCASE(Config). 233 234end_per_testcase(_Func, Config) -> 235 %% error_logger:info_msg("~p:~p END *****~n", [?MODULE, _Func]), 236 ?FILE_FIN_PER_TESTCASE(Config). 237 238%% Matches a term (the last) against alternatives 239expect(X, _, X) -> 240 X; 241expect(_, X, X) -> 242 X. 243 244expect(X, _, _, X) -> 245 X; 246expect(_, X, _, X) -> 247 X; 248expect(_, _, X, X) -> 249 X. 250 251expect(X, _, _, _, X) -> 252 X; 253expect(_, X, _, _, X) -> 254 X; 255expect(_, _, X, _, X) -> 256 X; 257expect(_, _, _, X, X) -> 258 X. 259 260%% Calculate the time difference 261time_dist({YY, MM, DD, H, M, S}, DT) -> 262 time_dist({{YY, MM, DD}, {H, M, S}}, DT); 263time_dist(DT, {YY, MM, DD, H, M, S}) -> 264 time_dist(DT, {{YY, MM, DD}, {H, M, S}}); 265time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) -> 266 calendar:datetime_to_gregorian_seconds(DT2) 267 - calendar:datetime_to_gregorian_seconds(DT1). 268 269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 270mini_server(Parent) -> 271 receive 272 die -> 273 ok; 274 {io_request,From,To,{put_chars,_Encoding,Data}} -> 275 Parent ! {io_request,From,To,{put_chars,Data}}, 276 From ! {io_reply, To, ok}, 277 mini_server(Parent); 278 {io_request,From,To,{get_chars,'',N}} -> 279 Parent ! {io_request,From,To,{get_chars,'',N}}, 280 From ! {io_reply, To, {ok, lists:duplicate(N,$a)}}, 281 mini_server(Parent); 282 {io_request,From,To,{get_line,''}} -> 283 Parent ! {io_request,From,To,{get_line,''}}, 284 From ! {io_reply, To, {ok, "hej\n"}}, 285 mini_server(Parent) 286 end. 287 288%% Test that standard i/o-servers work with file module. 289standard_io(Config) when is_list(Config) -> 290 %% Really just a smoke test 291 Pid = spawn(?MODULE,mini_server,[self()]), 292 register(mini_server,Pid), 293 ok = file:write(mini_server,<<"hej\n">>), 294 receive 295 {io_request,_,_,{put_chars,<<"hej\n">>}} -> 296 ok 297 after 1000 -> 298 exit(noreply) 299 end, 300 {ok,"aaaaa"} = file:read(mini_server,5), 301 receive 302 {io_request,_,_,{get_chars,'',5}} -> 303 ok 304 after 1000 -> 305 exit(noreply) 306 end, 307 {ok,"hej\n"} = file:read_line(mini_server), 308 receive 309 {io_request,_,_,{get_line,''}} -> 310 ok 311 after 1000 -> 312 exit(noreply) 313 end, 314 OldGL = group_leader(), 315 group_leader(Pid,self()), 316 ok = file:write(standard_io,<<"hej\n">>), 317 group_leader(OldGL,self()), 318 receive 319 {io_request,_,_,{put_chars,<<"hej\n">>}} -> 320 ok 321 after 1000 -> 322 exit(noreply) 323 end, 324 group_leader(Pid,self()), 325 {ok,"aaaaa"} = file:read(standard_io,5), 326 group_leader(OldGL,self()), 327 receive 328 {io_request,_,_,{get_chars,'',5}} -> 329 ok 330 after 1000 -> 331 exit(noreply) 332 end, 333 group_leader(Pid,self()), 334 {ok,"hej\n"} = file:read_line(standard_io), 335 group_leader(OldGL,self()), 336 receive 337 {io_request,_,_,{get_line,''}} -> 338 ok 339 after 1000 -> 340 exit(noreply) 341 end, 342 Pid ! die, 343 receive after 1000 -> ok end. 344 345%% Test that the old file IO protocol =< R16B still works. 346old_io_protocol(Config) when is_list(Config) -> 347 RootDir = proplists:get_value(priv_dir,Config), 348 Name = filename:join(RootDir, 349 atom_to_list(?MODULE) 350 ++"old_io_protocol.fil"), 351 MyData = "0123456789abcdefghijklmnopqrstuvxyz", 352 ok = ?FILE_MODULE:write_file(Name, MyData), 353 {ok, Fd} = ?FILE_MODULE:open(Name, write), 354 Fd ! {file_request,self(),Fd,truncate}, 355 receive 356 {file_reply,Fd,ok} -> ok 357 end, 358 ok = ?FILE_MODULE:close(Fd), 359 {ok, <<>>} = ?FILE_MODULE:read_file(Name), 360 [] = flush(), 361 ok. 362 363unicode_mode(Config) -> 364 Dir = {dir, proplists:get_value(priv_dir,Config)}, 365 OptVariants = [[Dir], 366 [Dir, {encoding, utf8}], 367 [Dir, binary], 368 [Dir, binary, {encoding, utf8}] 369 ], 370 ReadVariants = [{read, fun(Fd) -> um_read(Fd, fun(Fd1) -> file:read(Fd1, 1024) end) end}, 371 {read_line, fun(Fd) -> um_read(Fd, fun(Fd1) -> file:read_line(Fd1) end) end} 372 %%{pread, fun(Fd) -> file:pread(Fd, 0, 1024) end}, 373 %%{preadl, fun(Fd) -> file:pread(Fd, [{0, 1024}]) end}, 374 ], 375 376 _ = [read_write_0("ASCII: list: Hello World", Read, Opt) || 377 Opt <- OptVariants, Read <- ReadVariants], 378 _ = [read_write_0("LATIN1: list: åäöÅÄÖ", Read, Opt) || 379 Opt <- OptVariants, Read <- ReadVariants], 380 _ = [read_write_0(<<"ASCII: bin: Hello World">>, Read, Opt) || 381 Opt <- OptVariants, Read <- ReadVariants], 382 _ = [read_write_0(<<"LATIN1: bin: åäöÅÄÖ">>, Read, Opt) || 383 Opt <- OptVariants, Read <- ReadVariants], 384 %% These will be double encoded if option is encoding utf-8 385 _ = [read_write_0(<<"UTF8: bin: Ωß"/utf8>>, Read, Opt) || 386 Opt <- OptVariants, Read <- ReadVariants], 387 %% These should not work (with encoding set to utf-8) 388 %% according to file's documentation 389 _ = [read_write_0("UTF8: list: Ωß", Read, Opt) || 390 Opt <- OptVariants, Read <- ReadVariants], 391 ok. 392 393read_write_0(Str, {Func, ReadFun}, Options) -> 394 try 395 Res = read_write_1(Str, ReadFun, Options), 396 io:format("~p: ~ts ~p '~p'~n", [Func, Str, tl(Options), Res]), 397 ok 398 catch {fail, Line, ReadBytes = [_|_]} -> 399 io:format("~p:~p: ~p ERROR: ~w vs~n ~w~n - ~p~n", 400 [?MODULE, Line, Func, Str, ReadBytes, Options]), 401 exit({error, ?LINE}); 402 {fail, Line, ReadBytes} -> 403 io:format("~p:~p: ~p ERROR: ~ts vs~n ~w~n - ~p~n", 404 [?MODULE, Line, Func, Str, ReadBytes, Options]), 405 exit({error, ?LINE}); 406 error:What:Stacktrace -> 407 io:format("~p:??: ~p ERROR: ~p from~n ~w~n ~p~n", 408 [?MODULE, Func, What, Str, Options]), 409 410 io:format("\t~p~n", [Stacktrace]), 411 exit({error, ?LINE}) 412 end. 413 414read_write_1(Str0, ReadFun, [{dir,Dir}|Options]) -> 415 File = um_filename(Str0, Dir, Options), 416 Pre = "line 1\n", Post = "\nlast line\n", 417 Str = case is_list(Str0) andalso lists:max(Str0) > 255 of 418 false -> %% Normal case Use options 419 {ok, FdW} = file:open(File, [write|Options]), 420 IO = [Pre, Str0, Post], 421 ok = file:write(FdW, IO), 422 case is_binary(Str0) of 423 true -> iolist_to_binary(IO); 424 false -> lists:append(IO) 425 end; 426 true -> %% Test unicode lists 427 {ok, FdW} = file:open(File, [write]), 428 Utf8 = unicode:characters_to_binary([Pre, Str0, Post]), 429 file:write(FdW, Utf8), 430 {unicode, Utf8} 431 end, 432 file:close(FdW), 433 {ok, FdR} = file:open(File, [read|Options]), 434 ReadRes = ReadFun(FdR), 435 file:close(FdR), 436 Res = um_check(Str, ReadRes, Options), 437 file:delete(File), 438 Res. 439 440 441um_read(Fd, Fun) -> 442 um_read(Fd, Fun, []). 443 444um_read(Fd, Fun, Acc) -> 445 case Fun(Fd) of 446 eof -> 447 case is_binary(hd(Acc)) of 448 true -> {ok, iolist_to_binary(lists:reverse(Acc))}; 449 false -> {ok, lists:append(lists:reverse(Acc))} 450 end; 451 {ok, Data} -> 452 um_read(Fd, Fun, [Data|Acc]); 453 Error -> 454 Error 455 end. 456 457 458um_check(Str, {ok, Str}, _) -> ok; 459um_check(Bin, {ok, Res}, _Options) when is_binary(Bin), is_list(Res) -> 460 case list_to_binary(Res) of 461 Bin -> ok; 462 _ -> ?THROW_ERROR(Res) 463 end; 464um_check(Str, {ok, Res}, _Options) when is_list(Str), is_binary(Res) -> 465 case iolist_to_binary(Str) of 466 Res -> ok; 467 _ -> ?THROW_ERROR(Res) 468 end; 469um_check({unicode, Utf8Bin}, Res, Options) -> 470 um_check_unicode(Utf8Bin, Res, 471 proplists:get_value(binary, Options, false), 472 proplists:get_value(encoding, Options, none)); 473um_check(_Str, Res, _Options) -> 474 ?THROW_ERROR(Res). 475 476um_check_unicode(Utf8Bin, {ok, Utf8Bin}, true, none) -> 477 ok; 478um_check_unicode(Utf8Bin, {ok, List = [_|_]}, false, none) -> 479 case binary_to_list(Utf8Bin) == List of 480 true -> ok; 481 false -> ?THROW_ERROR(List) 482 end; 483um_check_unicode(_Utf8Bin, {error, {no_translation, unicode, latin1}}, _, _) -> 484 no_translation; 485um_check_unicode(_Utf8Bin, Error = {error, _}, _, _Unicode) -> 486 ?THROW_ERROR(Error); 487um_check_unicode(_Utf8Bin, {ok, _ListOrBin}, _, _UTF8_) -> 488 %% List = if is_binary(ListOrBin) -> unicode:characters_to_list(ListOrBin); 489 %% true -> ListOrBin 490 %% end, 491 %% io:format("In: ~w~n", [binary_to_list(Utf8Bin)]), 492 %% io:format("Ut: ~w~n", [List]), 493 ?THROW_ERROR({shoud_be, no_translation}). 494 495um_filename(Bin, Dir, Options) when is_binary(Bin) -> 496 um_filename(binary_to_list(Bin), Dir, Options); 497um_filename(Str = [_|_], Dir, Options) -> 498 Name = hd(string:lexemes(Str, ":")), 499 Enc = atom_to_list(proplists:get_value(encoding, Options, latin1)), 500 File = case lists:member(binary, Options) of 501 true -> 502 "test_" ++ Name ++ "_bin_enc_" ++ Enc; 503 false -> 504 "test_" ++ Name ++ "_list_enc_" ++ Enc 505 end, 506 filename:join(Dir, File). 507 508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 509 510read_write_file(Config) when is_list(Config) -> 511 RootDir = proplists:get_value(priv_dir,Config), 512 Name = filename:join(RootDir, 513 atom_to_list(?MODULE) 514 ++"_read_write_file"), 515 516 %% Try writing and reading back some term 517 SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, 518 Bin1 = term_to_binary(SomeTerm), 519 ok = do_read_write_file(Name, Bin1), 520 521 %% Try a "null" term 522 NullTerm = [], 523 Bin2 = term_to_binary(NullTerm), 524 ok = do_read_write_file(Name, Bin2), 525 526 %% Try reading a nonexistent file 527 Name2 = filename:join(RootDir, 528 atom_to_list(?MODULE) 529 ++"_nonexistent_file"), 530 {error, enoent} = ?FILE_MODULE:read_file(Name2), 531 {error, enoent} = ?FILE_MODULE:read_file(""), 532 {error, enoent} = ?FILE_MODULE:read_file(''), 533 534 %% Try writing to a bad filename 535 {error, enoent} = do_read_write_file("", Bin2), 536 537 %% Try writing something else than a binary 538 {error, badarg} = do_read_write_file(Name, {1,2,3}), 539 {error, badarg} = do_read_write_file(Name, self()), 540 541 %% Some non-term binaries 542 ok = do_read_write_file(Name, []), 543 544 %% Write some iolists 545 ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]), 546 ok = do_read_write_file(Name, ["string",<<"binary">>]), 547 ok = do_read_write_file(Name, "pure string"), 548 549 [] = flush(), 550 ok. 551 552do_read_write_file(Name, Data) -> 553 case ?FILE_MODULE:write_file(Name, Data) of 554 ok -> 555 BinData = iolist_to_binary(Data), 556 {ok,BinData} = ?FILE_MODULE:read_file(Name), 557 558 ok = ?FILE_MODULE:write_file(Name, Data, []), 559 {ok,BinData} = ?FILE_MODULE:read_file(Name), 560 561 ok = ?FILE_MODULE:write_file(Name, Data, [raw]), 562 {ok,BinData} = ?FILE_MODULE:read_file(Name), 563 564 ok; 565 {error,_}=Res -> 566 Res = ?FILE_MODULE:write_file(Name, Data, []), 567 Res = ?FILE_MODULE:write_file(Name, Data, [raw]), 568 Res 569 end. 570 571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 572 573 574make_del_dir(Config) when is_list(Config) -> 575 RootDir = proplists:get_value(priv_dir,Config), 576 NewDir = filename:join(RootDir, 577 atom_to_list(?MODULE) 578 ++"_mk-dir"), 579 ok = ?FILE_MODULE:make_dir(NewDir), 580 {error, eexist} = ?FILE_MODULE:make_dir(NewDir), 581 ok = ?FILE_MODULE:del_dir(NewDir), 582 {error, enoent} = ?FILE_MODULE:del_dir(NewDir), 583 %% Make sure we are not in a directory directly under test_server 584 %% as that would result in eacces errors when trying to delete '..', 585 %% because there are processes having that directory as current. 586 ok = ?FILE_MODULE:make_dir(NewDir), 587 {ok,CurrentDir} = file:get_cwd(), 588 case {os:type(), length(NewDir) >= 260 } of 589 {{win32,_}, true} -> 590 io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []), 591 io:format("\nNewDir = ~p\n", [NewDir]); 592 _ -> 593 ok = ?FILE_MODULE:set_cwd(NewDir) 594 end, 595 try 596 %% Check that we get an error when trying to create... 597 %% a deep directory 598 NewDir2 = filename:join(RootDir, 599 atom_to_list(?MODULE) 600 ++"_mk-dir-noexist/foo"), 601 {error, enoent} = ?FILE_MODULE:make_dir(NewDir2), 602 %% a nameless directory 603 {error, enoent} = ?FILE_MODULE:make_dir(""), 604 %% a directory with illegal name 605 {error, badarg} = ?FILE_MODULE:make_dir({1,2,3}), 606 607 %% a directory with illegal name, even if it's a (bad) list 608 {error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]), 609 610 %% Maybe this isn't an error, exactly, but worth mentioning anyway: 611 %% ok = ?FILE_MODULE:make_dir([$f,$o,$o,0,$b,$a,$r])), 612 %% The above line works, and created a directory "./foo" 613 %% More elegant would maybe have been to fail, or to really create 614 %% a directory, but with a name that incorporates the "bar" part of 615 %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same 616 %% dir. But this would slow it down. 617 618 %% Try deleting some bad directories 619 %% Deleting the parent directory to the current, sounds dangerous, huh? 620 %% Don't worry ;-) the parent directory should never be empty, right? 621 case ?FILE_MODULE:del_dir('..') of 622 {error, eexist} -> ok; 623 {error, eacces} -> ok; %OpenBSD 624 {error, einval} -> ok %FreeBSD 625 end, 626 {error, enoent} = ?FILE_MODULE:del_dir(""), 627 {error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]), 628 629 [] = flush() 630 after 631 ?FILE_MODULE:set_cwd(CurrentDir) 632 end, 633 ok. 634 635cur_dir_0(Config) when is_list(Config) -> 636 %% Find out the current dir, and cd to it ;-) 637 {ok,BaseDir} = ?FILE_MODULE:get_cwd(), 638 Dir1 = BaseDir ++ "", %% Check that it's a string 639 ok = ?FILE_MODULE:set_cwd(Dir1), 640 641 %% Make a new dir, and cd to that 642 RootDir = proplists:get_value(priv_dir,Config), 643 NewDir = filename:join(RootDir, 644 atom_to_list(?MODULE) 645 ++"_curdir"), 646 ok = ?FILE_MODULE:make_dir(NewDir), 647 case {os:type(), length(NewDir) >= 260} of 648 {{win32,_}, true} -> 649 io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"), 650 io:format("\nNewDir = ~p\n", [NewDir]); 651 _ -> 652 io:format("cd to ~s",[NewDir]), 653 ok = ?FILE_MODULE:set_cwd(NewDir), 654 655 %% Create a file in the new current directory, and check that it 656 %% really is created there 657 UncommonName = "uncommon.fil", 658 {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write), 659 ok = ?FILE_MODULE:close(Fd), 660 {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."), 661 true = lists:member(UncommonName,NewDirFiles), 662 663 %% Ensure that we get the same result with a trailing slash; the 664 %% APIs used on Windows will choke on them if passed directly. 665 {ok,NewDirFiles} = ?FILE_MODULE:list_dir("./"), 666 667 %% Delete the directory and return to the old current directory 668 %% and check that the created file isn't there (too!) 669 expect({error, einval}, {error, eacces}, 670 ?FILE_MODULE:del_dir(NewDir)), 671 ?FILE_MODULE:delete(UncommonName), 672 {ok,[]} = ?FILE_MODULE:list_dir("."), 673 ok = ?FILE_MODULE:set_cwd(Dir1), 674 io:format("cd back to ~s",[Dir1]), 675 676 ok = ?FILE_MODULE:del_dir(NewDir), 677 {error, enoent} = ?FILE_MODULE:set_cwd(NewDir), 678 ok = ?FILE_MODULE:set_cwd(Dir1), 679 io:format("cd back to ~s",[Dir1]), 680 {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."), 681 false = lists:member(UncommonName,OldDirFiles) 682 end, 683 684 %% Try doing some bad things 685 {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}), 686 {error, enoent} = ?FILE_MODULE:set_cwd(""), 687 {error, enoent} = ?FILE_MODULE:set_cwd(".......a......"), 688 {ok,BaseDir} = ?FILE_MODULE:get_cwd(), %% Still there? 689 690 %% On Windows, there should only be slashes, no backslashes, 691 %% in the return value of get_cwd(). 692 %% (The test is harmless on Unix, because filenames usually 693 %% don't contain backslashes.) 694 695 {ok, BaseDir} = ?FILE_MODULE:get_cwd(), 696 false = lists:member($\\, BaseDir), 697 698 [] = flush(), 699 ok. 700 701%% Tests ?FILE_MODULE:get_cwd/1. 702 703cur_dir_1(Config) when is_list(Config) -> 704 case os:type() of 705 {win32, _} -> 706 win_cur_dir_1(Config); 707 _ -> 708 {error, enotsup} = ?FILE_MODULE:get_cwd("d:") 709 end, 710 [] = flush(), 711 ok. 712 713win_cur_dir_1(_Config) -> 714 {ok,BaseDir} = ?FILE_MODULE:get_cwd(), 715 716 %% Get the drive letter from the current directory, 717 %% and try to get current directory for that drive. 718 719 [CurDrive,$:|_] = BaseDir, 720 {ok,BaseDir} = ?FILE_MODULE:get_cwd([CurDrive,$:]), 721 io:format("BaseDir = ~s\n", [BaseDir]), 722 723 %% We should error out on non-existent drives. Any reasonable system will 724 %% have at least one. 725 CurDirs = [?FILE_MODULE:get_cwd([Drive,$:]) || Drive <- lists:seq($A, $Z)], 726 lists:member({error,eaccess}, CurDirs), 727 728 %% Unfortunately, there is no way to move away from the 729 %% current drive as we can't use the "subst" command from 730 %% a SSH connection. We can't test any more. 731 732 ok. 733 734 735%%% 736%%% Test list_dir() on a non-existing pathname. 737%%% 738 739list_dir_error(Config) -> 740 Priv = proplists:get_value(priv_dir, Config), 741 NonExisting = filename:join(Priv, "non-existing-dir"), 742 {error,enoent} = ?FILE_MODULE:list_dir(NonExisting), 743 ok. 744 745%%% 746%%% Test list_dir() and list_dir_all(). 747%%% 748 749list_dir(Config) -> 750 RootDir = proplists:get_value(priv_dir, Config), 751 TestDir = filename:join(RootDir, ?MODULE_STRING++"_list_dir"), 752 ?FILE_MODULE:make_dir(TestDir), 753 list_dir_1(TestDir, 42, []). 754 755list_dir_1(TestDir, 0, Sorted) -> 756 [ok = ?FILE_MODULE:delete(filename:join(TestDir, F)) || 757 F <- Sorted], 758 ok = ?FILE_MODULE:del_dir(TestDir); 759list_dir_1(TestDir, Cnt, Sorted0) -> 760 Base = "file" ++ integer_to_list(Cnt), 761 Name = filename:join(TestDir, Base), 762 ok = ?FILE_MODULE:write_file(Name, Base), 763 Sorted = lists:merge([Base], Sorted0), 764 {ok,DirList0} = ?FILE_MODULE:list_dir(TestDir), 765 {ok,DirList1} = ?FILE_MODULE:list_dir_all(TestDir), 766 Sorted = lists:sort(DirList0), 767 Sorted = lists:sort(DirList1), 768 list_dir_1(TestDir, Cnt-1, Sorted). 769 770untranslatable_names(Config) -> 771 case no_untranslatable_names() of 772 true -> 773 {skip,"Not a problem on this OS"}; 774 false -> 775 untranslatable_names_1(Config) 776 end. 777 778untranslatable_names_1(Config) -> 779 {ok,OldCwd} = file:get_cwd(), 780 PrivDir = proplists:get_value(priv_dir, Config), 781 Dir = filename:join(PrivDir, "untranslatable_names"), 782 ok = file:make_dir(Dir), 783 Node = start_node(untranslatable_names, "+fnu"), 784 try 785 ok = file:set_cwd(Dir), 786 [ok = file:write_file(F, F) || {_,F} <- untranslatable_names()], 787 788 ExpectedListDir0 = [unicode:characters_to_list(N, utf8) || 789 {utf8,N} <- untranslatable_names()], 790 ExpectedListDir = lists:sort(ExpectedListDir0), 791 io:format("ExpectedListDir: ~p\n", [ExpectedListDir]), 792 ExpectedListDir = call_and_sort(Node, file, list_dir, [Dir]), 793 794 ExpectedListDirAll0 = [case Enc of 795 utf8 -> 796 unicode:characters_to_list(N, utf8); 797 latin1 -> 798 N 799 end || {Enc,N} <- untranslatable_names()], 800 ExpectedListDirAll = lists:sort(ExpectedListDirAll0), 801 io:format("ExpectedListDirAll: ~p\n", [ExpectedListDirAll]), 802 ExpectedListDirAll = call_and_sort(Node, file, list_dir_all, [Dir]) 803 after 804 catch test_server:stop_node(Node), 805 file:set_cwd(OldCwd), 806 [file:delete(F) || {_,F} <- untranslatable_names()], 807 file:del_dir(Dir) 808 end, 809 ok. 810 811untranslatable_names_error(Config) -> 812 case no_untranslatable_names() of 813 true -> 814 {skip,"Not a problem on this OS"}; 815 false -> 816 untranslatable_names_error_1(Config) 817 end. 818 819untranslatable_names_error_1(Config) -> 820 {ok,OldCwd} = file:get_cwd(), 821 PrivDir = proplists:get_value(priv_dir, Config), 822 Dir = filename:join(PrivDir, "untranslatable_names_error"), 823 ok = file:make_dir(Dir), 824 Node = start_node(untranslatable_names, "+fnue"), 825 try 826 ok = file:set_cwd(Dir), 827 [ok = file:write_file(F, F) || {_,F} <- untranslatable_names()], 828 829 ExpectedListDir0 = [unicode:characters_to_list(N, utf8) || 830 {utf8,N} <- untranslatable_names()], 831 ExpectedListDir = lists:sort(ExpectedListDir0), 832 io:format("ExpectedListDir: ~p\n", [ExpectedListDir]), 833 {error,{no_translation,BadFile}} = 834 rpc:call(Node, file, list_dir, [Dir]), 835 true = lists:keymember(BadFile, 2, untranslatable_names()) 836 837 after 838 catch test_server:stop_node(Node), 839 file:set_cwd(OldCwd), 840 [file:delete(F) || {_,F} <- untranslatable_names()], 841 file:del_dir(Dir) 842 end, 843 ok. 844 845untranslatable_names() -> 846 [{utf8,<<"abc">>}, 847 {utf8,<<"def">>}, 848 {utf8,<<"Lagerl",195,182,"f">>}, 849 {utf8,<<195,150,"stra Emterwik">>}, 850 {latin1,<<"M",229,"rbacka">>}, 851 {latin1,<<"V",228,"rmland">>}]. 852 853call_and_sort(Node, M, F, A) -> 854 {ok,Res} = rpc:call(Node, M, F, A), 855 lists:sort(Res). 856 857no_untranslatable_names() -> 858 case os:type() of 859 {unix,darwin} -> true; 860 {win32,_} -> true; 861 _ -> false 862 end. 863 864start_node(Name, Args) -> 865 [_,Host] = string:lexemes(atom_to_list(node()), "@"), 866 ct:log("Trying to start ~w@~s~n", [Name,Host]), 867 case test_server:start_node(Name, peer, [{args,Args}]) of 868 {error,Reason} -> 869 ct:fail(Reason); 870 {ok,Node} -> 871 ct:log("Node ~p started~n", [Node]), 872 Node 873 end. 874 875 876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 877 878 879 880open1(Config) when is_list(Config) -> 881 RootDir = proplists:get_value(priv_dir,Config), 882 NewDir = filename:join(RootDir, 883 atom_to_list(?MODULE) 884 ++"_files"), 885 ok = ?FILE_MODULE:make_dir(NewDir), 886 Name = filename:join(NewDir, "foo1.fil"), 887 {ok,Fd1} = ?FILE_MODULE:open(Name,read_write), 888 {ok,Fd2} = ?FILE_MODULE:open(Name,read), 889 Str = "{a,tuple}.\n", 890 io:format(Fd1,Str,[]), 891 {ok,0} = ?FILE_MODULE:position(Fd1,bof), 892 Str = io:get_line(Fd1,''), 893 Str = io:get_line(Fd2,''), 894 ok = ?FILE_MODULE:close(Fd2), 895 {ok,0} = ?FILE_MODULE:position(Fd1,bof), 896 ok = ?FILE_MODULE:truncate(Fd1), 897 eof = io:get_line(Fd1,''), 898 ok = ?FILE_MODULE:close(Fd1), 899 {ok,Fd3} = ?FILE_MODULE:open(Name,read), 900 eof = io:get_line(Fd3,''), 901 ok = ?FILE_MODULE:close(Fd3), 902 [] = flush(), 903 ok. 904 905%% Tests all open modes. 906 907old_modes(Config) when is_list(Config) -> 908 RootDir = proplists:get_value(priv_dir, Config), 909 NewDir = filename:join(RootDir, 910 atom_to_list(?MODULE) 911 ++"_old_open_modes"), 912 ok = ?FILE_MODULE:make_dir(NewDir), 913 Name1 = filename:join(NewDir, "foo1.fil"), 914 Marker = "hello, world", 915 916 %% write 917 {ok, Fd1} = ?FILE_MODULE:open(Name1, write), 918 ok = io:write(Fd1, Marker), 919 ok = io:put_chars(Fd1, ".\n"), 920 ok = ?FILE_MODULE:close(Fd1), 921 922 %% read 923 {ok, Fd2} = ?FILE_MODULE:open(Name1, read), 924 {ok, Marker} = io:read(Fd2, prompt), 925 ok = ?FILE_MODULE:close(Fd2), 926 927 %% read_write 928 {ok, Fd3} = ?FILE_MODULE:open(Name1, read_write), 929 {ok, Marker} = io:read(Fd3, prompt), 930 ok = io:write(Fd3, Marker), 931 ok = ?FILE_MODULE:close(Fd3), 932 933 [] = flush(), 934 ok. 935 936 937new_modes(Config) when is_list(Config) -> 938 RootDir = proplists:get_value(priv_dir, Config), 939 NewDir = filename:join(RootDir, 940 atom_to_list(?MODULE) 941 ++"_new_open_modes"), 942 ok = ?FILE_MODULE:make_dir(NewDir), 943 Name1 = filename:join(NewDir, "foo1.fil"), 944 Marker = "hello, world", 945 946 %% write 947 {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]), 948 ok = io:write(Fd1, Marker), 949 ok = io:put_chars(Fd1, ".\n"), 950 ok = ?FILE_MODULE:close(Fd1), 951 952 %% read 953 {ok, Fd2} = ?FILE_MODULE:open(Name1, [read]), 954 {ok, Marker} = io:read(Fd2, prompt), 955 ok = ?FILE_MODULE:close(Fd2), 956 957 %% read and write 958 {ok, Fd3} = ?FILE_MODULE:open(Name1, [read, write]), 959 {ok, Marker} = io:read(Fd3, prompt), 960 ok = io:write(Fd3, Marker), 961 ok = ?FILE_MODULE:close(Fd3), 962 963 %% read by default 964 {ok, Fd4} = ?FILE_MODULE:open(Name1, []), 965 {ok, Marker} = io:read(Fd4, prompt), 966 ok = ?FILE_MODULE:close(Fd4), 967 968 %% read and binary 969 {ok, Fd5} = ?FILE_MODULE:open(Name1, [read, binary]), 970 {ok, Marker} = io:read(Fd5, prompt), 971 ok = ?FILE_MODULE:close(Fd5), 972 973 %% read, raw 974 {ok, Fd6} = ?FILE_MODULE:open(Name1, [read, raw]), 975 {ok, [$\[]} = ?FILE_MODULE:read(Fd6, 1), 976 ok = ?FILE_MODULE:close(Fd6), 977 978 %% write and sync 979 case ?FILE_MODULE:open(Name1, [write, sync]) of 980 {ok, Fd7} -> 981 ok = io:write(Fd7, Marker), 982 ok = io:put_chars(Fd7, ".\n"), 983 ok = ?FILE_MODULE:close(Fd7), 984 {ok, Fd8} = ?FILE_MODULE:open(Name1, [read]), 985 {ok, Marker} = io:read(Fd8, prompt), 986 ok = ?FILE_MODULE:close(Fd8); 987 {error, enotsup} -> 988 %% for platforms that don't support the sync option 989 ok 990 end, 991 992 [] = flush(), 993 ok. 994 995path_open(Config) when is_list(Config) -> 996 RootDir = proplists:get_value(priv_dir,Config), 997 NewDir = filename:join(RootDir, 998 atom_to_list(?MODULE) 999 ++"_path_open"), 1000 ok = ?FILE_MODULE:make_dir(NewDir), 1001 FileName = "path_open.fil", 1002 Name = filename:join(RootDir, FileName), 1003 {ok,Fd1,_FullName1} = 1004 ?FILE_MODULE:path_open( 1005 [RootDir, 1006 "nosuch1", 1007 NewDir],FileName,write), 1008 io:format(Fd1,"ABCDEFGH",[]), 1009 ok = ?FILE_MODULE:close(Fd1), 1010 1011 %% locate it in the last dir 1012 {ok,Fd2,_FullName2} = 1013 ?FILE_MODULE:path_open( 1014 ["nosuch1", 1015 NewDir, 1016 RootDir],FileName,read), 1017 {ok,2} = 1018 ?FILE_MODULE:position(Fd2,2), "C" = io:get_chars(Fd2,'',1), 1019 ok = ?FILE_MODULE:close(Fd2), 1020 %% Try a failing path 1021 {error, enoent} = ?FILE_MODULE:path_open( 1022 ["nosuch1", 1023 NewDir],FileName,read), 1024 %% Check that it's found regardless of path, if an absolute name given 1025 {ok,Fd3,_FullPath3} = 1026 ?FILE_MODULE:path_open( 1027 ["nosuch1", 1028 NewDir],Name,read), 1029 {ok,2} = 1030 ?FILE_MODULE:position(Fd3,2), "C" = io:get_chars(Fd3,'',1), 1031 ok = ?FILE_MODULE:close(Fd3), 1032 1033 [] = flush(), 1034 ok. 1035 1036close(Config) when is_list(Config) -> 1037 RootDir = proplists:get_value(priv_dir,Config), 1038 Name = filename:join(RootDir, 1039 atom_to_list(?MODULE) 1040 ++"_close.fil"), 1041 {ok,Fd1} = ?FILE_MODULE:open(Name,read_write), 1042 %% Just closing it is no fun, we did that a million times already 1043 %% This is a common error, for code written before Erlang 4.3 1044 %% bacause then ?FILE_MODULE:open just returned a Pid, and not everyone 1045 %% really checked what they got. 1046 {'EXIT',_Msg} = (catch ok = ?FILE_MODULE:close({ok,Fd1})), 1047 ok = ?FILE_MODULE:close(Fd1), 1048 1049 %% Try closing one more time 1050 Val = ?FILE_MODULE:close(Fd1), 1051 io:format("Second close gave: ~p",[Val]), 1052 1053 %% All operations on a closed raw file should EINVAL, even if they're not 1054 %% supported on the current platform. 1055 {ok,Fd2} = ?FILE_MODULE:open(Name, [read, write, raw]), 1056 ok = ?FILE_MODULE:close(Fd2), 1057 1058 {error, einval} = ?FILE_MODULE:advise(Fd2, 5, 5, normal), 1059 {error, einval} = ?FILE_MODULE:allocate(Fd2, 5, 5), 1060 {error, einval} = ?FILE_MODULE:close(Fd2), 1061 {error, einval} = ?FILE_MODULE:datasync(Fd2), 1062 {error, einval} = ?FILE_MODULE:position(Fd2, 5), 1063 {error, einval} = ?FILE_MODULE:pread(Fd2, 5, 1), 1064 {error, einval} = ?FILE_MODULE:pwrite(Fd2, 5, "einval please"), 1065 {error, einval} = ?FILE_MODULE:read(Fd2, 1), 1066 {error, einval} = ?FILE_MODULE:sync(Fd2), 1067 {error, einval} = ?FILE_MODULE:truncate(Fd2), 1068 {error, einval} = ?FILE_MODULE:write(Fd2, "einval please"), 1069 1070 [] = flush(), 1071 ok. 1072 1073access(Config) when is_list(Config) -> 1074 RootDir = proplists:get_value(priv_dir,Config), 1075 Name = filename:join(RootDir, 1076 atom_to_list(?MODULE) 1077 ++"_access.fil"), 1078 Str = "ABCDEFGH", 1079 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1080 io:format(Fd1,Str,[]), 1081 ok = ?FILE_MODULE:close(Fd1), 1082 %% Check that we can't write when in read only mode 1083 {ok,Fd2} = ?FILE_MODULE:open(Name,read), 1084 case catch io:format(Fd2,"XXXX",[]) of 1085 ok -> 1086 ct:fail({format,write}); 1087 _ -> 1088 ok 1089 end, 1090 ok = ?FILE_MODULE:close(Fd2), 1091 {ok,Fd3} = ?FILE_MODULE:open(Name,read), 1092 Str = io:get_line(Fd3,''), 1093 ok = ?FILE_MODULE:close(Fd3), 1094 1095 [] = flush(), 1096 ok. 1097 1098%% Tests ?FILE_MODULE:read/2 and ?FILE_MODULE:write/2. 1099 1100read_write(Config) when is_list(Config) -> 1101 RootDir = proplists:get_value(priv_dir, Config), 1102 NewDir = filename:join(RootDir, 1103 atom_to_list(?MODULE) 1104 ++"_read_write"), 1105 ok = ?FILE_MODULE:make_dir(NewDir), 1106 Marker = "hello, world", 1107 MarkerB = list_to_binary(Marker), 1108 1109 %% Plain file. 1110 Name1 = filename:join(NewDir, "plain.fil"), 1111 {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]), 1112 read_write_test(Fd1, Marker, []), 1113 1114 %% Raw file. 1115 Name2 = filename:join(NewDir, "raw.fil"), 1116 {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]), 1117 read_write_test(Fd2, Marker, []), 1118 1119 %% Plain binary file. 1120 Name3 = filename:join(NewDir, "plain-b.fil"), 1121 {ok, Fd3} = ?FILE_MODULE:open(Name3, [read, write, binary]), 1122 read_write_test(Fd3, MarkerB, <<>>), 1123 1124 %% Raw binary file. 1125 Name4 = filename:join(NewDir, "raw-b.fil"), 1126 {ok, Fd4} = ?FILE_MODULE:open(Name4, [read, write, raw, binary]), 1127 read_write_test(Fd4, MarkerB, <<>>), 1128 1129 ok. 1130 1131read_write_test(File, Marker, Empty) -> 1132 ok = ?FILE_MODULE:write(File, Marker), 1133 {ok, 0} = ?FILE_MODULE:position(File, 0), 1134 {ok, Empty} = ?FILE_MODULE:read(File, 0), 1135 {ok, Marker} = ?FILE_MODULE:read(File, 100), 1136 eof = ?FILE_MODULE:read(File, 100), 1137 {ok, Empty} = ?FILE_MODULE:read(File, 0), 1138 ok = ?FILE_MODULE:close(File), 1139 [] = flush(), 1140 ok. 1141 1142 1143%% Tests ?FILE_MODULE:pread/2 and ?FILE_MODULE:pwrite/2. 1144 1145pread_write(Config) when is_list(Config) -> 1146 RootDir = proplists:get_value(priv_dir, Config), 1147 NewDir = filename:join(RootDir, 1148 atom_to_list(?MODULE) 1149 ++"_pread_write"), 1150 ok = ?FILE_MODULE:make_dir(NewDir), 1151 List = "hello, world", 1152 Bin = list_to_binary(List), 1153 1154 %% Plain file. 1155 Name1 = filename:join(NewDir, "plain.fil"), 1156 {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]), 1157 pread_write_test(Fd1, List), 1158 1159 %% Raw file. 1160 Name2 = filename:join(NewDir, "raw.fil"), 1161 {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]), 1162 pread_write_test(Fd2, List), 1163 1164 %% Plain file. Binary mode. 1165 Name3 = filename:join(NewDir, "plain-binary.fil"), 1166 {ok, Fd3} = ?FILE_MODULE:open(Name3, [binary, read, write]), 1167 pread_write_test(Fd3, Bin), 1168 1169 %% Raw file. Binary mode. 1170 Name4 = filename:join(NewDir, "raw-binary.fil"), 1171 {ok, Fd4} = ?FILE_MODULE:open(Name4, [binary, read, write, raw]), 1172 pread_write_test(Fd4, Bin), 1173 1174 ok. 1175 1176pread_write_test(File, Data) -> 1177 io:format("~p:pread_write_test(~p,~p)~n", [?MODULE, File, Data]), 1178 Size = if is_binary(Data) -> byte_size(Data); 1179 is_list(Data) -> length(Data) 1180 end, 1181 I = Size + 17, 1182 ok = ?FILE_MODULE:pwrite(File, 0, Data), 1183 {ok, Data} = ?FILE_MODULE:pread(File, 0, I), 1184 {ok, [Data]} = ?FILE_MODULE:pread(File, [{0, I}]), 1185 eof = ?FILE_MODULE:pread(File, I, 1), 1186 ok = ?FILE_MODULE:pwrite(File, [{0, Data}, {I, Data}]), 1187 {ok, [Data, eof, Data]} = 1188 ?FILE_MODULE:pread(File, [{0, Size}, {2*I, 1}, {I, Size}]), 1189 Plist = lists:seq(21*I, 0, -I), 1190 Pwrite = lists:map(fun(P)->{P,Data}end, Plist), 1191 Pread = [{22*I,Size} | lists:map(fun(P)->{P,Size}end, Plist)], 1192 Presult = [eof | lists:map(fun(_)->Data end, Plist)], 1193 ok = ?FILE_MODULE:pwrite(File, Pwrite), 1194 {ok, Presult} = ?FILE_MODULE:pread(File, Pread), 1195 ok = ?FILE_MODULE:close(File), 1196 [] = flush(), 1197 ok. 1198 1199%% Test appending to a file. 1200append(Config) when is_list(Config) -> 1201 RootDir = proplists:get_value(priv_dir, Config), 1202 NewDir = filename:join(RootDir, 1203 atom_to_list(?MODULE) 1204 ++"_append"), 1205 ok = ?FILE_MODULE:make_dir(NewDir), 1206 1207 First = "First line\n", 1208 Second = "Seond lines comes here\n", 1209 Third = "And here is the third line\n", 1210 1211 %% Write a small text file. 1212 Name1 = filename:join(NewDir, "a_file.txt"), 1213 {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]), 1214 ok = io:format(Fd1, First, []), 1215 ok = io:format(Fd1, Second, []), 1216 ok = ?FILE_MODULE:close(Fd1), 1217 1218 %% Open it a again and a append a line to it. 1219 {ok, Fd2} = ?FILE_MODULE:open(Name1, [append]), 1220 ok = io:format(Fd2, Third, []), 1221 ok = ?FILE_MODULE:close(Fd2), 1222 1223 %% Read it back and verify. 1224 Expected = list_to_binary([First, Second, Third]), 1225 {ok, Expected} = ?FILE_MODULE:read_file(Name1), 1226 1227 [] = flush(), 1228 ok. 1229 1230open_errors(Config) when is_list(Config) -> 1231 DataDir = 1232 filename:dirname( 1233 filename:join(proplists:get_value(data_dir, Config), "x")), 1234 DataDirSlash = DataDir++"/", 1235 {error, E1} = ?FILE_MODULE:open(DataDir, [read]), 1236 {error, E2} = ?FILE_MODULE:open(DataDirSlash, [read]), 1237 {error, E3} = ?FILE_MODULE:open(DataDir, [write]), 1238 {error, E4} = ?FILE_MODULE:open(DataDirSlash, [write]), 1239 {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4}, 1240 1241 [] = flush(), 1242 ok. 1243 1244%% Test exclusive access to a file. 1245exclusive(Config) when is_list(Config) -> 1246 RootDir = proplists:get_value(priv_dir,Config), 1247 NewDir = filename:join(RootDir, 1248 atom_to_list(?MODULE) 1249 ++"_exclusive"), 1250 ok = ?FILE_MODULE:make_dir(NewDir), 1251 Name = filename:join(NewDir, "ex_file.txt"), 1252 {ok, Fd} = ?FILE_MODULE:open(Name, [write, exclusive]), 1253 {error, eexist} = ?FILE_MODULE:open(Name, [write, exclusive]), 1254 ok = ?FILE_MODULE:close(Fd), 1255 ok. 1256 1257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1258 1259 1260pos1(Config) when is_list(Config) -> 1261 RootDir = proplists:get_value(priv_dir,Config), 1262 Name = filename:join(RootDir, 1263 atom_to_list(?MODULE) 1264 ++"_pos1.fil"), 1265 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1266 io:format(Fd1,"ABCDEFGH",[]), 1267 ok = ?FILE_MODULE:close(Fd1), 1268 {ok,Fd2} = ?FILE_MODULE:open(Name,read), 1269 1270 %% Start pos is first char 1271 io:format("Relative positions"), 1272 "A" = io:get_chars(Fd2,'',1), 1273 {ok,2} = ?FILE_MODULE:position(Fd2,{cur,1}), 1274 "C" = io:get_chars(Fd2,'',1), 1275 {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-3}), 1276 "A" = io:get_chars(Fd2,'',1), 1277 %% Backwards from first char should be an error 1278 {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-1}), 1279 {error, einval} = ?FILE_MODULE:position(Fd2,{cur,-1}), 1280 %% Reset position and move again 1281 {ok,0} = ?FILE_MODULE:position(Fd2,0), 1282 {ok,2} = ?FILE_MODULE:position(Fd2,{cur,2}), 1283 "C" = io:get_chars(Fd2,'',1), 1284 %% Go a lot forwards 1285 {ok,13} = ?FILE_MODULE:position(Fd2,{cur,10}), 1286 eof = io:get_chars(Fd2,'',1), 1287 1288 %% Try some fixed positions 1289 io:format("Fixed positions"), 1290 {ok,8} = 1291 ?FILE_MODULE:position(Fd2,8), eof = io:get_chars(Fd2,'',1), 1292 {ok,8} = 1293 ?FILE_MODULE:position(Fd2,cur), eof = io:get_chars(Fd2,'',1), 1294 {ok,7} = 1295 ?FILE_MODULE:position(Fd2,7), "H" = io:get_chars(Fd2,'',1), 1296 {ok,0} = 1297 ?FILE_MODULE:position(Fd2,0), "A" = io:get_chars(Fd2,'',1), 1298 {ok,3} = 1299 ?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1), 1300 {ok,12} = 1301 ?FILE_MODULE:position(Fd2,12), eof = io:get_chars(Fd2,'',1), 1302 {ok,3} = 1303 ?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1), 1304 %% Try the {bof,X} notation 1305 {ok,3} = ?FILE_MODULE:position(Fd2,{bof,3}), 1306 "D" = io:get_chars(Fd2,'',1), 1307 1308 %% Try eof positions 1309 io:format("EOF positions"), 1310 {ok,8} = 1311 ?FILE_MODULE:position(Fd2,{eof,0}), eof=io:get_chars(Fd2,'',1), 1312 {ok,7} = 1313 ?FILE_MODULE:position(Fd2,{eof,-1}), 1314 "H" = io:get_chars(Fd2,'',1), 1315 {ok,0} = 1316 ?FILE_MODULE:position(Fd2,{eof,-8}), "A"=io:get_chars(Fd2,'',1), 1317 {error, einval} = ?FILE_MODULE:position(Fd2,{eof,-9}), 1318 1319 [] = flush(), 1320 ok. 1321 1322pos2(Config) when is_list(Config) -> 1323 RootDir = proplists:get_value(priv_dir,Config), 1324 Name = filename:join(RootDir, 1325 atom_to_list(?MODULE) 1326 ++"_pos2.fil"), 1327 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1328 io:format(Fd1,"ABCDEFGH",[]), 1329 ok = ?FILE_MODULE:close(Fd1), 1330 {ok,Fd2} = ?FILE_MODULE:open(Name,read), 1331 {error, einval} = ?FILE_MODULE:position(Fd2,-1), 1332 1333 %% Make sure that we still can search after an error. 1334 {ok,0} = ?FILE_MODULE:position(Fd2, 0), 1335 {ok,3} = ?FILE_MODULE:position(Fd2, {bof,3}), 1336 "D" = io:get_chars(Fd2,'',1), 1337 1338 [] = flush(), 1339 io:format("DONE"), 1340 ok. 1341 1342%% When it does not use raw mode, file:position had a bug. 1343pos3(Config) when is_list(Config) -> 1344 RootDir = proplists:get_value(data_dir, Config), 1345 Name = filename:join(RootDir, "realmen.html.gz"), 1346 1347 {ok, Fd} = ?FILE_MODULE:open(Name, [read, binary]), 1348 {ok, _} = ?FILE_MODULE:read(Fd, 5), 1349 {error, einval} = ?FILE_MODULE:position(Fd, {bof, -1}), 1350 1351 %% Here ok had returned =( 1352 {error, einval} = ?FILE_MODULE:position(Fd, {cur, -10}), 1353 %% That test is actually questionable since file:position/2 1354 %% is documented to leave the file position undefined after 1355 %% it has returned an error. But on Posix systems the position 1356 %% is guaranteed to be unchanged after an error return. On e.g 1357 %% Windows there is nothing stated about this in the documentation. 1358 1359 ok. 1360 1361file_info_basic_file(Config) when is_list(Config) -> 1362 RootDir = proplists:get_value(priv_dir, Config), 1363 1364 %% Create a short file. 1365 Name = filename:join(RootDir, 1366 atom_to_list(?MODULE) 1367 ++"_basic_test.fil"), 1368 {ok,Fd1} = ?FILE_MODULE:open(Name, write), 1369 io:put_chars(Fd1, "foo bar"), 1370 ok = ?FILE_MODULE:close(Fd1), 1371 1372 %% Don't crash the file server when passing incorrect arguments. 1373 {error,badarg} = ?FILE_MODULE:read_file_info(Name, [{time, gurka}]), 1374 {error,badarg} = ?FILE_MODULE:read_file_info([#{} | gaffel]), 1375 1376 %% Test that the file has the expected attributes. 1377 %% The times are tricky, so we will save them to a separate test case. 1378 {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name), 1379 {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]), 1380 #file_info{size=Size,type=Type,access=Access, 1381 atime=AccessTime,mtime=ModifyTime} = FileInfo, 1382 io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]), 1383 Size = 7, 1384 Type = regular, 1385 read_write = Access, 1386 true = abs(time_dist(filter_atime(AccessTime, Config), 1387 filter_atime(ModifyTime, 1388 Config))) < 2, 1389 all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), 1390 1391 [] = flush(), 1392 ok. 1393 1394file_info_basic_directory(Config) when is_list(Config) -> 1395 %% Note: filename:join/1 removes any trailing slash, 1396 %% which is essential for ?FILE_MODULE:file_info/1 to work on 1397 %% platforms such as Windows95. 1398 RootDir = filename:join([proplists:get_value(priv_dir, Config)]), 1399 1400 %% Test that the RootDir directory has the expected attributes. 1401 test_directory(RootDir, read_write), 1402 1403 %% Note that on Windows file systems, 1404 %% "/" or "c:/" are *NOT* directories. 1405 %% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were 1406 %% directories. 1407 case os:type() of 1408 {win32, _} -> 1409 test_directory("/", read_write), 1410 test_directory("c:/", read_write), 1411 test_directory("c:\\", read_write); 1412 _ -> 1413 test_directory("/", read) 1414 end, 1415 ok. 1416 1417test_directory(Name, ExpectedAccess) -> 1418 {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name), 1419 {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]), 1420 #file_info{size=Size,type=Type,access=Access, 1421 atime=AccessTime,mtime=ModifyTime} = FileInfo, 1422 io:format("Testing directory ~s", [Name]), 1423 io:format("Directory size is ~p", [Size]), 1424 io:format("Access ~p", [Access]), 1425 io:format("Access time ~p; Modify time~p", 1426 [AccessTime, ModifyTime]), 1427 Type = directory, 1428 Access = ExpectedAccess, 1429 all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), 1430 [] = flush(), 1431 ok. 1432 1433all_integers([{A,B,C}|T]) -> 1434 all_integers([A,B,C|T]); 1435all_integers([Int|Rest]) when is_integer(Int) -> 1436 all_integers(Rest); 1437all_integers([]) -> ok. 1438 1439%% Try something nonexistent. 1440 1441file_info_bad(Config) when is_list(Config) -> 1442 RootDir = filename:join([proplists:get_value(priv_dir, Config)]), 1443 FileName = filename:join(RootDir, atom_to_list(?MODULE) ++ "_nonexistent"), 1444 {error,enoent} = ?FILE_MODULE:read_file_info(FileName), 1445 {error,enoent} = ?FILE_MODULE:read_file_info(FileName, [raw]), 1446 {error, enoent} = ?FILE_MODULE:read_file_info(""), 1447 {error, enoent} = ?FILE_MODULE:read_file_info("", [raw]), 1448 [] = flush(), 1449 ok. 1450 1451%% Test that the file times behave as they should. 1452 1453file_info_times(Config) when is_list(Config) -> 1454 %% We have to try this twice, since if the test runs across the change 1455 %% of a month the time diff calculations will fail. But it won't happen 1456 %% if you run it twice in succession. 1457 test_server:m_out_of_n( 1458 1,2, 1459 fun() -> file_info_int(Config) end), 1460 ok. 1461 1462file_info_int(Config) -> 1463 %% Note: filename:join/1 removes any trailing slash, 1464 %% which is essential for ?FILE_MODULE:file_info/1 to work on 1465 %% platforms such as Windows95. 1466 1467 RootDir = filename:join([proplists:get_value(priv_dir, Config)]), 1468 io:format("RootDir = ~p", [RootDir]), 1469 1470 Name = filename:join(RootDir, 1471 atom_to_list(?MODULE) 1472 ++"_file_info.fil"), 1473 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1474 io:put_chars(Fd1,"foo"), 1475 1476 %% check that the file got a modify date max a few seconds away from now 1477 {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Name), 1478 {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Name, [raw]), 1479 1480 %% We assert that everything but the size is the same, on some OSs the 1481 %% size may not have been flushed to disc and we do not want to do a 1482 %% sync to force it. 1483 FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size }, 1484 1485 #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1, 1486 1487 Now = erlang:localtime(), %??? 1488 io:format("Now ~p",[Now]), 1489 io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), 1490 true = abs(time_dist(filter_atime(Now, Config), 1491 filter_atime(AccTime1, 1492 Config))) < 8, 1493 true = abs(time_dist(Now,ModTime1)) < 8, 1494 1495 %% Sleep until we can be sure the seconds value has changed. 1496 %% Note: FAT-based filesystem (like on Windows 95) have 1497 %% a resolution of 2 seconds. 1498 timer:sleep(2200), 1499 1500 %% close the file, and watch the modify date change 1501 ok = ?FILE_MODULE:close(Fd1), 1502 {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name), 1503 {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name, [raw]), 1504 #file_info{size=Size,type=regular,access=Access, 1505 atime=AccTime2,mtime=ModTime2} = FileInfo2, 1506 io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), 1507 true = time_dist(ModTime1,ModTime2) >= 0, 1508 1509 %% this file is supposed to be binary, so it'd better keep it's size 1510 Size = 3, 1511 Access = read_write, 1512 1513 %% Do some directory checking 1514 {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir), 1515 {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir, [raw]), 1516 #file_info{size=DSize,type=directory,access=DAccess, 1517 atime=AccTime3,mtime=ModTime3} = FileInfo3, 1518 %% this dir was modified only a few secs ago 1519 io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]), 1520 true = abs(time_dist(Now,ModTime3)) < 5, 1521 DAccess = read_write, 1522 io:format("Dir size is ~p",[DSize]), 1523 1524 [] = flush(), 1525 ok. 1526 1527%% Filter access times, to copy with a deficiency of FAT file systems 1528%% (on Windows): The access time is actually only a date. 1529 1530filter_atime(Atime, Config) -> 1531 case lists:member(no_access_time, Config) of 1532 true -> 1533 case Atime of 1534 {Date, _} -> 1535 {Date, {0, 0, 0}}; 1536 {Y, M, D, _, _, _} -> 1537 {Y, M, D, 0, 0, 0} 1538 end; 1539 false -> 1540 Atime 1541 end. 1542 1543%% Test the write_file_info/2 function. 1544 1545file_write_file_info(Config) when is_list(Config) -> 1546 RootDir = get_good_directory(Config), 1547 io:format("RootDir = ~p", [RootDir]), 1548 1549 %% Set the file to read only AND update the file times at the same time. 1550 %% (This used to fail on Windows NT/95 for a local filesystem.) 1551 %% Note: Seconds must be even; see note in file_info_times/1. 1552 1553 Name1 = filename:join(RootDir, 1554 atom_to_list(?MODULE) 1555 ++"_write_file_info_ro"), 1556 ok = ?FILE_MODULE:write_file(Name1, "hello"), 1557 Time = {{1997, 01, 02}, {12, 35, 42}}, 1558 Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time}, 1559 ok = ?FILE_MODULE:write_file_info(Name1, Info), 1560 1561 %% Read back the times. 1562 1563 {ok, ActualInfo} = ?FILE_MODULE:read_file_info(Name1), 1564 #file_info{mode=_Mode, atime=ActAtime, mtime=Time, 1565 ctime=ActCtime} = ActualInfo, 1566 FilteredAtime = filter_atime(Time, Config), 1567 FilteredAtime = filter_atime(ActAtime, Config), 1568 case os:type() of 1569 {win32, _} -> 1570 %% On Windows, "ctime" means creation time and it can 1571 %% be set. 1572 ActCtime = Time; 1573 _ -> 1574 ok 1575 end, 1576 {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), 1577 1578 %% Make the file writable again. 1579 1580 ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}), 1581 ok = ?FILE_MODULE:write_file(Name1, "hello again"), 1582 1583 %% And unwritable. 1584 ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}), 1585 {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), 1586 1587 %% Same with raw. 1588 ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}, [raw]), 1589 ok = ?FILE_MODULE:write_file(Name1, "hello again"), 1590 ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}, [raw]), 1591 {error,eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), 1592 1593 %% Write the times again. 1594 %% Note: Seconds must be even; see note in file_info_times/1. 1595 1596 NewTime = {{1997, 02, 15}, {13, 18, 20}}, 1597 NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime}, 1598 ok = ?FILE_MODULE:write_file_info(Name1, NewInfo), 1599 {ok, ActualInfo2} = ?FILE_MODULE:read_file_info(Name1), 1600 #file_info{atime=NewActAtime, mtime=NewTime, 1601 ctime=NewActCtime} = ActualInfo2, 1602 NewFilteredAtime = filter_atime(NewTime, Config), 1603 NewFilteredAtime = filter_atime(NewActAtime, Config), 1604 case os:type() of 1605 {win32, _} -> NewActCtime = NewTime; 1606 _ -> ok 1607 end, 1608 1609 %% The file should still be unwritable. 1610 {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), 1611 1612 %% Make the file writeable again, so that we can remove the 1613 %% test suites ... :-) 1614 ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}), 1615 1616 [] = flush(), 1617 ok. 1618 1619file_wfi_helpers(Config) when is_list(Config) -> 1620 RootDir = get_good_directory(Config), 1621 io:format("RootDir = ~p", [RootDir]), 1622 1623 Name = filename:join(RootDir, 1624 atom_to_list(?MODULE) ++ "_wfi_helpers"), 1625 1626 ok = ?FILE_MODULE:write_file(Name, "hello again"), 1627 NewTime = {{1997, 02, 15}, {13, 18, 20}}, 1628 ok = ?FILE_MODULE:change_time(Name, NewTime, NewTime), 1629 1630 {ok, #file_info{atime=NewActAtime, mtime=NewTime}} = 1631 ?FILE_MODULE:read_file_info(Name), 1632 1633 NewFilteredAtime = filter_atime(NewTime, Config), 1634 NewFilteredAtime = filter_atime(NewActAtime, Config), 1635 1636 %% Make the file unwritable 1637 ok = ?FILE_MODULE:change_mode(Name, 8#400), 1638 {error, eacces} = ?FILE_MODULE:write_file(Name, "hello again"), 1639 1640 %% ... and writable again 1641 ok = ?FILE_MODULE:change_mode(Name, 8#600), 1642 ok = ?FILE_MODULE:write_file(Name, "hello again"), 1643 1644 %% We have no idea which users will work, so all we can do is to check 1645 %% that it returns enoent instead of crashing. 1646 {error, enoent} = ?FILE_MODULE:change_group("bogus file name", 0), 1647 {error, enoent} = ?FILE_MODULE:change_owner("bogus file name", 0), 1648 1649 [] = flush(), 1650 ok. 1651 1652%% Returns a directory on a file system that has correct file times. 1653 1654get_good_directory(Config) -> 1655 proplists:get_value(priv_dir, Config). 1656 1657 1658consult1(Config) when is_list(Config) -> 1659 RootDir = proplists:get_value(priv_dir,Config), 1660 Name = filename:join(RootDir, 1661 atom_to_list(?MODULE) 1662 ++"_consult.fil"), 1663 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1664 %% note that there is no final \n (only a space) 1665 io:format(Fd1, 1666 "{this,[is,1.0],'journey'}.\n\"into\". (sound). ", 1667 []), 1668 ok = ?FILE_MODULE:close(Fd1), 1669 {ok,[{this,[is,1.0],journey},"into",sound]} = 1670 ?FILE_MODULE:consult(Name), 1671 1672 {ok,Fd2} = ?FILE_MODULE:open(Name,write), 1673 %% note the missing double quote 1674 io:format( 1675 Fd2,"{this,[is,1.0],'journey'}.\n \"into. (sound). ",[]), 1676 ok = ?FILE_MODULE:close(Fd2), 1677 {error, {_, _, _} = Msg} = ?FILE_MODULE:consult(Name), 1678 io:format("Errmsg: ~p",[Msg]), 1679 1680 {error, enoent} = ?FILE_MODULE:consult(Name ++ ".nonexistent"), 1681 1682 [] = flush(), 1683 ok. 1684 1685path_consult(Config) when is_list(Config) -> 1686 RootDir = proplists:get_value(priv_dir,Config), 1687 FileName = atom_to_list(?MODULE)++"_path_consult.fil", 1688 Name = filename:join(RootDir, FileName), 1689 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1690 io:format(Fd1,"{this,is,a,journey,into,sound}.\n",[]), 1691 ok = ?FILE_MODULE:close(Fd1), 1692 %% File last in path 1693 {ok,[{this,is,a,journey,into,sound}],Dir} = 1694 ?FILE_MODULE:path_consult( 1695 [filename:join(RootDir, "dir1"), 1696 filename:join(RootDir, ".."), 1697 filename:join(RootDir, "dir2"), 1698 RootDir], FileName), 1699 true = lists:prefix(RootDir,Dir), 1700 1701 %% While maybe not an error, it may be worth noting that 1702 %% when the full path to a file is given, it's always found 1703 %% regardless of the contents of the path 1704 {ok,_,_} = ?FILE_MODULE:path_consult(["nosuch1","nosuch2"],Name), 1705 1706 [] = flush(), 1707 ok. 1708 1709 1710eval1(Config) when is_list(Config) -> 1711 RootDir = proplists:get_value(priv_dir,Config), 1712 Name = filename:join(RootDir, 1713 atom_to_list(?MODULE)++"_eval.fil"), 1714 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1715 %% note that there is no final \n (only a space) 1716 io:format(Fd1,"put(evaluated_ok,\ntrue). ",[]), 1717 ok = ?FILE_MODULE:close(Fd1), 1718 ok = ?FILE_MODULE:eval(Name), 1719 true = get(evaluated_ok), 1720 1721 {ok,Fd2} = ?FILE_MODULE:open(Name,write), 1722 %% note that there is no final \n (only a space) 1723 io:format(Fd2,"put(evaluated_ok,\nR). ",[]), 1724 ok = ?FILE_MODULE:close(Fd2), 1725 ok = ?FILE_MODULE:eval( 1726 Name, 1727 erl_eval:add_binding('R', true, erl_eval:new_bindings())), 1728 true = get(evaluated_ok), 1729 1730 {ok,Fd3} = ?FILE_MODULE:open(Name,write), 1731 %% garbled 1732 io:format(Fd3,"puGARBLED-GARBLED\ntrue). ",[]), 1733 ok = ?FILE_MODULE:close(Fd3), 1734 {error, {_, _, _} = Msg} = ?FILE_MODULE:eval(Name), 1735 io:format("Errmsg1: ~p",[Msg]), 1736 1737 {error, enoent} = ?FILE_MODULE:eval(Name ++ ".nonexistent"), 1738 1739 [] = flush(), 1740 ok. 1741 1742path_eval(Config) when is_list(Config) -> 1743 RootDir = proplists:get_value(priv_dir,Config), 1744 FileName = atom_to_list(?MODULE)++"_path_eval.fil", 1745 Name = filename:join(RootDir, FileName), 1746 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1747 io:format(Fd1,"put(evaluated_ok,true).\n",[]), 1748 ok = ?FILE_MODULE:close(Fd1), 1749 %% File last in path 1750 {ok,Dir} = 1751 ?FILE_MODULE:path_eval( 1752 [filename:join(RootDir, "dir1"), 1753 filename:join(RootDir, ".."), 1754 filename:join(RootDir, "dir2"), 1755 RootDir],FileName), 1756 true = get(evaluated_ok), 1757 true = lists:prefix(RootDir,Dir), 1758 1759 %% While maybe not an error, it may be worth noting that 1760 %% when the full path to a file is given, it's always found 1761 %% regardless of the contents of the path 1762 {ok,Fd2} = ?FILE_MODULE:open(Name,write), 1763 io:format(Fd2,"put(evaluated_ok,R).\n",[]), 1764 ok = ?FILE_MODULE:close(Fd2), 1765 {ok,_} = ?FILE_MODULE:path_eval( 1766 ["nosuch1","nosuch2"], 1767 Name, 1768 erl_eval:add_binding('R', true, erl_eval:new_bindings())), 1769 1770 [] = flush(), 1771 ok. 1772 1773 1774script1(Config) when is_list(Config) -> 1775 RootDir = proplists:get_value(priv_dir,Config), 1776 Name = filename:join(RootDir, 1777 atom_to_list(?MODULE)++"_script.fil"), 1778 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1779 %% note that there is no final \n (only a space) 1780 io:format(Fd1,"A = 11,\nB = 6,\nA+B. ",[]), 1781 ok = ?FILE_MODULE:close(Fd1), 1782 {ok,17} = ?FILE_MODULE:script(Name), 1783 1784 {ok,Fd2} = ?FILE_MODULE:open(Name,write), 1785 %% note that there is no final \n (only a space) 1786 io:format(Fd2,"A = 11,\nA+B. ",[]), 1787 ok = ?FILE_MODULE:close(Fd2), 1788 {ok,17} = ?FILE_MODULE:script( 1789 Name, 1790 erl_eval:add_binding('B', 6, erl_eval:new_bindings())), 1791 1792 {ok,Fd3} = ?FILE_MODULE:open(Name,write), 1793 io:format(Fd3,"A = 11,\nB = six,\nA+B. ",[]), 1794 ok = ?FILE_MODULE:close(Fd3), 1795 {error, {_, _, _} = Msg} = ?FILE_MODULE:script(Name), 1796 io:format("Errmsg1: ~p",[Msg]), 1797 1798 {error, enoent} = ?FILE_MODULE:script(Name ++ ".nonexistent"), 1799 1800 [] = flush(), 1801 ok. 1802 1803path_script(Config) when is_list(Config) -> 1804 RootDir = proplists:get_value(priv_dir,Config), 1805 FileName = atom_to_list(?MODULE)++"_path_script.fil", 1806 Name = filename:join(RootDir, FileName), 1807 {ok,Fd1} = ?FILE_MODULE:open(Name,write), 1808 io:format(Fd1,"A = 11,\nB = 6,\nA+B.\n",[]), 1809 ok = ?FILE_MODULE:close(Fd1), 1810 %% File last in path 1811 {ok, 17, Dir} = 1812 ?FILE_MODULE:path_script( 1813 [filename:join(RootDir, "dir1"), 1814 filename:join(RootDir, ".."), 1815 filename:join(RootDir, "dir2"), 1816 RootDir],FileName), 1817 true = lists:prefix(RootDir,Dir), 1818 1819 %% While maybe not an error, it may be worth noting that 1820 %% when the full path to a file is given, it's always found 1821 %% regardless of the contents of the path 1822 {ok,Fd2} = ?FILE_MODULE:open(Name,write), 1823 io:format(Fd2,"A = 11,\nA+B.",[]), 1824 ok = ?FILE_MODULE:close(Fd2), 1825 {ok, 17, Dir} = 1826 ?FILE_MODULE:path_script( 1827 ["nosuch1","nosuch2"], 1828 Name, 1829 erl_eval:add_binding('B', 6, erl_eval:new_bindings())), 1830 1831 [] = flush(), 1832 ok. 1833 1834 1835 1836truncate(Config) when is_list(Config) -> 1837 RootDir = proplists:get_value(priv_dir,Config), 1838 Name = filename:join(RootDir, 1839 atom_to_list(?MODULE) 1840 ++"_truncate.fil"), 1841 1842 %% Create a file with some data. 1843 MyData = "0123456789abcdefghijklmnopqrstuvxyz", 1844 ok = ?FILE_MODULE:write_file(Name, MyData), 1845 1846 %% Truncate the file to 10 characters. 1847 {ok, Fd} = ?FILE_MODULE:open(Name, read_write), 1848 {ok, 10} = ?FILE_MODULE:position(Fd, 10), 1849 ok = ?FILE_MODULE:truncate(Fd), 1850 ok = ?FILE_MODULE:close(Fd), 1851 1852 %% Read back the file and check that it has been truncated. 1853 Expected = list_to_binary("0123456789"), 1854 {ok, Expected} = ?FILE_MODULE:read_file(Name), 1855 1856 %% Open the file read only and verify that it is not possible to 1857 %% truncate it, OTP-1960 1858 {ok, Fd2} = ?FILE_MODULE:open(Name, read), 1859 {ok, 5} = ?FILE_MODULE:position(Fd2, 5), 1860 {error, _} = ?FILE_MODULE:truncate(Fd2), 1861 1862 [] = flush(), 1863 ok. 1864 1865 1866%% Tests that ?FILE_MODULE:datasync/1 at least doesn't crash. 1867datasync(Config) when is_list(Config) -> 1868 PrivDir = proplists:get_value(priv_dir, Config), 1869 Sync = filename:join(PrivDir, 1870 atom_to_list(?MODULE) 1871 ++"_sync.fil"), 1872 1873 %% Raw open. 1874 {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]), 1875 ok = ?FILE_MODULE:datasync(Fd), 1876 ok = ?FILE_MODULE:close(Fd), 1877 1878 %% Ordinary open. 1879 {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]), 1880 ok = ?FILE_MODULE:datasync(Fd2), 1881 ok = ?FILE_MODULE:close(Fd2), 1882 1883 [] = flush(), 1884 ok. 1885 1886 1887%% Tests that ?FILE_MODULE:sync/1 at least doesn't crash. 1888sync(Config) when is_list(Config) -> 1889 PrivDir = proplists:get_value(priv_dir, Config), 1890 Sync = filename:join(PrivDir, 1891 atom_to_list(?MODULE) 1892 ++"_sync.fil"), 1893 1894 %% Raw open. 1895 {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]), 1896 ok = ?FILE_MODULE:sync(Fd), 1897 ok = ?FILE_MODULE:close(Fd), 1898 1899 %% Ordinary open. 1900 {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]), 1901 ok = ?FILE_MODULE:sync(Fd2), 1902 ok = ?FILE_MODULE:close(Fd2), 1903 1904 [] = flush(), 1905 ok. 1906 1907%% Tests that ?FILE_MODULE:advise/4 at least doesn't crash. 1908advise(Config) when is_list(Config) -> 1909 PrivDir = proplists:get_value(priv_dir, Config), 1910 Advise = filename:join(PrivDir, 1911 atom_to_list(?MODULE) 1912 ++"_advise.fil"), 1913 1914 Line1 = "Hello\n", 1915 Line2 = "World!\n", 1916 1917 {ok, Fd} = ?FILE_MODULE:open(Advise, [write]), 1918 ok = ?FILE_MODULE:advise(Fd, 0, 0, normal), 1919 ok = io:format(Fd, "~s", [Line1]), 1920 ok = io:format(Fd, "~s", [Line2]), 1921 ok = ?FILE_MODULE:close(Fd), 1922 1923 {ok, Fd2} = ?FILE_MODULE:open(Advise, [write]), 1924 ok = ?FILE_MODULE:advise(Fd2, 0, 0, random), 1925 ok = io:format(Fd2, "~s", [Line1]), 1926 ok = io:format(Fd2, "~s", [Line2]), 1927 ok = ?FILE_MODULE:close(Fd2), 1928 1929 {ok, Fd3} = ?FILE_MODULE:open(Advise, [write]), 1930 ok = ?FILE_MODULE:advise(Fd3, 0, 0, sequential), 1931 ok = io:format(Fd3, "~s", [Line1]), 1932 ok = io:format(Fd3, "~s", [Line2]), 1933 ok = ?FILE_MODULE:close(Fd3), 1934 1935 {ok, Fd4} = ?FILE_MODULE:open(Advise, [write]), 1936 ok = ?FILE_MODULE:advise(Fd4, 0, 0, will_need), 1937 ok = io:format(Fd4, "~s", [Line1]), 1938 ok = io:format(Fd4, "~s", [Line2]), 1939 ok = ?FILE_MODULE:close(Fd4), 1940 1941 {ok, Fd5} = ?FILE_MODULE:open(Advise, [write]), 1942 ok = ?FILE_MODULE:advise(Fd5, 0, 0, dont_need), 1943 ok = io:format(Fd5, "~s", [Line1]), 1944 ok = io:format(Fd5, "~s", [Line2]), 1945 ok = ?FILE_MODULE:close(Fd5), 1946 1947 {ok, Fd6} = ?FILE_MODULE:open(Advise, [write]), 1948 ok = ?FILE_MODULE:advise(Fd6, 0, 0, no_reuse), 1949 ok = io:format(Fd6, "~s", [Line1]), 1950 ok = io:format(Fd6, "~s", [Line2]), 1951 ok = ?FILE_MODULE:close(Fd6), 1952 1953 {ok, Fd7} = ?FILE_MODULE:open(Advise, [write]), 1954 {error, einval} = ?FILE_MODULE:advise(Fd7, 0, 0, bad_advise), 1955 ok = ?FILE_MODULE:close(Fd7), 1956 1957 %% test write without advise, then a read after an advise 1958 {ok, Fd8} = ?FILE_MODULE:open(Advise, [write]), 1959 ok = io:format(Fd8, "~s", [Line1]), 1960 ok = io:format(Fd8, "~s", [Line2]), 1961 ok = ?FILE_MODULE:close(Fd8), 1962 {ok, Fd9} = ?FILE_MODULE:open(Advise, [read]), 1963 Offset = 0, 1964 %% same as a 0 length in some implementations 1965 Length = length(Line1) + length(Line2), 1966 ok = ?FILE_MODULE:advise(Fd9, Offset, Length, sequential), 1967 {ok, Line1} = ?FILE_MODULE:read_line(Fd9), 1968 {ok, Line2} = ?FILE_MODULE:read_line(Fd9), 1969 eof = ?FILE_MODULE:read_line(Fd9), 1970 ok = ?FILE_MODULE:close(Fd9), 1971 1972 [] = flush(), 1973 ok. 1974 1975%% Tests that ?FILE_MODULE:allocate/3 at least doesn't crash. 1976allocate(Config) when is_list(Config) -> 1977 PrivDir = proplists:get_value(priv_dir, Config), 1978 Allocate = filename:join(PrivDir, 1979 atom_to_list(?MODULE) 1980 ++"_allocate.fil"), 1981 1982 Line1 = "Hello\n", 1983 Line2 = "World!\n", 1984 1985 {ok, Fd} = ?FILE_MODULE:open(Allocate, [write, binary]), 1986 allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])), 1987 ok = io:format(Fd, "~s", [Line1]), 1988 ok = io:format(Fd, "~s", [Line2]), 1989 ok = ?FILE_MODULE:close(Fd), 1990 1991 {ok, Fd2} = ?FILE_MODULE:open(Allocate, [write, binary]), 1992 allocate_and_assert(Fd2, 1, iolist_size(Line1)), 1993 ok = io:format(Fd2, "~s", [Line1]), 1994 ok = io:format(Fd2, "~s", [Line2]), 1995 ok = ?FILE_MODULE:close(Fd2), 1996 1997 {ok, Fd3} = ?FILE_MODULE:open(Allocate, [write, binary]), 1998 allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1), 1999 ok = io:format(Fd3, "~s", [Line1]), 2000 ok = io:format(Fd3, "~s", [Line2]), 2001 ok = ?FILE_MODULE:close(Fd3), 2002 2003 {ok, Fd4} = ?FILE_MODULE:open(Allocate, [write, binary]), 2004 allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])), 2005 ok = io:format(Fd4, "~s", [Line1]), 2006 ok = io:format(Fd4, "~s", [Line2]), 2007 ok = ?FILE_MODULE:close(Fd4), 2008 2009 [] = flush(), 2010 ok. 2011 2012allocate_and_assert(Fd, Offset, Length) -> 2013 %% Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have 2014 %% any other negative side effect. We can't really asssert against a 2015 %% specific return value, because support for file space pre-allocation 2016 %% depends on the OS, OS version and underlying filesystem. 2017 %% 2018 %% The Linux kernel added support for fallocate() in version 2.6.23, 2019 %% which currently works only for the ext4, ocfs2, xfs and btrfs file 2020 %% systems. posix_fallocate() is available in glibc as of version 2021 %% 2.1.94, but it was buggy until glibc version 2.7. 2022 %% 2023 %% Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. 2024 %% 2025 %% Solaris supports posix_fallocate() but only for the UFS file system 2026 %% apparently (not supported for ZFS). 2027 %% 2028 %% FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). 2029 %% 2030 %% For Windows there's apparently no way to pre-allocate file space, at 2031 %% least with same semantics as posix_fallocate(), fallocate() and 2032 %% fcntl F_PREALLOCATE. 2033 Result = ?FILE_MODULE:allocate(Fd, Offset, Length), 2034 case os:type() of 2035 {win32, _} -> 2036 {error, enotsup} = Result; 2037 _ -> 2038 _ = Result 2039 end. 2040 2041%% Tests that asserts that file:allocate/3 changes file size 2042allocate_file_size(Config) when is_list(Config) -> 2043 case os:type() of 2044 {unix, darwin} -> 2045 PrivDir = proplists:get_value(priv_dir, Config), 2046 Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"), 2047 2048 {ok, Fd} = ?FILE_MODULE:open(Allocate, [write]), 2049 ok = ?FILE_MODULE:allocate(Fd, 0, 1024), 2050 {ok, 1024} = ?FILE_MODULE:position(Fd, eof), 2051 ok = ?FILE_MODULE:close(Fd), 2052 2053 [] = flush(), 2054 ok; 2055 {unix, linux} -> 2056 {skip, "file:allocate/3 on Linux does not change file size"}; 2057 {win32, _} -> 2058 {skip, "Windows does not support file:allocate/3"}; 2059 _ -> 2060 {skip, "Support for allocate/3 is spotty in our test platform at the moment."} 2061 end. 2062 2063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2064 2065delete(Config) when is_list(Config) -> 2066 RootDir = proplists:get_value(priv_dir,Config), 2067 Name = filename:join(RootDir, 2068 atom_to_list(?MODULE) 2069 ++"_delete.fil"), 2070 {ok, Fd1} = ?FILE_MODULE:open(Name, write), 2071 io:format(Fd1,"ok.\n",[]), 2072 ok = ?FILE_MODULE:close(Fd1), 2073 %% Check that the file is readable 2074 {ok, Fd2} = ?FILE_MODULE:open(Name, read), 2075 ok = ?FILE_MODULE:close(Fd2), 2076 ok = ?FILE_MODULE:delete(Name), 2077 %% Check that the file is not readable anymore 2078 {error, _} = ?FILE_MODULE:open(Name, read), 2079 %% Try deleting a nonexistent file 2080 {error, enoent} = ?FILE_MODULE:delete(Name), 2081 [] = flush(), 2082 ok. 2083 2084rename(Config) when is_list(Config) -> 2085 RootDir = proplists:get_value(priv_dir,Config), 2086 FileName1 = atom_to_list(?MODULE)++"_rename.fil", 2087 FileName2 = atom_to_list(?MODULE)++"_rename.ful", 2088 Name1 = filename:join(RootDir, FileName1), 2089 Name2 = filename:join(RootDir, FileName2), 2090 {ok,Fd1} = ?FILE_MODULE:open(Name1,write), 2091 ok = ?FILE_MODULE:close(Fd1), 2092 %% Rename, and check that id really changed name 2093 ok = ?FILE_MODULE:rename(Name1,Name2), 2094 {error, _} = ?FILE_MODULE:open(Name1,read), 2095 {ok,Fd2} = ?FILE_MODULE:open(Name2,read), 2096 ok = ?FILE_MODULE:close(Fd2), 2097 %% Try renaming something to itself 2098 ok = ?FILE_MODULE:rename(Name2,Name2), 2099 %% Try renaming something that doesn't exist 2100 {error, enoent} = ?FILE_MODULE:rename(Name1,Name2), 2101 %% Try renaming to something else than a string 2102 {error, badarg} = ?FILE_MODULE:rename(Name1,{foo,bar}), 2103 2104 %% Move between directories 2105 DirName1 = filename:join(RootDir, 2106 atom_to_list(?MODULE) 2107 ++"_rename_dir"), 2108 DirName2 = filename:join(RootDir, 2109 atom_to_list(?MODULE) 2110 ++"_second_rename_dir"), 2111 Name1foo = filename:join(DirName1, "foo.fil"), 2112 Name2foo = filename:join(DirName2, "foo.fil"), 2113 Name2bar = filename:join(DirName2, "bar.dir"), 2114 ok = ?FILE_MODULE:make_dir(DirName1), 2115 %% The name has to include the full file name, path in not enough 2116 expect({error, eisdir}, {error, eexist}, 2117 ?FILE_MODULE:rename(Name2,DirName1)), 2118 ok = ?FILE_MODULE:rename(Name2, Name1foo), 2119 %% Now rename the directory 2120 ok = ?FILE_MODULE:rename(DirName1,DirName2), 2121 %% And check that the file is there now 2122 {ok,Fd3} = ?FILE_MODULE:open(Name2foo, read), 2123 ok = ?FILE_MODULE:close(Fd3), 2124 %% Try some dirty things now: move the directory into itself 2125 {error, Msg1} = ?FILE_MODULE:rename(DirName2, Name2bar), 2126 io:format("Errmsg1: ~p",[Msg1]), 2127 %% move dir into a file in itself 2128 {error, Msg2} = ?FILE_MODULE:rename(DirName2, Name2foo), 2129 io:format("Errmsg2: ~p",[Msg2]), 2130 2131 [] = flush(), 2132 ok. 2133 2134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2135 2136names(Config) when is_list(Config) -> 2137 RootDir = proplists:get_value(priv_dir,Config), 2138 FileName = "foo1.fil", 2139 Name1 = filename:join(RootDir, FileName), 2140 Name2 = [RootDir,"/","foo1",".","fil"], 2141 Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il], 2142 {ok,Fd0} = ?FILE_MODULE:open(Name1,write), 2143 ok = ?FILE_MODULE:close(Fd0), 2144 2145 %% Try some file names 2146 {ok,Fd1} = ?FILE_MODULE:open(Name1,read), 2147 ok = ?FILE_MODULE:close(Fd1), 2148 {ok,Fd2f} = ?FILE_MODULE:open(lists:flatten(Name2),read), 2149 ok = ?FILE_MODULE:close(Fd2f), 2150 {ok,Fd2} = ?FILE_MODULE:open(Name2,read), 2151 ok = ?FILE_MODULE:close(Fd2), 2152 {ok,Fd3} = ?FILE_MODULE:open(Name3,read), 2153 ok = ?FILE_MODULE:close(Fd3), 2154 2155 %% Now try the same on raw files. 2156 {ok,Fd4} = ?FILE_MODULE:open(Name2, [read, raw]), 2157 ok = ?FILE_MODULE:close(Fd4), 2158 {ok,Fd4f} = ?FILE_MODULE:open(lists:flatten(Name2), [read, raw]), 2159 ok = ?FILE_MODULE:close(Fd4f), 2160 {ok,Fd5} = ?FILE_MODULE:open(Name3, [read, raw]), 2161 ok = ?FILE_MODULE:close(Fd5), 2162 2163 case length(Name1) > 255 of 2164 true -> 2165 io:format("Path too long for an atom:\n\n~p\n", [Name1]); 2166 false -> 2167 Name4 = list_to_atom(Name1), 2168 {ok,Fd6} = ?FILE_MODULE:open(Name4,read), 2169 ok = ?FILE_MODULE:close(Fd6) 2170 end, 2171 2172 %% Try some path names 2173 Path1 = RootDir, 2174 Path2 = [RootDir], 2175 Path3 = ['',[],[RootDir,[[]]]], 2176 {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read), 2177 ok = ?FILE_MODULE:close(Fd11), 2178 {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read), 2179 ok = ?FILE_MODULE:close(Fd12), 2180 {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read), 2181 ok = ?FILE_MODULE:close(Fd13), 2182 case length(Path1) > 255 of 2183 true-> 2184 io:format("Path too long for an atom:\n\n~p\n", [Path1]); 2185 false -> 2186 Path4 = list_to_atom(Path1), 2187 {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read), 2188 ok = ?FILE_MODULE:close(Fd14) 2189 end, 2190 [] = flush(), 2191 ok. 2192 2193volume_relative_paths(Config) when is_list(Config) -> 2194 case os:type() of 2195 {win32, _} -> 2196 {ok, [Drive, $: | _]} = file:get_cwd(), 2197 %% Relative to current device root. 2198 {ok, RootInfo} = file:read_file_info([Drive, $:, $/]), 2199 {ok, RootInfo} = file:read_file_info("/"), 2200 %% Relative to current device directory. 2201 {ok, DirContents} = file:list_dir([Drive, $:]), 2202 {ok, DirContents} = file:list_dir("."), 2203 [] = flush(), 2204 ok; 2205 _ -> 2206 {skip, "This test is Windows-specific."} 2207 end. 2208 2209unc_paths(Config) when is_list(Config) -> 2210 case os:type() of 2211 {win32, _} -> 2212 %% We assume administrative shares are set up and reachable, and we 2213 %% settle for testing presence as some of the returned data is 2214 %% different. 2215 {ok, _} = file:read_file_info("C:\\Windows\\explorer.exe"), 2216 {ok, _} = file:read_file_info("\\\\localhost\\c$\\Windows\\explorer.exe"), 2217 2218 {ok, Files} = file:list_dir("C:\\Windows\\"), 2219 {ok, Files} = file:list_dir("\\\\localhost\\c$\\Windows\\"), 2220 2221 {ok, Cwd} = file:get_cwd(), 2222 2223 try 2224 ok = file:set_cwd("\\\\localhost\\c$\\Windows\\"), 2225 {ok, _} = file:read_file_info("explorer.exe") 2226 after 2227 file:set_cwd(Cwd) 2228 end, 2229 2230 [] = flush(), 2231 ok; 2232 _ -> 2233 {skip, "This test is Windows-specific."} 2234 end. 2235 2236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2237 2238 2239e_delete(Config) when is_list(Config) -> 2240 RootDir = proplists:get_value(priv_dir, Config), 2241 Base = filename:join(RootDir, 2242 atom_to_list(?MODULE)++"_e_delete"), 2243 ok = ?FILE_MODULE:make_dir(Base), 2244 2245 %% Delete a non-existing file. 2246 {error, enoent} = 2247 ?FILE_MODULE:delete(filename:join(Base, "non_existing")), 2248 2249 %% Delete a directory. 2250 {error, eperm} = ?FILE_MODULE:delete(Base), 2251 2252 %% Use a path-name with a non-directory component. 2253 Afile = filename:join(Base, "a_file"), 2254 ok = ?FILE_MODULE:write_file(Afile, "hello\n"), 2255 {error, E} = 2256 expect({error, enotdir}, {error, enoent}, 2257 ?FILE_MODULE:delete(filename:join(Afile, "another_file"))), 2258 io:format("Result: ~p~n", [E]), 2259 2260 %% No permission. 2261 case os:type() of 2262 {win32, _} -> 2263 %% Remove a character device. 2264 expect({error, eacces}, {error, einval}, 2265 ?FILE_MODULE:delete("nul")); 2266 _ -> 2267 ?FILE_MODULE:write_file_info( 2268 Base, #file_info {mode=0}), 2269 {error, eacces} = ?FILE_MODULE:delete(Afile), 2270 ?FILE_MODULE:write_file_info( 2271 Base, #file_info {mode=8#700}) 2272 end, 2273 2274 [] = flush(), 2275 ok. 2276 2277%%% FreeBSD gives EEXIST when renaming a file to an empty dir, although the 2278%%% manual page can be interpreted as saying that EISDIR should be given. 2279%%% (What about FreeBSD? We store our nightly build results on a FreeBSD 2280%%% file system, that's what.) 2281 2282e_rename(Config) when is_list(Config) -> 2283 RootDir = proplists:get_value(priv_dir, Config), 2284 Base = filename:join(RootDir, 2285 atom_to_list(?MODULE)++"_e_rename"), 2286 ok = ?FILE_MODULE:make_dir(Base), 2287 2288 %% Create an empty directory. 2289 EmptyDir = filename:join(Base, "empty_dir"), 2290 ok = ?FILE_MODULE:make_dir(EmptyDir), 2291 2292 %% Create a non-empty directory. 2293 NonEmptyDir = filename:join(Base, "non_empty_dir"), 2294 ok = ?FILE_MODULE:make_dir(NonEmptyDir), 2295 ok = ?FILE_MODULE:write_file( 2296 filename:join(NonEmptyDir, "a_file"), 2297 "hello\n"), 2298 2299 %% Create another non-empty directory. 2300 ADirectory = filename:join(Base, "a_directory"), 2301 ok = ?FILE_MODULE:make_dir(ADirectory), 2302 ok = ?FILE_MODULE:write_file( 2303 filename:join(ADirectory, "a_file"), 2304 "howdy\n\n"), 2305 2306 %% Create a data file. 2307 File = filename:join(Base, "just_a_file"), 2308 ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"), 2309 2310 %% Move an existing directory to a non-empty directory. 2311 {error, eexist} = ?FILE_MODULE:rename(ADirectory, NonEmptyDir), 2312 2313 %% Move a root directory. 2314 {error, einval} = ?FILE_MODULE:rename("/", "arne"), 2315 2316 %% Move Base into Base/new_name. 2317 {error, einval} = 2318 ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")), 2319 2320 %% Overwrite a directory with a file. 2321 expect({error, eexist}, %FreeBSD (?) 2322 {error, eisdir}, 2323 ?FILE_MODULE:rename(File, EmptyDir)), 2324 expect({error, eexist}, %FreeBSD (?) 2325 {error, eisdir}, 2326 ?FILE_MODULE:rename(File, NonEmptyDir)), 2327 2328 %% Move a non-existing file. 2329 NonExistingFile = filename:join(Base, "non_existing_file"), 2330 {error, enoent} = ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir), 2331 2332 %% Overwrite a file with a directory. 2333 expect({error, eexist}, %FreeBSD (?) 2334 {error, enotdir}, 2335 ?FILE_MODULE:rename(ADirectory, File)), 2336 2337 %% Move a file to another filesystem. 2338 %% XXX - This test case is bogus. We cannot be guaranteed that 2339 %% the source and destination are on 2340 %% different filesystems. 2341 %% 2342 %% XXX - Gross hack! 2343 Comment = case os:type() of 2344 {unix, _} -> 2345 OtherFs = "/tmp", 2346 NameOnOtherFs = filename:join(OtherFs, filename:basename(File)), 2347 {ok, Com} = case ?FILE_MODULE:rename(File, NameOnOtherFs) of 2348 {error, exdev} -> 2349 %% The file could be in 2350 %% the same filesystem! 2351 {ok, ok}; 2352 ok -> 2353 {ok, {comment, 2354 "Moving between filesystems " 2355 "suceeded, files are probably " 2356 "in the same filesystem!"}}; 2357 {error, eperm} -> 2358 {ok, {comment, "SBS! You don't " 2359 "have the permission to do " 2360 "this test!"}}; 2361 Else -> 2362 Else 2363 end, 2364 Com; 2365 {win32, _} -> 2366 %% At least Windows NT can 2367 %% successfully move a file to 2368 %% another drive. 2369 ok 2370 end, 2371 [] = flush(), 2372 Comment. 2373 2374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2375 2376e_make_dir(Config) when is_list(Config) -> 2377 RootDir = proplists:get_value(priv_dir, Config), 2378 Base = filename:join(RootDir, 2379 atom_to_list(?MODULE)++"_e_make_dir"), 2380 ok = ?FILE_MODULE:make_dir(Base), 2381 2382 %% A component of the path does not exist. 2383 {error, enoent} = ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])), 2384 2385 %% Use a path-name with a non-directory component. 2386 Afile = filename:join(Base, "a_directory"), 2387 ok = ?FILE_MODULE:write_file(Afile, "hello\n"), 2388 case ?FILE_MODULE:make_dir( 2389 filename:join(Afile, "another_directory")) of 2390 {error, enotdir} -> io:format("Result: enotdir"); 2391 {error, enoent} -> io:format("Result: enoent") 2392 end, 2393 2394 %% No permission (on Unix only). 2395 case os:type() of 2396 {win32, _} -> 2397 ok; 2398 _ -> 2399 ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}), 2400 {error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")), 2401 ?FILE_MODULE:write_file_info( 2402 Base, #file_info {mode=8#700}) 2403 end, 2404 ok. 2405 2406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2407 2408e_del_dir(Config) when is_list(Config) -> 2409 RootDir = proplists:get_value(priv_dir, Config), 2410 Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")), 2411 io:format("Base: ~p", [Base]), 2412 ok = ?FILE_MODULE:make_dir(Base), 2413 2414 %% Delete a non-existent directory. 2415 {error, enoent} = 2416 ?FILE_MODULE:del_dir(filename:join(Base, "non_existing")), 2417 2418 %% Use a path-name with a non-directory component. 2419 Afile = filename:join(Base, "a_directory"), 2420 ok = ?FILE_MODULE:write_file(Afile, "hello\n"), 2421 {error, E1} = expect({error, enotdir}, {error, enoent}, 2422 ?FILE_MODULE:del_dir( 2423 filename:join(Afile, "another_directory"))), 2424 io:format("Result: ~p", [E1]), 2425 2426 %% Delete a non-empty directory. 2427 {error, E2} = expect({error, enotempty}, {error, eexist}, {error, eacces}, 2428 ?FILE_MODULE:del_dir(Base)), 2429 io:format("Result: ~p", [E2]), 2430 2431 %% Remove the current directory. 2432 {error, E3} = expect({error, einval}, 2433 {error, eperm}, % Linux and DUX 2434 {error, eacces}, 2435 {error, ebusy}, 2436 ?FILE_MODULE:del_dir(".")), 2437 io:format("Result: ~p", [E3]), 2438 2439 %% No permission. 2440 case os:type() of 2441 {win32, _} -> 2442 ok; 2443 _ -> 2444 ADirectory = filename:join(Base, "no_perm"), 2445 ok = ?FILE_MODULE:make_dir(ADirectory), 2446 ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}), 2447 {error, eacces} = ?FILE_MODULE:del_dir(ADirectory), 2448 ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#700}) 2449 end, 2450 [] = flush(), 2451 ok. 2452 2453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2454 2455 2456%% Trying reading and positioning from a compressed file. 2457 2458read_compressed_cooked(Config) when is_list(Config) -> 2459 Data = proplists:get_value(data_dir, Config), 2460 Real = filename:join(Data, "realmen.html.gz"), 2461 {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed]), 2462 try_read_file_list(Fd). 2463 2464read_compressed_cooked_binary(Config) when is_list(Config) -> 2465 Data = proplists:get_value(data_dir, Config), 2466 Real = filename:join(Data, "realmen.html.gz"), 2467 {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed,binary]), 2468 try_read_file_binary(Fd). 2469 2470%% Trying reading and positioning from an uncompressed file, 2471%% but with the compressed flag given. 2472 2473read_not_really_compressed(Config) when is_list(Config) -> 2474 Data = proplists:get_value(data_dir, Config), 2475 Priv = proplists:get_value(priv_dir, Config), 2476 2477 %% The file realmen.html might have got CRs added (by WinZip). 2478 %% Remove them, or the file positions will not be correct. 2479 2480 Real = filename:join(Data, "realmen.html"), 2481 RealPriv = filename:join(Priv, 2482 atom_to_list(?MODULE)++"_realmen.html"), 2483 {ok, RealDataBin} = ?FILE_MODULE:read_file(Real), 2484 RealData = remove_crs(binary_to_list(RealDataBin), []), 2485 ok = ?FILE_MODULE:write_file(RealPriv, RealData), 2486 {ok, Fd} = ?FILE_MODULE:open(RealPriv, [read, compressed]), 2487 try_read_file_list(Fd). 2488 2489remove_crs([$\r|Rest], Result) -> 2490 remove_crs(Rest, Result); 2491remove_crs([C|Rest], Result) -> 2492 remove_crs(Rest, [C|Result]); 2493remove_crs([], Result) -> 2494 lists:reverse(Result). 2495 2496try_read_file_list(Fd) -> 2497 %% Seek to the current position (nothing should happen). 2498 2499 {ok, 0} = ?FILE_MODULE:position(Fd, 0), 2500 {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}), 2501 2502 %% Read a few lines from a compressed file. 2503 2504 ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n", 2505 ShouldBe = io:get_line(Fd, ''), 2506 2507 %% Now seek forward. 2508 2509 {ok, 381} = ?FILE_MODULE:position(Fd, 381), 2510 Back = "Back in the good old days -- the \"Golden Era\" " ++ 2511 "of computers, it was\n", 2512 Back = io:get_line(Fd, ''), 2513 2514 %% Try to search forward relative to the current position. 2515 2516 {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}), 2517 RealPos = 4273, 2518 {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}), 2519 RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n", 2520 RealProg = io:get_line(Fd, ''), 2521 2522 %% Seek backward. 2523 2524 AfterTitle = length("<TITLE>"), 2525 {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle), 2526 Title = "Real Programmers Don't Use PASCAL</TITLE>\n", 2527 Title = io:get_line(Fd, ''), 2528 2529 %% Seek past the end of the file. 2530 2531 {ok, _} = ?FILE_MODULE:position(Fd, 25000), 2532 2533 %% Done. 2534 2535 ?FILE_MODULE:close(Fd), 2536 [] = flush(), 2537 ok. 2538 2539try_read_file_binary(Fd) -> 2540 %% Seek to the current position (nothing should happen). 2541 2542 {ok, 0} = ?FILE_MODULE:position(Fd, 0), 2543 {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}), 2544 2545 %% Read a few lines from a compressed file. 2546 2547 ShouldBe = <<"<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n">>, 2548 ShouldBe = io:get_line(Fd, ''), 2549 2550 %% Now seek forward. 2551 2552 {ok, 381} = ?FILE_MODULE:position(Fd, 381), 2553 Back = <<"Back in the good old days -- the \"Golden Era\" " 2554 "of computers, it was\n">>, 2555 Back = io:get_line(Fd, ''), 2556 2557 %% Try to search forward relative to the current position. 2558 2559 {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}), 2560 RealPos = 4273, 2561 {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}), 2562 RealProg = <<"<LI> Real Programmers aren't afraid to use GOTOs.\n">>, 2563 RealProg = io:get_line(Fd, ''), 2564 2565 %% Seek backward. 2566 2567 AfterTitle = length("<TITLE>"), 2568 {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle), 2569 Title = <<"Real Programmers Don't Use PASCAL</TITLE>\n">>, 2570 Title = io:get_line(Fd, ''), 2571 2572 %% Done. 2573 2574 ?FILE_MODULE:close(Fd), 2575 [] = flush(), 2576 ok. 2577 2578read_cooked_tar_problem(Config) when is_list(Config) -> 2579 Data = proplists:get_value(data_dir, Config), 2580 ProblemFile = filename:join(Data, "cooked_tar_problem.tar.gz"), 2581 {ok,Fd} = ?FILE_MODULE:open(ProblemFile, [read,compressed,binary]), 2582 2583 {ok,34304} = file:position(Fd, 34304), 2584 {ok,Bin} = file:read(Fd, 512), 2585 512 = byte_size(Bin), 2586 2587 {ok,34304+512+1024} = file:position(Fd, {cur,1024}), 2588 2589 ok = file:close(Fd), 2590 2591 ok. 2592 2593 2594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2595 2596write_compressed(Config) when is_list(Config) -> 2597 Priv = proplists:get_value(priv_dir, Config), 2598 MyFile = filename:join(Priv, 2599 atom_to_list(?MODULE)++"_test.gz"), 2600 2601 %% Write a file. 2602 2603 {ok, Fd} = ?FILE_MODULE:open(MyFile, [write, compressed]), 2604 {ok, 0} = ?FILE_MODULE:position(Fd, 0), 2605 Prefix = "hello\n", 2606 End = "end\n", 2607 ok = io:put_chars(Fd, Prefix), 2608 {ok, 143} = ?FILE_MODULE:position(Fd, 143), 2609 ok = io:put_chars(Fd, End), 2610 ok = ?FILE_MODULE:close(Fd), 2611 2612 %% Read the file and verify the contents. 2613 2614 {ok, Fd1} = ?FILE_MODULE:open(MyFile, [read, compressed]), 2615 Prefix = io:get_line(Fd1, ''), 2616 Second = lists:duplicate(143-length(Prefix), 0) ++ End, 2617 Second = io:get_line(Fd1, ''), 2618 ok = ?FILE_MODULE:close(Fd1), 2619 2620 %% Verify successful compression by uncompressing the file 2621 %% using zlib:gunzip/1. 2622 2623 {ok,Contents} = file:read_file(MyFile), 2624 <<"hello\n",0:137/unit:8,"end\n">> = zlib:gunzip(Contents), 2625 2626 %% Ensure that the file is compressed. 2627 2628 TotalSize = 143 + length(End), 2629 case ?FILE_MODULE:read_file_info(MyFile) of 2630 {ok, #file_info{size=Size}} when Size < TotalSize -> 2631 ok; 2632 {ok, #file_info{size=Size}} when Size == TotalSize -> 2633 ct:fail(file_not_compressed) 2634 end, 2635 2636 %% Write again to ensure that the file is truncated. 2637 2638 {ok, Fd2} = ?FILE_MODULE:open(MyFile, [write, compressed]), 2639 NewString = "aaaaaaaaaaa", 2640 ok = io:put_chars(Fd2, NewString), 2641 ok = ?FILE_MODULE:close(Fd2), 2642 {ok, Fd3} = ?FILE_MODULE:open(MyFile, [read, compressed]), 2643 {ok, NewString} = ?FILE_MODULE:read(Fd3, 1024), 2644 ok = ?FILE_MODULE:close(Fd3), 2645 2646 %% Done. 2647 2648 [] = flush(), 2649 ok. 2650 2651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2652 2653catenated_gzips(Config) when is_list(Config) -> 2654 Priv = proplists:get_value(priv_dir, Config), 2655 MyFile = filename:join(Priv, ?MODULE_STRING++"_test.gz"), 2656 2657 First = "Hello, all good men going to search parties. ", 2658 Second = "Now I really need your help.", 2659 All = iolist_to_binary([First|Second]), 2660 Cat = [zlib:gzip(First),zlib:gzip(Second)], 2661 2662 ok = file:write_file(MyFile, Cat), 2663 2664 {ok,Fd} = file:open(MyFile, [read,compressed,binary]), 2665 {ok,All} = file:read(Fd, 100000), 2666 ok = file:close(Fd), 2667 2668 ok. 2669 2670 2671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2672 2673compress_errors(Config) when is_list(Config) -> 2674 DataDir = 2675 filename:dirname( 2676 filename:join(proplists:get_value(data_dir, Config), "x")), 2677 DataDirSlash = DataDir++"/", 2678 {error, enoent} = ?FILE_MODULE:open("non_existing__", 2679 [compressed, read]), 2680 {error, einval} = ?FILE_MODULE:open("non_existing__", 2681 [compressed, read, write]), 2682 {error, einval} = ?FILE_MODULE:open("non_existing__", 2683 [compressed, read, append]), 2684 {error, einval} = ?FILE_MODULE:open("non_existing__", 2685 [compressed, write, append]), 2686 {error, E1} = ?FILE_MODULE:open(DataDir, [compressed, read]), 2687 {error, E2} = ?FILE_MODULE:open(DataDirSlash, [compressed, read]), 2688 {error, E3} = ?FILE_MODULE:open(DataDir, [compressed, write]), 2689 {error, E4} = ?FILE_MODULE:open(DataDirSlash, [compressed, write]), 2690 {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4}, 2691 2692 %% Read a corrupted .gz file. 2693 2694 Corrupted = filename:join(DataDir, "corrupted.gz"), 2695 {ok, Fd} = ?FILE_MODULE:open(Corrupted, [read, compressed]), 2696 {error, eio} = ?FILE_MODULE:read(Fd, 100), 2697 ?FILE_MODULE:close(Fd), 2698 2699 [] = flush(), 2700 ok. 2701 2702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2703 2704compress_async_crash(Config) when is_list(Config) -> 2705 DataDir = proplists:get_value(data_dir, Config), 2706 Path = filename:join(DataDir, "test.gz"), 2707 ExpectedData = <<"qwerty">>, 2708 2709 _ = ?FILE_MODULE:delete(Path), 2710 {ok, Fd} = ?FILE_MODULE:open(Path, [write, binary, compressed]), 2711 ok = ?FILE_MODULE:write(Fd, ExpectedData), 2712 ok = ?FILE_MODULE:close(Fd), 2713 2714 %% Test that when using async thread pool, the emulator doesn't crash 2715 %% when the efile port driver is stopped while a compressed file operation 2716 %% is in progress (being carried by an async thread). 2717 ok = compress_async_crash_loop(10000, Path, ExpectedData), 2718 ok = ?FILE_MODULE:delete(Path), 2719 ok. 2720 2721compress_async_crash_loop(0, _Path, _ExpectedData) -> 2722 ok; 2723compress_async_crash_loop(N, Path, ExpectedData) -> 2724 Parent = self(), 2725 {Pid, Ref} = spawn_monitor( 2726 fun() -> 2727 {ok, Fd} = ?FILE_MODULE:open( 2728 Path, [read, compressed, raw, binary]), 2729 Len = byte_size(ExpectedData), 2730 Parent ! {self(), continue}, 2731 {ok, ExpectedData} = ?FILE_MODULE:read(Fd, Len), 2732 ok = ?FILE_MODULE:close(Fd), 2733 receive foobar -> ok end 2734 end), 2735 receive 2736 {Pid, continue} -> 2737 exit(Pid, shutdown), 2738 receive 2739 {'DOWN', Ref, _, _, Reason} -> 2740 shutdown = Reason 2741 end; 2742 {'DOWN', Ref, _, _, Reason2} -> 2743 ct:fail({worker_exited, Reason2}) 2744 after 60000 -> 2745 exit(Pid, shutdown), 2746 erlang:demonitor(Ref, [flush]), 2747 ct:fail(worker_timeout) 2748 end, 2749 compress_async_crash_loop(N - 1, Path, ExpectedData). 2750 2751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2752 2753unicode(Config) when is_list(Config) -> 2754 Dir = proplists:get_value(priv_dir, Config), 2755 Name = filename:join(Dir, "data-utf8.txt"), 2756 Txt = lists:seq(128, 255), 2757 D = unicode:characters_to_binary(Txt, latin1, latin1), 2758 {ok,Fd1} = 2759 ?FILE_MODULE:open(Name, [write,read,binary,{encoding,unicode}]), 2760 ok = ?FILE_MODULE:truncate(Fd1), 2761 ok = ?FILE_MODULE:write(Fd1, Txt), 2762 {ok,0} = ?FILE_MODULE:position(Fd1, bof), 2763 {ok,D} = ?FILE_MODULE:read(Fd1, 129), 2764 {ok,0} = ?FILE_MODULE:position(Fd1, bof), 2765 {ok,D1} = ?FILE_MODULE:read(Fd1, 64), 2766 {ok,Pos} = ?FILE_MODULE:position(Fd1, cur), 2767 {ok,D2} = ?FILE_MODULE:pread(Fd1, {cur,0}, 65), 2768 D = <<D1/binary, D2/binary>>, 2769 {ok,D1} = ?FILE_MODULE:pread(Fd1, bof, 64), 2770 {ok,Pos} = ?FILE_MODULE:position(Fd1, Pos), 2771 {ok,D2} = ?FILE_MODULE:read(Fd1, 64), 2772 ok = ?FILE_MODULE:close(Fd1), 2773 %% 2774 RawD = unicode:characters_to_binary(Txt, latin1, unicode), 2775 {ok,RawD} = ?FILE_MODULE:read_file(Name), 2776 %% 2777 {ok,Fd2} = ?FILE_MODULE:open(Name, [read,{encoding,unicode}]), 2778 {ok,Txt} = ?FILE_MODULE:read(Fd2, 129), 2779 {Txt1,Txt2} = lists:split(64, Txt), 2780 {ok,Txt2} = ?FILE_MODULE:pread(Fd2, Pos, 65), 2781 {ok,0} = ?FILE_MODULE:position(Fd2, bof), 2782 {ok,Txt1} = ?FILE_MODULE:read(Fd2, 64), 2783 ok = ?FILE_MODULE:close(Fd2). 2784 2785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2786 2787%% Test the file:altname/1 function. 2788altname(Config) when is_list(Config) -> 2789 RootDir = proplists:get_value(priv_dir, Config), 2790 NewDir = filename:join(RootDir, 2791 "long alternative path name with spaces"), 2792 ok = ?FILE_MODULE:make_dir(NewDir), 2793 Name = filename:join(NewDir, "a_file_with_long_name"), 2794 ShortName = filename:join(NewDir, "short"), 2795 NonexName = filename:join(NewDir, "nonexistent"), 2796 ok = ?FILE_MODULE:write_file(Name, "some contents\n"), 2797 ok = ?FILE_MODULE:write_file(ShortName, "some contents\n"), 2798 Result = 2799 case ?FILE_MODULE:altname(NewDir) of 2800 {error, enotsup} -> 2801 {skipped, "Altname not supported on this platform"}; 2802 {ok, "LONGAL~1"} -> 2803 {ok, "A_FILE~1"} = ?FILE_MODULE:altname(Name), 2804 {ok, "c:/"} = ?FILE_MODULE:altname("C:/"), 2805 {ok, "c:/"} = ?FILE_MODULE:altname("C:\\"), 2806 {error,enoent} = ?FILE_MODULE:altname(NonexName), 2807 {ok, "short"} = ?FILE_MODULE:altname(ShortName), 2808 ok 2809 end, 2810 [] = flush(), 2811 Result. 2812 2813 2814%% Test creating a hard link. 2815make_link(Config) when is_list(Config) -> 2816 RootDir = proplists:get_value(priv_dir, Config), 2817 NewDir = filename:join(RootDir, 2818 atom_to_list(?MODULE) 2819 ++"_make_link"), 2820 ok = ?FILE_MODULE:make_dir(NewDir), 2821 2822 Name = filename:join(NewDir, "a_file"), 2823 ok = ?FILE_MODULE:write_file(Name, "some contents\n"), 2824 2825 Alias = filename:join(NewDir, "an_alias"), 2826 Result = 2827 case ?FILE_MODULE:make_link(Name, Alias) of 2828 {error, enotsup} -> 2829 {skipped, "Links not supported on this platform"}; 2830 ok -> 2831 %% Note: We take the opportunity to test 2832 %% ?FILE_MODULE:read_link_info/1, 2833 %% which should in behave exactly as 2834 %% ?FILE_MODULE:read_file_info/1 2835 %% since they are not used on symbolic links. 2836 2837 {ok, Info} = ?FILE_MODULE:read_link_info(Name), 2838 {ok,Info} = ?FILE_MODULE:read_link_info(Name, [raw]), 2839 {ok, Info} = ?FILE_MODULE:read_link_info(Alias), 2840 {ok,Info} = ?FILE_MODULE:read_link_info(Alias, [raw]), 2841 #file_info{links = 2, type = regular} = Info, 2842 {error, eexist} = 2843 ?FILE_MODULE:make_link(Name, Alias), 2844 ok 2845 end, 2846 2847 [] = flush(), 2848 Result. 2849 2850%% Test that reading link info for an ordinary file or directory works 2851%% (on all platforms). 2852read_link_info_for_non_link(Config) when is_list(Config) -> 2853 {ok, #file_info{type=directory}} = 2854 ?FILE_MODULE:read_link_info("."), 2855 {ok, #file_info{type=directory}} = ?FILE_MODULE:read_link_info(".", [raw]), 2856 2857 [] = flush(), 2858 ok. 2859 2860%% Test operations on symbolic links (for Unix). 2861symlinks(Config) when is_list(Config) -> 2862 {error, _} = ?FILE_MODULE:read_link(lists:duplicate(10000,$a)), 2863 {error, _} = ?FILE_MODULE:read_link_all(lists:duplicate(10000,$a)), 2864 RootDir = proplists:get_value(priv_dir, Config), 2865 NewDir = filename:join(RootDir, 2866 atom_to_list(?MODULE) 2867 ++"_symlinks"), 2868 ok = ?FILE_MODULE:make_dir(NewDir), 2869 2870 Name = filename:join(NewDir, "a_plain_file"), 2871 ok = ?FILE_MODULE:write_file(Name, "some stupid content\n"), 2872 2873 Alias = filename:join(NewDir, "a_symlink_alias"), 2874 Result = 2875 case ?FILE_MODULE:make_symlink(Name, Alias) of 2876 {error, enotsup} -> 2877 {skipped, "Links not supported on this platform"}; 2878 {error, eperm} -> 2879 {win32,_} = os:type(), 2880 {skipped, "Windows user not privileged to create symlinks"}; 2881 ok -> 2882 {ok, Info1} = ?FILE_MODULE:read_file_info(Name), 2883 {ok,Info1} = ?FILE_MODULE:read_file_info(Name, [raw]), 2884 {ok, Info1} = ?FILE_MODULE:read_file_info(Alias), 2885 {ok,Info1} = ?FILE_MODULE:read_file_info(Alias, [raw]), 2886 {ok, Info1} = ?FILE_MODULE:read_link_info(Name), 2887 {ok,Info1} = ?FILE_MODULE:read_link_info(Name, [raw]), 2888 #file_info{links = 1, type = regular} = Info1, 2889 2890 {ok, Info2} = ?FILE_MODULE:read_link_info(Alias), 2891 {ok,Info2} = ?FILE_MODULE:read_link_info(Alias, [raw]), 2892 #file_info{links=1, type=symlink} = Info2, 2893 {ok, Name} = ?FILE_MODULE:read_link(Alias), 2894 {ok, Name} = ?FILE_MODULE:read_link_all(Alias), 2895 %% If all is good, delete dir again (avoid hanging dir on windows) 2896 rm_rf(?FILE_MODULE,NewDir), 2897 ok 2898 end, 2899 2900 [] = flush(), 2901 Result. 2902 2903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2904 2905copy(Config) when is_list(Config) -> 2906 RootDir = proplists:get_value(priv_dir, Config), 2907 %% Create a text file. 2908 Name1 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_1.txt"), 2909 Line = "The quick brown fox jumps over a lazy dog. 0123456789\n", 2910 Len = length(Line), 2911 {ok, Handle1} = ?FILE_MODULE:open(Name1, [write]), 2912 {_, Size1} = 2913 iterate({0, 0}, 2914 done, 2915 fun({_, S}) when S >= 128*1024 -> 2916 done; 2917 ({N, S}) -> 2918 H = integer_to_list(N), 2919 ok = ?FILE_MODULE:write(Handle1, [H, " ", Line]), 2920 {N + 1, S + length(H) + 1 + Len} 2921 end), 2922 ?FILE_MODULE:close(Handle1), 2923 %% Make a copy 2924 Name2 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_2.txt"), 2925 {ok, Size1} = ?FILE_MODULE:copy(Name1, Name2), 2926 %% Concatenate 1 2927 Name3 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_3.txt"), 2928 {ok, Handle3} = ?FILE_MODULE:open(Name3, [raw, write, binary]), 2929 {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle3), 2930 {ok, Handle2} = ?FILE_MODULE:open(Name2, [read, binary]), 2931 {ok, Size1} = ?FILE_MODULE:copy(Handle2, Handle3), 2932 ok = ?FILE_MODULE:close(Handle3), 2933 ok = ?FILE_MODULE:close(Handle2), 2934 %% Concatenate 2 2935 Name4 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_4.txt"), 2936 {ok, Handle4} = ?FILE_MODULE:open(Name4, [write, binary]), 2937 {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle4), 2938 {ok, Handle5} = ?FILE_MODULE:open(Name2, [raw, read, binary]), 2939 {ok, Size1} = ?FILE_MODULE:copy(Handle5, Handle4), 2940 ok = ?FILE_MODULE:close(Handle5), 2941 ok = ?FILE_MODULE:close(Handle4), 2942 %% %% Just for test of the test 2943 %% {ok, Handle2q} = ?FILE_MODULE:open(Name2, [write, append]), 2944 %% ok = ?FILE_MODULE:write(Handle2q, "q"), 2945 %% ok = ?FILE_MODULE:close(Handle2q), 2946 %% Compare the files 2947 {ok, Handle1a} = ?FILE_MODULE:open(Name1, [raw, read]), 2948 {ok, Handle2a} = ?FILE_MODULE:open(Name2, [raw, read]), 2949 true = stream_cmp(fd_stream_factory([Handle1a]), 2950 fd_stream_factory([Handle2a])), 2951 {ok, 0} = ?FILE_MODULE:position(Handle1a, 0), 2952 {ok, 0} = ?FILE_MODULE:position(Handle2a, 0), 2953 {ok, Handle3a} = ?FILE_MODULE:open(Name3, [raw, read]), 2954 true = stream_cmp(fd_stream_factory([Handle1a, Handle2a]), 2955 fd_stream_factory([Handle2a])), 2956 ok = ?FILE_MODULE:close(Handle1a), 2957 ok = ?FILE_MODULE:close(Handle2a), 2958 ok = ?FILE_MODULE:close(Handle3a), 2959 [] = flush(), 2960 ok. 2961 2962 2963 2964fd_stream_factory([]) -> 2965 []; 2966fd_stream_factory([Fd | T] = L) -> 2967 fun() -> 2968 case ?FILE_MODULE:read(Fd, 8192) of 2969 {ok, Data} when is_binary(Data) -> 2970 binary_to_list(Data) ++ fd_stream_factory(L); 2971 {ok, Data} when is_list(Data) -> 2972 Data ++ fd_stream_factory(L); 2973 eof -> 2974 fd_stream_factory(T); 2975 {error, _} = Error -> 2976 Error 2977 end 2978 end. 2979 2980 2981 2982stream_cmp(F1, F2) when is_function(F1), is_function(F2) -> 2983 stream_cmp(F1(), F2()); 2984stream_cmp(F, X) when is_function(F) -> 2985 stream_cmp(F(), X); 2986stream_cmp(X, F) when is_function(F) -> 2987 stream_cmp(X, F()); 2988stream_cmp({error, _} = Error, _) -> 2989 Error; 2990stream_cmp(_, {error, _} = Error) -> 2991 Error; 2992stream_cmp([], []) -> 2993 true; 2994stream_cmp([], [_|_]) -> 2995 false; 2996stream_cmp([_|_], []) -> 2997 false; 2998stream_cmp([H | T1], [H | T2]) -> 2999 stream_cmp(T1, T2). 3000 3001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3002 3003%% Test the get_cwd(), open(), and copy() file server calls. 3004new_slave(_RootDir, Cwd) -> 3005 L = "qwertyuiopasdfghjklzxcvbnm", 3006 N = length(L), 3007 {ok, Cwd} = ?FILE_MODULE:get_cwd(), 3008 {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase 3009 {ok, FD1} = ?FILE_MODULE:open("file1.txt", write), 3010 ok = ?FILE_MODULE:close(FD1), 3011 {ok, FD2} = ?FILE_MODULE:open("file1.txt", 3012 [write, append, 3013 binary, compressed, 3014 delayed_write, 3015 {delayed_write, 0, 0}, 3016 read_ahead, 3017 {read_ahead, 0}]), 3018 ok = ?FILE_MODULE:write(FD2, L), 3019 ok = ?FILE_MODULE:close(FD2), 3020 {ok, N2} = ?FILE_MODULE:copy("file1.txt", "file2.txt"), 3021 io:format("Size ~p, compressed ~p.~n", [N, N2]), 3022 {ok, FD3} = ?FILE_MODULE:open("file2.txt", 3023 [binary, compressed]), 3024 %% The file_io_server will translate the binary into a list 3025 {ok, L} = ?FILE_MODULE:read(FD3, N+1), 3026 ok = ?FILE_MODULE:close(FD3), 3027 %% 3028 ok = ?FILE_MODULE:delete("file1.txt"), 3029 ok = ?FILE_MODULE:delete("file2.txt"), 3030 [] = flush(), 3031 ok. 3032 3033 3034%% Test the get_cwd() and open() file server calls. 3035old_slave(_RootDir, Cwd) -> 3036 L = "qwertyuiopasdfghjklzxcvbnm", 3037 N = length(L), 3038 {ok, Cwd} = ?FILE_MODULE:get_cwd(), 3039 {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase 3040 {ok, FD1} = ?FILE_MODULE:open("file1.txt", write), 3041 ok = ?FILE_MODULE:close(FD1), 3042 {ok, FD2} = ?FILE_MODULE:open("file1.txt", 3043 [write, binary, compressed]), 3044 ok = ?FILE_MODULE:write(FD2, L), 3045 ok = ?FILE_MODULE:close(FD2), 3046 {ok, FD3} = ?FILE_MODULE:open("file1.txt", [write, append]), 3047 ok = ?FILE_MODULE:close(FD3), 3048 {ok, FD4} = ?FILE_MODULE:open("file1.txt", 3049 [binary, compressed]), 3050 %% The file_io_server will translate the binary into a list 3051 {ok, L} = ?FILE_MODULE:read(FD4, N+1), 3052 ok = ?FILE_MODULE:close(FD4), 3053 %% 3054 ok = ?FILE_MODULE:delete("file1.txt"), 3055 [] = flush(), 3056 ok. 3057 3058run_test(Test, Args) -> 3059 case (catch apply(?MODULE, Test, Args)) of 3060 {'EXIT', _} = Exit -> 3061 {done, Exit, get(test_server_loc)}; 3062 Result -> 3063 {done, Result} 3064 end. 3065 3066%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3067 3068%% Tests the file open option {delayed_write, Size, Delay}. 3069 3070delayed_write(Config) when is_list(Config) -> 3071 RootDir = proplists:get_value(priv_dir, Config), 3072 File = filename:join(RootDir, 3073 atom_to_list(?MODULE)++"_delayed_write.txt"), 3074 Data1 = "asdfghjkl", 3075 Data2 = "qwertyuio", 3076 Data3 = "zxcvbnm,.", 3077 Size = length(Data1), 3078 Size = length(Data2), 3079 Size = length(Data3), 3080 Data1Data1 = Data1++Data1, 3081 Data1Data1Data1 = Data1Data1++Data1, 3082 Data1Data1Data1Data1 = Data1Data1++Data1Data1, 3083 %% 3084 %% Test caching and normal close of non-raw file 3085 {ok, Fd1} = 3086 ?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 400}]), 3087 ok = ?FILE_MODULE:write(Fd1, Data1), 3088 %% Wait for a reasonable amount of time to check whether the write was 3089 %% practically instantaneous or actually delayed. 3090 timer:sleep(100), 3091 {ok, Fd2} = ?FILE_MODULE:open(File, [read]), 3092 eof = ?FILE_MODULE:read(Fd2, 1), 3093 ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size 3094 timer:sleep(100), 3095 {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1), 3096 ok = ?FILE_MODULE:write(Fd1, Data1), 3097 timer:sleep(500), % Wait until data flush on timeout 3098 {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1), 3099 ok = ?FILE_MODULE:write(Fd1, Data1), 3100 ok = ?FILE_MODULE:close(Fd1), % Data flush on close 3101 timer:sleep(100), 3102 {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1), 3103 ok = ?FILE_MODULE:close(Fd2), 3104 %% 3105 %% Test implicit close through exit by file owning process, 3106 %% raw file, default parameters. 3107 Parent = self(), 3108 Fun = fun() -> 3109 Child = self(), 3110 Test = 3111 fun () -> 3112 {ok, Fd} = ?FILE_MODULE:open(File, 3113 [raw, write, delayed_write]), 3114 ok = ?FILE_MODULE:write(Fd, Data1), 3115 Parent ! {Child, wrote}, 3116 receive 3117 {Parent, continue, Reason} -> 3118 {ok, Reason} 3119 end 3120 end, 3121 case (catch Test()) of 3122 {ok, Reason} -> exit(Reason); 3123 Unknown -> 3124 exit({Unknown, get(test_server_loc)}) 3125 end 3126 end, 3127 Child1 = spawn(Fun), 3128 Mref1 = erlang:monitor(process, Child1), 3129 receive 3130 {Child1, wrote} -> 3131 ok; 3132 {'DOWN', Mref1, _, _, _} = Down1a -> 3133 ct:fail(Down1a) 3134 end, 3135 timer:sleep(100), % Just in case the file system is slow 3136 {ok, Fd3} = ?FILE_MODULE:open(File, [read]), 3137 eof = ?FILE_MODULE:read(Fd3, 1), 3138 Child1 ! {Parent, continue, normal}, 3139 receive 3140 {'DOWN', Mref1, process, Child1, normal} -> 3141 ok; 3142 {'DOWN', Mref1, _, _, _} = Down1b -> 3143 ct:fail(Down1b) 3144 end, 3145 timer:sleep(100), % Just in case the file system is slow 3146 {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1), 3147 ok = ?FILE_MODULE:close(Fd3), 3148 %% 3149 %% The same again, but this time with reason 'kill'. 3150 Child2 = spawn(Fun), 3151 Mref2 = erlang:monitor(process, Child2), 3152 receive 3153 {Child2, wrote} -> 3154 ok; 3155 {'DOWN', Mref2, _, _, _} = Down2a -> 3156 ct:fail(Down2a) 3157 end, 3158 timer:sleep(100), % Just in case the file system is slow 3159 {ok, Fd4} = ?FILE_MODULE:open(File, [read]), 3160 eof = ?FILE_MODULE:read(Fd4, 1), 3161 Child2 ! {Parent, continue, kill}, 3162 receive 3163 {'DOWN', Mref2, process, Child2, kill} -> 3164 ok; 3165 {'DOWN', Mref2, _, _, _} = Down2b -> 3166 ct:fail(Down2b) 3167 end, 3168 timer:sleep(100), % Just in case the file system is slow 3169 eof = ?FILE_MODULE:pread(Fd4, bof, 1), 3170 ok = ?FILE_MODULE:close(Fd4), 3171 %% 3172 %% Test if file position works with delayed_write 3173 {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, 3174 delayed_write]), 3175 ok = ?FILE_MODULE:truncate(Fd5), 3176 ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]), 3177 {ok, 0} = ?FILE_MODULE:position(Fd5, bof), 3178 ok = ?FILE_MODULE:write(Fd5, [Data3]), 3179 {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1), 3180 {ok, 0} = ?FILE_MODULE:position(Fd5, bof), 3181 Data3Data2 = Data3++Data2, 3182 {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1), 3183 ok = ?FILE_MODULE:close(Fd5), 3184 %% 3185 [] = flush(), 3186 ok. 3187 3188 3189%% Tests file:pid2name/1. 3190pid2name(Config) when is_list(Config) -> 3191 RootDir = proplists:get_value(priv_dir, Config), 3192 Base = test_server:temp_name( 3193 filename:join(RootDir, "pid2name_")), 3194 Name1 = [Base, '.txt'], 3195 Name2 = Base ++ ".txt", 3196 %% 3197 {ok, Pid} = file:open(Name1, [write]), 3198 {ok, Name2} = file:pid2name(Pid), 3199 undefined = file:pid2name(self()), 3200 ok = file:close(Pid), 3201 ct:sleep(1000), 3202 false = is_process_alive(Pid), 3203 undefined = file:pid2name(Pid), 3204 ok. 3205 3206 3207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3208 3209%% Tests the file open option {read_ahead, Size}. 3210 3211read_ahead(Config) when is_list(Config) -> 3212 RootDir = proplists:get_value(priv_dir, Config), 3213 File = filename:join(RootDir, 3214 atom_to_list(?MODULE)++"_read_ahead.txt"), 3215 Data1 = "asdfghjkl", % Must be 3216 Data2 = "qwertyuio", % same 3217 Data3 = "zxcvbnm,.", % length 3218 Size = length(Data1), 3219 Size = length(Data2), 3220 Size = length(Data3), 3221 %% 3222 %% Test caching of normal non-raw file 3223 {ok, Fd1} = ?FILE_MODULE:open(File, [write]), 3224 ok = ?FILE_MODULE:write(Fd1, [Data1|Data1]), 3225 timer:sleep(1000), % Just in case the file system is slow 3226 {ok, Fd2} = ?FILE_MODULE:open(File, [read, {read_ahead, 2*Size}]), 3227 {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), 3228 ok = ?FILE_MODULE:pwrite(Fd1, Size, Data2), 3229 timer:sleep(1000), % Just in case the file system is slow 3230 {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), % Will read cached data 3231 Data2Data2Data2 = Data2++Data2++Data2, 3232 ok = ?FILE_MODULE:pwrite(Fd1, eof, Data2Data2Data2), 3233 timer:sleep(1000), % Just in case the file system is slow 3234 {ok, Data2Data2Data2} = 3235 ?FILE_MODULE:read(Fd2, 3*Size), % Read more than cache buffer 3236 ok = ?FILE_MODULE:close(Fd1), 3237 ok = ?FILE_MODULE:close(Fd2), 3238 %% Test caching of raw file and default parameters 3239 {ok, Fd3} = ?FILE_MODULE:open(File, [raw, write]), 3240 ok = ?FILE_MODULE:write(Fd3, [Data1|Data1]), 3241 timer:sleep(1000), % Just in case the file system is slow 3242 {ok, Fd4} = ?FILE_MODULE:open(File, [raw, read, read_ahead]), 3243 {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), 3244 ok = ?FILE_MODULE:pwrite(Fd3, Size, Data2), 3245 timer:sleep(1000), % Just in case the file system is slow 3246 {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), % Will read cached data 3247 ok = ?FILE_MODULE:close(Fd3), 3248 ok = ?FILE_MODULE:close(Fd4), 3249 %% Test if the file position works in combination with read_ahead 3250 {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, read_ahead]), 3251 ok = ?FILE_MODULE:truncate(Fd5), 3252 ok = ?FILE_MODULE:write(Fd5, [Data1,Data1|Data3]), 3253 {ok, 0} = ?FILE_MODULE:position(Fd5, bof), 3254 {ok, Data1} = ?FILE_MODULE:read(Fd5, Size), 3255 ok = ?FILE_MODULE:write(Fd5, Data2), 3256 {ok, 0} = ?FILE_MODULE:position(Fd5, bof), 3257 Data1Data2Data3 = Data1++Data2++Data3, 3258 {ok, Data1Data2Data3} = ?FILE_MODULE:read(Fd5, 3*Size+1), 3259 ok = ?FILE_MODULE:close(Fd5), 3260 3261 %% Ensure that a read that draws from both the buffer and the file won't 3262 %% return anything wonky. 3263 SplitData = << <<(I rem 256)>> || I <- lists:seq(1, 1024) >>, 3264 file:write_file(File, SplitData), 3265 {ok, Fd6} = ?FILE_MODULE:open(File, [raw, read, binary, {read_ahead, 256}]), 3266 {ok, <<1>>} = file:read(Fd6, 1), 3267 <<1, Shifted:512/binary, _Rest/binary>> = SplitData, 3268 {ok, Shifted} = file:read(Fd6, 512), 3269 3270 %% 3271 [] = flush(), 3272 ok. 3273 3274 3275 3276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3277 3278 3279 3280%% Tests the segmenting of large reads. 3281segment_read(Config) when is_list(Config) -> 3282 Name = filename:join(proplists:get_value(priv_dir, Config), 3283 ?MODULE_STRING ++ "_segment_read"), 3284 SegSize = 256*1024, 3285 SegCnt = SegSize div 4, 3286 Cnt = 4 * SegCnt, 3287 ok = create_file(Name, Cnt), 3288 %% 3289 %% read_file/1 3290 %% 3291 {ok, Bin} = ?FILE_MODULE:read_file(Name), 3292 true = verify_bin(Bin, 0, Cnt), 3293 %% 3294 %% read/2 3295 %% 3296 %% Not segmented 3297 {ok, FD1} = ?FILE_MODULE:open(Name, [read, raw, binary]), 3298 {ok, B1a} = ?FILE_MODULE:read(FD1, SegSize), 3299 {ok, B1b} = ?FILE_MODULE:read(FD1, SegSize), 3300 {ok, B1c} = ?FILE_MODULE:read(FD1, SegSize), 3301 {ok, B1d} = ?FILE_MODULE:read(FD1, SegSize), 3302 ok = ?FILE_MODULE:close(FD1), 3303 true = verify_bin(B1a, 0*SegCnt, SegCnt), 3304 true = verify_bin(B1b, 1*SegCnt, SegCnt), 3305 true = verify_bin(B1c, 2*SegCnt, SegCnt), 3306 true = verify_bin(B1d, 3*SegCnt, SegCnt), 3307 %% 3308 %% Segmented 3309 {ok, FD2} = ?FILE_MODULE:open(Name, [read, raw, binary]), 3310 {ok, B2a} = ?FILE_MODULE:read(FD2, 1*SegSize), 3311 {ok, B2b} = ?FILE_MODULE:read(FD2, 2*SegSize), 3312 {ok, B2c} = ?FILE_MODULE:read(FD2, 2*SegSize), 3313 ok = ?FILE_MODULE:close(FD2), 3314 true = verify_bin(B2a, 0*SegCnt, 1*SegCnt), 3315 true = verify_bin(B2b, 1*SegCnt, 2*SegCnt), 3316 true = verify_bin(B2c, 3*SegCnt, 1*SegCnt), 3317 %% 3318 %% pread/3 3319 %% 3320 {ok, FD3} = ?FILE_MODULE:open(Name, [read, raw, binary]), 3321 %% 3322 %% Not segmented 3323 {ok, B3d} = ?FILE_MODULE:pread(FD3, 3*SegSize, SegSize), 3324 {ok, B3c} = ?FILE_MODULE:pread(FD3, 2*SegSize, SegSize), 3325 {ok, B3b} = ?FILE_MODULE:pread(FD3, 1*SegSize, SegSize), 3326 {ok, B3a} = ?FILE_MODULE:pread(FD3, 0*SegSize, SegSize), 3327 true = verify_bin(B3a, 0*SegCnt, SegCnt), 3328 true = verify_bin(B3b, 1*SegCnt, SegCnt), 3329 true = verify_bin(B3c, 2*SegCnt, SegCnt), 3330 true = verify_bin(B3d, 3*SegCnt, SegCnt), 3331 %% 3332 %% Segmented 3333 {ok, B3g} = ?FILE_MODULE:pread(FD3, 3*SegSize, 2*SegSize), 3334 {ok, B3f} = ?FILE_MODULE:pread(FD3, 1*SegSize, 2*SegSize), 3335 {ok, B3e} = ?FILE_MODULE:pread(FD3, 0*SegSize, 1*SegSize), 3336 true = verify_bin(B3e, 0*SegCnt, 1*SegCnt), 3337 true = verify_bin(B3f, 1*SegCnt, 2*SegCnt), 3338 true = verify_bin(B3g, 3*SegCnt, 1*SegCnt), 3339 %% 3340 ok = ?FILE_MODULE:close(FD3), 3341 %% 3342 %% pread/2 3343 %% 3344 {ok, FD5} = ?FILE_MODULE:open(Name, [read, raw, binary]), 3345 %% 3346 %% +---+---+---+---+ 3347 %% | 4 | 3 | 2 | 1 | 3348 %% +---+---+---+---+ 3349 %% < ^ > 3350 {ok, [B5d, B5c, B5b, B5a]} = 3351 ?FILE_MODULE:pread(FD5, [{3*SegSize, SegSize}, 3352 {2*SegSize, SegSize}, 3353 {1*SegSize, SegSize}, 3354 {0*SegSize, SegSize}]), 3355 true = verify_bin(B5a, 0*SegCnt, SegCnt), 3356 true = verify_bin(B5b, 1*SegCnt, SegCnt), 3357 true = verify_bin(B5c, 2*SegCnt, SegCnt), 3358 true = verify_bin(B5d, 3*SegCnt, SegCnt), 3359 %% 3360 %% +---+-------+-------+ 3361 %% | 3 | 2 | 1 | 3362 %% +---+-------+-------+ 3363 %% < ^ ^ > 3364 {ok, [B5g, B5f, B5e]} = 3365 ?FILE_MODULE:pread(FD5, [{3*SegSize, 2*SegSize}, 3366 {1*SegSize, 2*SegSize}, 3367 {0*SegSize, 1*SegSize}]), 3368 true = verify_bin(B5e, 0*SegCnt, 1*SegCnt), 3369 true = verify_bin(B5f, 1*SegCnt, 2*SegCnt), 3370 true = verify_bin(B5g, 3*SegCnt, 1*SegCnt), 3371 %% 3372 %% 3373 %% +-------+-----------+ 3374 %% | 2 | 1 | 3375 %% +-------+-----------+ 3376 %% < ^ ^ > 3377 {ok, [B5i, B5h]} = 3378 ?FILE_MODULE:pread(FD5, [{2*SegSize, 3*SegSize}, 3379 {0*SegSize, 2*SegSize}]), 3380 true = verify_bin(B5h, 0*SegCnt, 2*SegCnt), 3381 true = verify_bin(B5i, 2*SegCnt, 2*SegCnt), 3382 %% 3383 %% +-------+---+---+ 3384 %% | 3 | 2 | 1 | 3385 %% +-------+---+---+ 3386 %% < ^ ^ > 3387 {ok, [B5l, B5k, B5j]} = 3388 ?FILE_MODULE:pread(FD5, [{3*SegSize, 1*SegSize}, 3389 {2*SegSize, 1*SegSize}, 3390 {0*SegSize, 2*SegSize}]), 3391 true = verify_bin(B5j, 0*SegCnt, 2*SegCnt), 3392 true = verify_bin(B5k, 2*SegCnt, 1*SegCnt), 3393 true = verify_bin(B5l, 3*SegCnt, 1*SegCnt), 3394 %% 3395 %% Real time response time test. 3396 %% 3397 Req = lists:flatten(lists:duplicate(17, 3398 [{2*SegSize, 2*SegSize}, 3399 {0*SegSize, 2*SegSize}])), 3400 {{ok, _}, Comment} = 3401 response_analysis(?FILE_MODULE, pread, [FD5, Req]), 3402 ok = ?FILE_MODULE:close(FD5), 3403 %% 3404 [] = flush(), 3405 {comment, Comment}. 3406 3407 3408 3409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3410 3411 3412 3413%% Tests the segmenting of large writes. 3414segment_write(Config) when is_list(Config) -> 3415 Name = filename:join(proplists:get_value(priv_dir, Config), 3416 ?MODULE_STRING ++ "_segment_write"), 3417 SegSize = 256*1024, 3418 SegCnt = SegSize div 4, 3419 Cnt = 4 * SegCnt, 3420 Bin = create_bin(0, Cnt), 3421 %% 3422 %% write/2 3423 %% 3424 %% Not segmented 3425 {ok, FD1} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3426 ok = ?FILE_MODULE:write(FD1, subbin(Bin, 0*SegSize, 1*SegSize)), 3427 ok = ?FILE_MODULE:write(FD1, subbin(Bin, 1*SegSize, 1*SegSize)), 3428 ok = ?FILE_MODULE:write(FD1, subbin(Bin, 2*SegSize, 1*SegSize)), 3429 ok = ?FILE_MODULE:write(FD1, subbin(Bin, 3*SegSize, 1*SegSize)), 3430 ok = ?FILE_MODULE:close(FD1), 3431 true = verify_file(Name, Cnt), 3432 %% 3433 %% Segmented 3434 {ok, FD2} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3435 ok = ?FILE_MODULE:write(FD2, subbin(Bin, 0*SegSize, 1*SegSize)), 3436 ok = ?FILE_MODULE:write(FD2, subbin(Bin, 1*SegSize, 2*SegSize)), 3437 ok = ?FILE_MODULE:write(FD2, subbin(Bin, 3*SegSize, 1*SegSize)), 3438 ok = ?FILE_MODULE:close(FD2), 3439 true = verify_file(Name, Cnt), 3440 %% 3441 %% +---+---+---+---+ 3442 %% | | | | | 3443 %% +---+---+---+---+ 3444 %% < ^ > 3445 ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize), 3446 subbin(Bin, 1*SegSize, 1*SegSize), 3447 subbin(Bin, 2*SegSize, 1*SegSize), 3448 subbin(Bin, 3*SegSize, 1*SegSize)]), 3449 true = verify_file(Name, Cnt), 3450 %% 3451 %% +---+-------+---+ 3452 %% | | | | 3453 %% +---+-------+---+ 3454 %% < ^ ^ > 3455 ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize), 3456 subbin(Bin, 1*SegSize, 2*SegSize), 3457 subbin(Bin, 3*SegSize, 1*SegSize)]), 3458 true = verify_file(Name, Cnt), 3459 %% 3460 %% +-------+-------+ 3461 %% | | | 3462 %% +-------+-------+ 3463 %% < ^ ^ > 3464 ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize), 3465 subbin(Bin, 2*SegSize, 2*SegSize)]), 3466 true = verify_file(Name, Cnt), 3467 %% 3468 %% +-------+---+---+ 3469 %% | | | | 3470 %% +-------+---+---+ 3471 %% < ^ ^ > 3472 ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize), 3473 subbin(Bin, 2*SegSize, 1*SegSize), 3474 subbin(Bin, 3*SegSize, 1*SegSize)]), 3475 true = verify_file(Name, Cnt), 3476 %% 3477 %% pwrite/3 3478 %% 3479 %% Not segmented 3480 {ok, FD3} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3481 ok = ?FILE_MODULE:pwrite(FD3, 3*SegSize, 3482 subbin(Bin, 3*SegSize, 1*SegSize)), 3483 ok = ?FILE_MODULE:pwrite(FD3, 2*SegSize, 3484 subbin(Bin, 2*SegSize, 1*SegSize)), 3485 ok = ?FILE_MODULE:pwrite(FD3, 1*SegSize, 3486 subbin(Bin, 1*SegSize, 1*SegSize)), 3487 ok = ?FILE_MODULE:pwrite(FD3, 0*SegSize, 3488 subbin(Bin, 0*SegSize, 1*SegSize)), 3489 ok = ?FILE_MODULE:close(FD3), 3490 true = verify_file(Name, Cnt), 3491 %% 3492 %% Segmented 3493 {ok, FD4} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3494 ok = ?FILE_MODULE:pwrite(FD4, 3*SegSize, 3495 subbin(Bin, 3*SegSize, 1*SegSize)), 3496 ok = ?FILE_MODULE:pwrite(FD4, 1*SegSize, 3497 subbin(Bin, 1*SegSize, 2*SegSize)), 3498 ok = ?FILE_MODULE:pwrite(FD4, 0*SegSize, 3499 subbin(Bin, 0*SegSize, 1*SegSize)), 3500 ok = ?FILE_MODULE:close(FD4), 3501 true = verify_file(Name, Cnt), 3502 3503 3504 3505 %% 3506 %% pwrite/2 3507 %% 3508 %% Not segmented 3509 {ok, FD5} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3510 ok = ?FILE_MODULE:pwrite(FD5, [{3*SegSize, 3511 subbin(Bin, 3*SegSize, 1*SegSize)}]), 3512 ok = ?FILE_MODULE:pwrite(FD5, [{2*SegSize, 3513 subbin(Bin, 2*SegSize, 1*SegSize)}]), 3514 ok = ?FILE_MODULE:pwrite(FD5, [{1*SegSize, 3515 subbin(Bin, 1*SegSize, 1*SegSize)}]), 3516 ok = ?FILE_MODULE:pwrite(FD5, [{0*SegSize, 3517 subbin(Bin, 0*SegSize, 1*SegSize)}]), 3518 ok = ?FILE_MODULE:close(FD5), 3519 true = verify_file(Name, Cnt), 3520 %% 3521 %% Segmented 3522 {ok, FD6} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3523 ok = ?FILE_MODULE:pwrite(FD6, [{3*SegSize, 3524 subbin(Bin, 3*SegSize, 1*SegSize)}]), 3525 ok = ?FILE_MODULE:pwrite(FD6, [{1*SegSize, 3526 subbin(Bin, 1*SegSize, 2*SegSize)}]), 3527 ok = ?FILE_MODULE:pwrite(FD6, [{0*SegSize, 3528 subbin(Bin, 0*SegSize, 1*SegSize)}]), 3529 ok = ?FILE_MODULE:close(FD6), 3530 true = verify_file(Name, Cnt), 3531 %% 3532 %% +---+---+---+---+ 3533 %% | 4 | 3 | 2 | 1 | 3534 %% +---+---+---+---+ 3535 %% < ^ > 3536 ok = pwrite_file(Name, [{3*SegSize, 3537 subbin(Bin, 3*SegSize, 1*SegSize)}, 3538 {2*SegSize, 3539 subbin(Bin, 2*SegSize, 1*SegSize)}, 3540 {1*SegSize, 3541 subbin(Bin, 1*SegSize, 1*SegSize)}, 3542 {0*SegSize, 3543 subbin(Bin, 0*SegSize, 1*SegSize)}]), 3544 true = verify_file(Name, Cnt), 3545 %% 3546 %% +---+-------+---+ 3547 %% | 3 | 2 | 1 | 3548 %% +---+-------+---+ 3549 %% < ^ ^ > 3550 ok = pwrite_file(Name, [{3*SegSize, 3551 subbin(Bin, 3*SegSize, 1*SegSize)}, 3552 {1*SegSize, 3553 subbin(Bin, 1*SegSize, 2*SegSize)}, 3554 {0*SegSize, 3555 subbin(Bin, 0*SegSize, 1*SegSize)}]), 3556 true = verify_file(Name, Cnt), 3557 %% 3558 %% +-------+-------+ 3559 %% | 2 | 1 | 3560 %% +-------+-------+ 3561 %% < ^ ^ > 3562 ok = pwrite_file(Name, [{2*SegSize, 3563 subbin(Bin, 2*SegSize, 2*SegSize)}, 3564 {0*SegSize, 3565 subbin(Bin, 0*SegSize, 2*SegSize)}]), 3566 true = verify_file(Name, Cnt), 3567 %% 3568 %% +-------+---+---+ 3569 %% | 3 | 2 | 1 | 3570 %% +-------+---+---+ 3571 %% < ^ ^ > 3572 ok = pwrite_file(Name, [{3*SegSize, 3573 subbin(Bin, 3*SegSize, 1*SegSize)}, 3574 {2*SegSize, 3575 subbin(Bin, 2*SegSize, 1*SegSize)}, 3576 {0*SegSize, 3577 subbin(Bin, 0*SegSize, 2*SegSize)}]), 3578 true = verify_file(Name, Cnt), 3579 %% 3580 %% Real time response time test. 3581 %% 3582 {ok, FD7} = ?FILE_MODULE:open(Name, [write, raw, binary]), 3583 Req = lists:flatten(lists:duplicate(17, 3584 [{2*SegSize, 3585 subbin(Bin, 2*SegSize, 2*SegSize)}, 3586 {0*SegSize, 3587 subbin(Bin, 0*SegSize, 2*SegSize)}])), 3588 {ok, Comment} = 3589 response_analysis(?FILE_MODULE, pwrite, [FD7, Req]), 3590 ok = ?FILE_MODULE:close(FD7), 3591 %% 3592 [] = flush(), 3593 {comment, Comment}. 3594 3595 3596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3597 3598%% Test Dets special indirect pread. 3599ipread(Config) when is_list(Config) -> 3600 Dir = proplists:get_value(priv_dir, Config), 3601 ok = ipread_int(Dir, [raw, binary]), 3602 ok = ipread_int(Dir, [raw]), 3603 ok = ipread_int(Dir, [binary]), 3604 ok = ipread_int(Dir, []), 3605 ok = ipread_int(Dir, [ram, binary]), 3606 ok = ipread_int(Dir, [ram]), 3607 %% 3608 [] = flush(), 3609 ok. 3610 3611ipread_int(Dir, ModeList) -> 3612 Name = 3613 filename:join(Dir, 3614 lists:flatten([?MODULE_STRING, "_ipread", 3615 lists:map(fun (X) -> 3616 ["_", atom_to_list(X)] 3617 end, 3618 ModeList)])), 3619 io:format("ipread_int<~p, ~p>~n", [Name, ModeList]), 3620 {Conv, Sizeof} = 3621 case lists:member(binary, ModeList) of 3622 true -> 3623 {fun (Bin) when is_binary(Bin) -> Bin; 3624 (List) when is_list(List) -> list_to_binary(List) 3625 end, 3626 fun erlang:byte_size/1}; 3627 false -> 3628 {fun (Bin) when is_binary(Bin) -> binary_to_list(Bin); 3629 (List) when is_list(List) -> List 3630 end, 3631 fun erlang:length/1} 3632 end, 3633 Pos = 4711, 3634 Data = Conv("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG"), 3635 Size = Sizeof(Data), 3636 Init = Conv(" "), 3637 SizeInit = Sizeof(Init), 3638 Head = Conv(<<Size:32/big-unsigned, Pos:32/big-unsigned>>), 3639 Filler = Conv(bytes($ , Pos-SizeInit-Sizeof(Head))), 3640 Size1 = Size+1, 3641 SizePos = Size+Pos, 3642 %% 3643 {ok, FD} = ?FILE_MODULE:open(Name, [write, read | ModeList]), 3644 ok = ?FILE_MODULE:truncate(FD), 3645 ok = ?FILE_MODULE:write(FD, Init), 3646 ok = ?FILE_MODULE:write(FD, Head), 3647 ok = ?FILE_MODULE:write(FD, Filler), 3648 ok = ?FILE_MODULE:write(FD, Data), 3649 %% Correct read 3650 {ok, {Size, Pos, Data}} = 3651 ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, infinity), 3652 %% Invalid header - size > max 3653 eof = 3654 ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size-1), 3655 %% Data block protudes over eof 3656 ok = 3657 ?FILE_MODULE:pwrite(FD, SizeInit, 3658 <<Size1:32/big-unsigned, 3659 Pos:32/big-unsigned>>), 3660 {ok, {Size1, Pos, Data}} = 3661 ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size1), 3662 %% Data block outside file 3663 ok = 3664 ?FILE_MODULE:pwrite(FD, SizeInit, 3665 <<Size:32/big-unsigned, 3666 SizePos:32/big-unsigned>>), 3667 {ok, {Size, SizePos, eof}} = 3668 ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size), 3669 %% Zero size 3670 ok = 3671 ?FILE_MODULE:pwrite(FD, SizeInit, 3672 <<0:32/big-unsigned, 3673 Pos:32/big-unsigned>>), 3674 {ok, {0, Pos, eof}} = 3675 ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size), 3676 %% Invalid header - protudes over eof 3677 eof = 3678 ?FILE_MODULE:ipread_s32bu_p32bu(FD, 3679 Pos+Size-(Sizeof(Head)-1), 3680 infinity), 3681 %% Header not even in file 3682 eof = 3683 ?FILE_MODULE:ipread_s32bu_p32bu(FD, Pos+Size, infinity), 3684 %% 3685 ok = ?FILE_MODULE:close(FD), 3686 ok. 3687 3688 3689 3690%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3691 3692%% Tests interleaved read and writes. 3693interleaved_read_write(Config) when is_list(Config) -> 3694 Dir = proplists:get_value(priv_dir, Config), 3695 File = 3696 filename:join(Dir, ?MODULE_STRING++"interleaved_read_write.txt"), 3697 {ok,F1} = ?FILE_MODULE:open(File, [write]), 3698 ok = ?FILE_MODULE:write(F1, "data---r1."), % 10 chars each 3699 ok = ?FILE_MODULE:write(F1, "data---r2."), 3700 ok = ?FILE_MODULE:write(F1, "data---r3."), 3701 ok = ?FILE_MODULE:close(F1), 3702 {ok,F2} = ?FILE_MODULE:open(File, [read, write]), 3703 {ok, "data---r1."} = ?FILE_MODULE:read(F2, 10), 3704 ok = ?FILE_MODULE:write(F2, "data---w2."), 3705 ok = ?FILE_MODULE:close(F2), 3706 {ok,F3} = ?FILE_MODULE:open(File, [read]), 3707 {ok, "data---r1."} = ?FILE_MODULE:read(F3, 10), 3708 {ok, "data---w2."} = ?FILE_MODULE:read(F3, 10), 3709 {ok, "data---r3."} = ?FILE_MODULE:read(F3, 10), 3710 eof = ?FILE_MODULE:read(F3, 1), 3711 ok = ?FILE_MODULE:close(F2), 3712 %% 3713 [] = flush(), 3714 ok. 3715 3716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3717 3718%% OTP-5814. eval/consult/script return correct line numbers. 3719otp_5814(Config) when is_list(Config) -> 3720 PrivDir = proplists:get_value(priv_dir, Config), 3721 File = filename:join(PrivDir, "otp_5814"), 3722 Path = [PrivDir], 3723 ok = file:write_file(File, <<"{a,b,c}. 3724 a. 3725b. 3726c. 3727{d,e, 3728 [}.">>), 3729 {error, {6,erl_parse,_}} = file:eval(File), 3730 {error, {6,erl_parse,_}} = file:consult(File), 3731 {error, {6,erl_parse,_}} = file:path_consult(Path, File), 3732 {error, {6,erl_parse,_}} = file:path_eval(Path, File), 3733 {error, {6,erl_parse,_}} = file:script(File), 3734 {error, {6,erl_parse,_}} = file:path_script(Path, File), 3735 3736 ok = file:write_file(File, <<>>), 3737 {error, {1,file,undefined_script}} = file:path_script(Path, File), 3738 3739 %% The error is not propagated... 3740 ok = file:write_file(File, <<"a. 3741 b. 37421/0.">>), 3743 {error, {3, file, {error, badarith, _}}} = file:eval(File), 3744 3745ok = file:write_file(File, <<"erlang:raise(throw, apa, []).">>), 3746{error, {1, file, {throw, apa, _}}} = file:eval(File), 3747 3748file:delete(File), 3749ok. 3750 3751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3752 3753%% OTP-10852. +fnu and latin1 filenames. 3754otp_10852(Config) when is_list(Config) -> 3755 Node = start_node(erl_pp_helper, "+fnu"), 3756 Dir = proplists:get_value(priv_dir, Config), 3757 B = filename:join(Dir, <<"\xE4">>), 3758 ok = rpc_call(Node, get_cwd, [B]), 3759 {error, no_translation} = rpc_call(Node, set_cwd, [B]), 3760 ok = rpc_call(Node, delete, [B]), 3761 ok = rpc_call(Node, rename, [B, B]), 3762 ok = rpc_call(Node, read_file_info, [B]), 3763 ok = rpc_call(Node, read_link_info, [B]), 3764 ok = rpc_call(Node, read_link, [B]), 3765 ok = rpc_call(Node, write_file_info, [B,#file_info{}]), 3766 ok = rpc_call(Node, list_dir, [B]), 3767 ok = rpc_call(Node, list_dir_all, [B]), 3768 ok = rpc_call(Node, read_file, [B]), 3769 ok = rpc_call(Node, make_link, [B,B]), 3770 case rpc_call(Node, make_symlink, [B,B]) of 3771 {error, eilseq} -> 3772 %% Some versions of OS X refuse to create files with illegal names. 3773 {unix,darwin} = os:type(); 3774 {error, eperm} -> 3775 %% The test user might not have permission to create symlinks. 3776 {win32,_} = os:type(); 3777 ok -> 3778 ok 3779 end, 3780 ok = rpc_call(Node, delete, [B]), 3781 case rpc_call(Node, make_dir, [B]) of 3782 {error, eilseq} -> 3783 {unix,darwin} = os:type(); 3784 ok -> 3785 ok 3786 end, 3787 ok = rpc_call(Node, del_dir, [B]), 3788 case rpc_call(Node, write_file, [B,B]) of 3789 {error, eilseq} -> 3790 {unix,darwin} = os:type(); 3791 ok -> 3792 {ok, Fd} = rpc_call(Node, open, [B,[read]]), 3793 ok = rpc_call(Node, close, [Fd]), 3794 {ok,0} = rpc_call(Node, copy, [B,B]), 3795 {ok, Fd2, B} = rpc_call(Node, path_open, [["."], B, [read]]), 3796 ok = rpc_call(Node, close, [Fd2]) 3797 end, 3798 true = test_server:stop_node(Node), 3799 ok. 3800 3801rpc_call(N, F, As) -> 3802 case rpc:call(N, ?FILE_MODULE, F, As) of 3803 {error, enotsup} -> ok; 3804 {error, enoent} -> ok; 3805 {error, badarg} -> ok; 3806 Else -> Else 3807 end. 3808 3809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3810 3811large_file() -> 3812 [{timetrap,{minutes,20}}]. 3813 3814%% Tests positioning in large files (> 4G). 3815large_file(Config) when is_list(Config) -> 3816 run_large_file_test(Config, 3817 fun(Name) -> do_large_file(Name) end, 3818 "_large_file"). 3819 3820do_large_file(Name) -> 3821 S = "1234567890", 3822 L = length(S), 3823 R = lists:reverse(S), 3824 P = 1 bsl 32, 3825 Ss = lists:sort(S), 3826 Rs = lists:reverse(Ss), 3827 {ok,F} = ?FILE_MODULE:open(Name, [raw,read,write]), 3828 ok = ?FILE_MODULE:write(F, S), 3829 {ok,P} = ?FILE_MODULE:position(F, P), 3830 ok = ?FILE_MODULE:write(F, R), 3831 {ok,0} = ?FILE_MODULE:position(F, bof), 3832 {ok,S} = ?FILE_MODULE:read(F, L), 3833 {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), 3834 {ok,R} = ?FILE_MODULE:read(F, L+1), 3835 {ok,S} = ?FILE_MODULE:pread(F, 0, L), 3836 {ok,R} = ?FILE_MODULE:pread(F, P, L+1), 3837 ok = ?FILE_MODULE:pwrite(F, 0, Ss), 3838 ok = ?FILE_MODULE:pwrite(F, P, Rs), 3839 {ok,0} = ?FILE_MODULE:position(F, bof), 3840 {ok,Ss} = ?FILE_MODULE:read(F, L), 3841 {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), 3842 {ok,Rs} = ?FILE_MODULE:read(F, L+1), 3843 ok = ?FILE_MODULE:close(F), 3844 %% Reopen the file with 'append'; used to fail on Windows causing 3845 %% writes to go to the beginning of the file for files > 4GB. 3846 PL = P + L, 3847 PLL = PL + L, 3848 {ok,F1} = ?FILE_MODULE:open(Name, [raw,read,write,append]), 3849 ok = ?FILE_MODULE:write(F1, R), 3850 {ok,PLL} = ?FILE_MODULE:position(F1, {cur,0}), 3851 {ok,Rs} = ?FILE_MODULE:pread(F1, P, L), 3852 {ok,PL} = ?FILE_MODULE:position(F1, {eof,-L}), 3853 {ok,R} = ?FILE_MODULE:read(F1, L+1), 3854 ok = ?FILE_MODULE:close(F1), 3855 3856 ok. 3857 3858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3859 3860large_write() -> 3861 [{timetrap,{minutes,20}}]. 3862 3863large_write(Config) when is_list(Config) -> 3864 run_large_file_test(Config, 3865 fun(Name) -> do_large_write(Name) end, 3866 "_large_write"). 3867 3868do_large_write(Name) -> 3869 Memsize = memsize(), 3870 io:format("Memsize = ~w Bytes~n", [Memsize]), 3871 case {erlang:system_info(wordsize),Memsize} of 3872 {4,_} -> 3873 {skip,"Needs a 64-bit emulator"}; 3874 {8,N} when N < 6 bsl 30 -> 3875 {skip, 3876 "This machine has < 6 GB memory: " 3877 ++integer_to_list(N)}; 3878 {8,_} -> 3879 Size = 4*1024*1024*1024+1, 3880 Bin = <<0:Size/unit:8>>, 3881 ok = file:write_file(Name, Bin), 3882 {ok,#file_info{size=Size}} = file:read_file_info(Name), 3883 ok 3884 end. 3885 3886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3887 3888%% Benchmarks 3889%% 3890%% Note that we only measure the time it takes to run the isolated file 3891%% operations and that the actual test runtime can differ significantly, 3892%% especially on the write side as the files need to be truncated before 3893%% writing. 3894 3895large_writes(Config) when is_list(Config) -> 3896 Modes = [raw, binary], 3897 OpCount = 4096, 3898 Data = <<0:(64 bsl 10)/unit:8>>, 3899 run_write_benchmark(Config, Modes, OpCount, Data). 3900 3901large_writes_delayed(Config) when is_list(Config) -> 3902 %% Each write is exactly as large as the delay buffer, causing the writes 3903 %% to pass through each time, giving us a decent idea of how much overhead 3904 %% delayed_write adds. 3905 Modes = [raw, binary, {delayed_write, 64 bsl 10, 2000}], 3906 OpCount = 4096, 3907 Data = <<0:(64 bsl 10)/unit:8>>, 3908 run_write_benchmark(Config, Modes, OpCount, Data). 3909 3910tiny_writes(Config) when is_list(Config) -> 3911 Modes = [raw, binary], 3912 OpCount = 512 bsl 10, 3913 Data = <<0>>, 3914 run_write_benchmark(Config, Modes, OpCount, Data). 3915 3916tiny_writes_delayed(Config) when is_list(Config) -> 3917 Modes = [raw, binary, {delayed_write, 512 bsl 10, 2000}], 3918 OpCount = 512 bsl 10, 3919 Data = <<0>>, 3920 run_write_benchmark(Config, Modes, OpCount, Data). 3921 3922%% The read benchmarks assume that "benchmark_scratch_file" has been filled by 3923%% the write benchmarks. 3924 3925tiny_reads(Config) when is_list(Config) -> 3926 Modes = [raw, binary], 3927 OpCount = 512 bsl 10, 3928 run_read_benchmark(Config, Modes, OpCount, 1). 3929 3930tiny_reads_ahead(Config) when is_list(Config) -> 3931 Modes = [raw, binary, {read_ahead, 512 bsl 10}], 3932 OpCount = 512 bsl 10, 3933 run_read_benchmark(Config, Modes, OpCount, 1). 3934 3935run_write_benchmark(Config, Modes, OpCount, Data) -> 3936 run_benchmark(Config, [write | Modes], OpCount, fun file:write/2, Data). 3937 3938run_read_benchmark(Config, Modes, OpCount, OpSize) -> 3939 run_benchmark(Config, [read | Modes], OpCount, fun file:read/2, OpSize). 3940 3941run_benchmark(Config, Modes, OpCount, Fun, Arg) -> 3942 ScratchDir = proplists:get_value(priv_dir, Config), 3943 Path = filename:join(ScratchDir, "benchmark_scratch_file"), 3944 {ok, Fd} = file:open(Path, Modes), 3945 submit_throughput_results(Fun, [Fd, Arg], OpCount). 3946 3947submit_throughput_results(Fun, Args, Times) -> 3948 MSecs = measure_repeated_file_op(Fun, Args, Times, millisecond), 3949 IOPS = trunc(Times * (1000 / MSecs)), 3950 ct_event:notify(#event{ name = benchmark_data, data = [{value,IOPS}] }), 3951 {comment, io_lib:format("~p IOPS, ~p ms", [IOPS, trunc(MSecs)])}. 3952 3953measure_repeated_file_op(Fun, Args, Times, Unit) -> 3954 Start = os:perf_counter(Unit), 3955 repeated_apply(Fun, Args, Times), 3956 os:perf_counter(Unit) - Start. 3957 3958repeated_apply(_F, _Args, Times) when Times =< 0 -> 3959 ok; 3960repeated_apply(F, Args, Times) -> 3961 erlang:apply(F, Args), 3962 repeated_apply(F, Args, Times - 1). 3963 3964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3965 3966 3967response_analysis(Module, Function, Arguments) -> 3968 Parent = self(), 3969 erlang:yield(), % Schedule out before test 3970 Child = 3971 spawn_link( 3972 fun () -> 3973 receive {Parent, start, Ts} -> ok end, 3974 Stat = 3975 iterate(response_stat(response_stat(init, Ts), 3976 micro_ts()), 3977 done, 3978 fun (S) -> 3979 erlang:yield(), 3980 receive 3981 {Parent, stop} -> 3982 done 3983 after 0 -> 3984 response_stat(S, micro_ts()) 3985 end 3986 end), 3987 Parent ! {self(), stopped, response_stat(Stat, micro_ts())} 3988 end), 3989 Child ! {Parent, start, micro_ts()}, 3990 Result = apply(Module, Function, Arguments), 3991 Child ! {Parent, stop}, 3992 {N, Sum, _, M, Max} = receive {Child, stopped, X} -> X end, 3993 Mean_ms = (0.001*Sum) / (N-1), 3994 Max_ms = 0.001 * Max, 3995 Comment = 3996 lists:flatten( 3997 io_lib:format( 3998 "Scheduling interval: Mean = ~.3f ms, " 3999 ++"Max = ~.3f ms for no ~p of ~p.~n", 4000 [Mean_ms, Max_ms, M, (N-1)])), 4001 {Result, Comment}. 4002 4003micro_ts() -> 4004 erlang:monotonic_time(microsecond). 4005 4006response_stat(init, Ts) -> 4007 {0, 0, Ts, 0, 0}; 4008response_stat({N, Sum, Ts0, M, Max}, Ts) -> 4009 D = Ts - Ts0, 4010 if D > Max -> 4011 {N+1, Sum+D, Ts, N, D}; 4012 true -> 4013 {N+1, Sum+D, Ts, M, Max} 4014 end. 4015 4016 4017 4018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4019 4020 4021 4022%% This function is kept just for benchmarking reasons. 4023%% create_file/2 below is some 44 times faster. 4024 4025create_file_slow(Name, N) when is_integer(N), N >= 0 -> 4026 {ok, FD} = 4027 ?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]), 4028 ok = create_file_slow(FD, 0, N), 4029 ok = ?FILE_MODULE:close(FD), 4030 ok. 4031 4032create_file_slow(_FD, M, M) -> 4033 ok; 4034create_file_slow(FD, M, N) -> 4035 ok = ?FILE_MODULE:write(FD, <<M:32/unsigned>>), 4036 create_file_slow(FD, M+1, N). 4037 4038 4039 4040%% Creates a file 'Name' containing 'N' unsigned 32 bit integers 4041%% from 0 to N-1. 4042 4043create_file(Name, N) when is_integer(N), N >= 0 -> 4044 {ok, FD} = 4045 ?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]), 4046 ok = create_file(FD, 0, N), 4047 ok = ?FILE_MODULE:close(FD), 4048 ok. 4049 4050create_file(_FD, M, M) -> 4051 ok; 4052create_file(FD, M, N) when M + 1024 =< N -> 4053 create_file(FD, M, M + 1024, []), 4054 create_file(FD, M + 1024, N); 4055create_file(FD, M, N) -> 4056 create_file(FD, M, N, []). 4057 4058create_file(FD, M, M, R) -> 4059 ok = ?FILE_MODULE:write(FD, R); 4060create_file(FD, M, N0, R) when M + 8 =< N0 -> 4061 N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4, 4062 N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8, 4063 create_file(FD, M, N8, 4064 [<<N8:32/unsigned, N7:32/unsigned, 4065 N6:32/unsigned, N5:32/unsigned, 4066 N4:32/unsigned, N3:32/unsigned, 4067 N2:32/unsigned, N1:32/unsigned>> | R]); 4068create_file(FD, M, N0, R) -> 4069 N1 = N0-1, 4070 create_file(FD, M, N1, [<<N1:32/unsigned>> | R]). 4071 4072 4073 4074create_bin(M, N) when is_integer(M), is_integer(N), N >= 0, M >= 0 -> 4075 create_bin(M, M+N, []). 4076 4077create_bin(N, N, R) -> 4078 list_to_binary(R); 4079create_bin(M, N0, R) when M+8 =< N0 -> 4080 N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4, 4081 N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8, 4082 create_bin(M, N8, 4083 [<<N8:32/unsigned, N7:32/unsigned, 4084 N6:32/unsigned, N5:32/unsigned, 4085 N4:32/unsigned, N3:32/unsigned, 4086 N2:32/unsigned, N1:32/unsigned>> | R]); 4087create_bin(M, N0, R) -> 4088 N1 = N0-1, 4089 create_bin(M, N1, [<<N1:32/unsigned>> | R]). 4090 4091 4092 4093 4094verify_bin(<<>>, _, 0) -> 4095 true; 4096verify_bin(<<>>, _, _) -> 4097 false; 4098verify_bin(Bin, N, Cnt) -> 4099 N0 = N + 0, N1 = N + 1, N2 = N + 2, N3 = N + 3, 4100 N4 = N + 4, N5 = N + 5, N6 = N + 6, N7 = N + 7, 4101 case Bin of 4102 <<N0:32/unsigned, N1:32/unsigned, N2:32/unsigned, N3:32/unsigned, 4103 N4:32/unsigned, N5:32/unsigned, N6:32/unsigned, N7:32/unsigned, 4104 B/binary>> -> 4105 verify_bin(B, N+8, Cnt-8); 4106 <<N:32/unsigned, B/binary>> -> 4107 verify_bin(B, N+1, Cnt-1); 4108 _ -> 4109 false 4110 end. 4111 4112 4113 4114verify_file(Name, N) when is_integer(N), N >= 0 -> 4115 case ?FILE_MODULE:open(Name, [raw, read, binary]) of 4116 {ok, FD} -> 4117 Result = verify_file(FD, 0, 64*1024, N), 4118 ok = ?FILE_MODULE:close(FD), 4119 Result; 4120 Error -> 4121 Error 4122 end. 4123 4124verify_file(FD, N, _, N) -> 4125 case ?FILE_MODULE:read(FD, 1) of 4126 eof -> 4127 true; 4128 {ok, _} -> 4129 false 4130 end; 4131verify_file(FD, M, Cnt, N) when M+Cnt =< N -> 4132 case ?FILE_MODULE:read(FD, 4*Cnt) of 4133 {ok, Bin} -> 4134 case verify_bin(Bin, M, Cnt) of 4135 true -> 4136 verify_file(FD, M+Cnt, Cnt, N); 4137 false -> 4138 false 4139 end; 4140 _ -> 4141 false 4142 end; 4143verify_file(FD, M, _Cnt, N) -> 4144 verify_file(FD, M, N-M, N). 4145 4146 4147 4148subbin(Bin, M, N) -> 4149 <<_:M/binary, B:N/binary, _/binary>> = Bin, 4150 B. 4151 4152 4153 4154write_file(Name, Data) -> 4155 case ?FILE_MODULE:open(Name, [raw, write, binary]) of 4156 {ok, FD} -> 4157 Result = ?FILE_MODULE:write(FD, Data), 4158 case {Result, ?FILE_MODULE:close(FD)} of 4159 {ok, R} -> R; 4160 _ -> Result 4161 end; 4162 Error -> 4163 Error 4164 end. 4165 4166pwrite_file(Name, Data) -> 4167 case ?FILE_MODULE:open(Name, [raw, write, binary]) of 4168 {ok, FD} -> 4169 Result = ?FILE_MODULE:pwrite(FD, Data), 4170 case {Result, ?FILE_MODULE:close(FD)} of 4171 {ok, R} -> R; 4172 _ -> Result 4173 end; 4174 Error -> 4175 Error 4176 end. 4177 4178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4179%% Read_line tests 4180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4181 4182 4183read_line_testdata(PrivDir) -> 4184 All0 = [{fun read_line_create0/1,"Testdata1.txt",5,10}, 4185 {fun read_line_create1/1,"Testdata2.txt",401,802}, 4186 {fun read_line_create2/1,"Testdata3.txt",1,2}, 4187 {fun read_line_create3/1,"Testdata4.txt",601,fail}, 4188 {fun read_line_create4/1,"Testdata5.txt",601,1002}, 4189 {fun read_line_create5/1,"Testdata6.txt",601,1202}, 4190 {fun read_line_create6/1,"Testdata7.txt",601,1202}, 4191 {fun read_line_create7/1,"Testdata8.txt",4001,8002}], 4192 [ {A,filename:join([PrivDir,B]),C,D} || {A,B,C,D} <- All0 ]. 4193 4194read_line_create_files(TestData) -> 4195 [ Function(File) || {Function,File,_,_} <- TestData ]. 4196 4197read_line_remove_files(TestData) -> 4198 [ file:delete(File) || {_Function,File,_,_} <- TestData ]. 4199 4200%% read_line with ?PRIM_FILE. 4201read_line_1(Config) when is_list(Config) -> 4202 PrivDir = proplists:get_value(priv_dir, Config), 4203 All = read_line_testdata(PrivDir), 4204 read_line_create_files(All), 4205 [ begin 4206 io:format("read_line_all: ~s~n",[File]), 4207 {X,_} = read_line_all(File), 4208 true 4209 end || {_,File,X,_} <- All ], 4210 [ begin 4211 io:format("read_line_all_alternating: ~s~n",[File]), 4212 {Y,_} = read_line_all_alternating(File), 4213 true 4214 end || {_,File,_,Y} <- All , Y =/= fail], 4215 [ begin 4216 io:format("read_line_all_alternating (failing as should): ~s~n",[File]), 4217 {'EXIT',_} = (catch read_line_all_alternating(File)), 4218 true 4219 end || {_,File,_,Y} <- All , Y =:= fail], 4220 read_line_remove_files(All), 4221 ok. 4222%% read_line with file. 4223read_line_2(Config) when is_list(Config) -> 4224 PrivDir = proplists:get_value(priv_dir, Config), 4225 All = read_line_testdata(PrivDir), 4226 read_line_create_files(All), 4227 [ begin 4228 io:format("read_line_all: ~s~n",[File]), 4229 {X,_} = read_line_all2(File), 4230 true 4231 end || {_,File,X,_} <- All ], 4232 [ begin 4233 io:format("read_line_all_alternating: ~s~n",[File]), 4234 {Y,_} = read_line_all_alternating2(File), 4235 true 4236 end || {_,File,_,Y} <- All , Y =/= fail], 4237 [ begin 4238 io:format("read_line_all_alternating (failing as should): ~s~n",[File]), 4239 {'EXIT',_} = (catch read_line_all_alternating2(File)), 4240 true 4241 end || {_,File,_,Y} <- All , Y =:= fail], 4242 read_line_remove_files(All), 4243 ok. 4244%% read_line with raw file. 4245read_line_3(Config) when is_list(Config) -> 4246 PrivDir = proplists:get_value(priv_dir, Config), 4247 All = read_line_testdata(PrivDir), 4248 read_line_create_files(All), 4249 [ begin 4250 io:format("read_line_all: ~s~n",[File]), 4251 {X,_} = read_line_all3(File), 4252 true 4253 end || {_,File,X,_} <- All ], 4254 [ begin 4255 io:format("read_line_all_alternating: ~s~n",[File]), 4256 {Y,_} = read_line_all_alternating3(File), 4257 true 4258 end || {_,File,_,Y} <- All , Y =/= fail], 4259 [ begin 4260 io:format("read_line_all_alternating (failing as should): ~s~n",[File]), 4261 {'EXIT',_} = (catch read_line_all_alternating3(File)), 4262 true 4263 end || {_,File,_,Y} <- All , Y =:= fail], 4264 read_line_remove_files(All), 4265 ok. 4266%% read_line with raw buffered file. 4267read_line_4(Config) when is_list(Config) -> 4268 PrivDir = proplists:get_value(priv_dir, Config), 4269 All = read_line_testdata(PrivDir), 4270 read_line_create_files(All), 4271 [ begin 4272 io:format("read_line_all: ~s~n",[File]), 4273 {X,_} = read_line_all4(File), 4274 true 4275 end || {_,File,X,_} <- All ], 4276 [ begin 4277 io:format("read_line_all_alternating: ~s~n",[File]), 4278 {Y,_} = read_line_all_alternating4(File), 4279 true 4280 end || {_,File,_,Y} <- All , Y =/= fail], 4281 [ begin 4282 io:format("read_line_all_alternating (failing as should): ~s~n",[File]), 4283 {'EXIT',_} = (catch read_line_all_alternating4(File)), 4284 true 4285 end || {_,File,_,Y} <- All , Y =:= fail], 4286 read_line_remove_files(All), 4287 ok. 4288 4289rl_lines() -> 4290 [ <<"hej">>,<<"hopp">>,<<"i">>,<<"lingon\rskogen">>]. 4291 4292read_line_create0(Filename) -> 4293 {ok,F} = file:open(Filename,[write]), 4294 L = rl_lines(), 4295 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4296 file:write(F,<<"Inget radslut\r">>), 4297 file:close(F). 4298read_line_create1(Filename) -> 4299 {ok,F} = file:open(Filename,[write]), 4300 L = rl_lines(), 4301 [ begin 4302 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4303 file:write(F,<<"Inget radslut\r">>) 4304 end || _ <- lists:seq(1,100)], 4305 file:close(F). 4306read_line_create2(Filename) -> 4307 {ok,F} = file:open(Filename,[write]), 4308 L = rl_lines(), 4309 [ begin 4310 [ file:write(F,[R]) || R <- L ], 4311 file:write(F,<<"Inget radslut\r">>) 4312 end || _ <- lists:seq(1,200)], 4313 file:write(F,<<"\r\n">>), 4314 file:close(F). 4315 4316read_line_create3(Filename) -> 4317 {ok,F} = file:open(Filename,[write]), 4318 L = rl_lines(), 4319 [ begin 4320 file:write(F,<<"\r\n">>), 4321 file:write(F,<<"\r\n">>), 4322 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4323 file:write(F,<<"Inget radslut\r">>) 4324 end || _ <- lists:seq(1,100)], 4325 file:close(F). 4326 4327read_line_create4(Filename) -> 4328 {ok,F} = file:open(Filename,[write]), 4329 L = rl_lines(), 4330 [ begin 4331 file:write(F,<<"\n">>), 4332 file:write(F,<<"\n">>), 4333 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4334 file:write(F,<<"Inget radslut\r">>) 4335 end || _ <- lists:seq(1,100)], 4336 file:close(F). 4337 4338read_line_create5(Filename) -> 4339 {ok,F} = file:open(Filename,[write]), 4340 L = rl_lines(), 4341 [ begin 4342 file:write(F,<<"i\n">>), 4343 file:write(F,<<"i\n">>), 4344 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4345 file:write(F,<<"Inget radslut\r">>) 4346 end || _ <- lists:seq(1,100)], 4347 file:close(F). 4348 4349read_line_create6(Filename) -> 4350 {ok,F} = file:open(Filename,[write]), 4351 L = rl_lines(), 4352 [ begin 4353 file:write(F,<<"i\r\n">>), 4354 file:write(F,<<"i\r\n">>), 4355 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4356 file:write(F,<<"Inget radslut\r">>) 4357 end || _ <- lists:seq(1,100)], 4358 file:close(F). 4359read_line_create7(Filename) -> 4360 {ok,F} = file:open(Filename,[write]), 4361 L = rl_lines(), 4362 [ begin 4363 [ file:write(F,[R,<<"\r\n">>]) || R <- L ], 4364 file:write(F,<<"Inget radslut\r">>) 4365 end || _ <- lists:seq(1,1000)], 4366 file:close(F). 4367 4368read_line_all(Filename) -> 4369 {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]), 4370 X=read_rl_lines(F), 4371 ?PRIM_FILE:close(F), 4372 Bin = list_to_binary([B || {ok,B} <- X]), 4373 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4374 "\r\n","\n",[global,{return,binary}]), 4375 {length(X),Bin}. 4376 4377read_line_all2(Filename) -> 4378 {ok,F} = file:open(Filename,[read,binary]), 4379 X=read_rl_lines2(F), 4380 file:close(F), 4381 Bin = list_to_binary([B || {ok,B} <- X]), 4382 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4383 "\r\n","\n",[global,{return,binary}]), 4384 {length(X),Bin}. 4385 4386read_line_all3(Filename) -> 4387 {ok,F} = file:open(Filename,[read,binary,raw]), 4388 X=read_rl_lines2(F), 4389 file:close(F), 4390 Bin = list_to_binary([B || {ok,B} <- X]), 4391 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4392 "\r\n","\n",[global,{return,binary}]), 4393 {length(X),Bin}. 4394read_line_all4(Filename) -> 4395 {ok,F} = file:open(Filename,[read,binary,raw,{read_ahead,8192}]), 4396 X=read_rl_lines2(F), 4397 file:close(F), 4398 Bin = list_to_binary([B || {ok,B} <- X]), 4399 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4400 "\r\n","\n",[global,{return,binary}]), 4401 {length(X),Bin}. 4402 4403read_rl_lines(F) -> 4404 case ?PRIM_FILE:read_line(F) of 4405 eof -> 4406 []; 4407 {error,X} -> 4408 {error,X}; 4409 List -> 4410 [List | read_rl_lines(F)] 4411 end. 4412 4413read_rl_lines2(F) -> 4414 case file:read_line(F) of 4415 eof -> 4416 []; 4417 {error,X} -> 4418 {error,X}; 4419 List -> 4420 [List | read_rl_lines2(F)] 4421 end. 4422 4423read_line_all_alternating(Filename) -> 4424 {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]), 4425 X=read_rl_lines(F,true), 4426 ?PRIM_FILE:close(F), 4427 Bin = list_to_binary([B || {ok,B} <- X]), 4428 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4429 "\r\n","\n",[global,{return,binary}]), 4430 {length(X),Bin}. 4431 4432read_line_all_alternating2(Filename) -> 4433 {ok,F} = file:open(Filename,[read,binary]), 4434 X=read_rl_lines2(F,true), 4435 file:close(F), 4436 Bin = list_to_binary([B || {ok,B} <- X]), 4437 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4438 "\r\n","\n",[global,{return,binary}]), 4439 {length(X),Bin}. 4440read_line_all_alternating3(Filename) -> 4441 {ok,F} = file:open(Filename,[read,binary,raw]), 4442 X=read_rl_lines2(F,true), 4443 file:close(F), 4444 Bin = list_to_binary([B || {ok,B} <- X]), 4445 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4446 "\r\n","\n",[global,{return,binary}]), 4447 {length(X),Bin}. 4448read_line_all_alternating4(Filename) -> 4449 {ok,F} = file:open(Filename,[read,binary,raw,{read_ahead,8192}]), 4450 X=read_rl_lines2(F,true), 4451 file:close(F), 4452 Bin = list_to_binary([B || {ok,B} <- X]), 4453 Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), 4454 "\r\n","\n",[global,{return,binary}]), 4455 {length(X),Bin}. 4456 4457read_rl_lines(F,Alternate) -> 4458 case begin 4459 case Alternate of 4460 true -> ?PRIM_FILE:read(F,1); 4461 false -> ?PRIM_FILE:read_line(F) 4462 end 4463 end of 4464 eof -> 4465 []; 4466 {error,X} -> 4467 {error,X}; 4468 List -> 4469 [List | read_rl_lines(F,not Alternate)] 4470 end. 4471read_rl_lines2(F,Alternate) -> 4472 case begin 4473 case Alternate of 4474 true -> file:read(F,1); 4475 false -> file:read_line(F) 4476 end 4477 end of 4478 eof -> 4479 []; 4480 {error,X} -> 4481 {error,X}; 4482 List -> 4483 [List | read_rl_lines2(F,not Alternate)] 4484 end. 4485 4486 4487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4488 4489bytes(B, N) 4490 when is_integer(B), 0 =< B, B =< 255, is_integer(N), N > 2, N band 1 == 0 -> 4491 [bytes(B, N bsr 1), bytes(B, N bsr 1)]; 4492bytes(B, 0) 4493 when is_integer(B), 0 =< B, B =< 255 -> 4494 []; 4495bytes(B, 2) 4496 when is_integer(B), 0 =< B, B =< 255 -> 4497 [B, B]; 4498bytes(B, N) 4499 when is_integer(B), 0 =< B, B =< 255, is_integer(N), N > 0 -> 4500 [B, bytes(B, N-1)]. 4501 4502 4503%% A simple loop construct. 4504%% 4505%% Calls 'Fun' with argument 'Start' first and then repeatedly with 4506%% its returned value (state) until 'Fun' returns 'Stop'. Then 4507%% the last state value that was not 'Stop' is returned. 4508 4509iterate(Start, Done, Fun) when is_function(Fun) -> 4510 iterate(Start, Done, Fun, Start). 4511 4512iterate(Done, Done, _Fun, I) -> 4513 I; 4514iterate(I, Done, Fun, _) -> 4515 iterate(Fun(I), Done, Fun, I). 4516 4517 4518 4519flush() -> 4520 flush([]). 4521 4522flush(Msgs) -> 4523 receive 4524 Msg -> 4525 flush([Msg | Msgs]) 4526 after 0 -> 4527 lists:reverse(Msgs) 4528 end. 4529 4530%%% 4531%%% Support for testing large files. 4532%%% 4533 4534run_large_file_test(Config, Run, Name) -> 4535 case {os:type(),os:version()} of 4536 {{win32,nt},_} -> 4537 do_run_large_file_test(Config, Run, Name); 4538 {{unix,sunos},OsVersion} when OsVersion < {5,5,1} -> 4539 {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}; 4540 {{unix,_},_} -> 4541 case disc_free(proplists:get_value(priv_dir, Config)) of 4542 error -> 4543 {skip, "Failed to query disk space for priv_dir. " 4544 "Is it on a remote file system?~n"}; 4545 N when N >= 5 * (1 bsl 20) -> 4546 ct:pal("Free disk: ~w KByte~n", [N]), 4547 do_run_large_file_test(Config, Run, Name); 4548 N when N < 5 * (1 bsl 20) -> 4549 ct:pal("Free disk: ~w KByte~n", [N]), 4550 {skip,"Less than 5 GByte free"} 4551 end; 4552 _ -> 4553 {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"} 4554 end. 4555 4556 4557do_run_large_file_test(Config, Run, Name0) -> 4558 Name = filename:join(proplists:get_value(priv_dir, Config), 4559 ?MODULE_STRING ++ Name0), 4560 4561 %% Set up a process that will delete this file. 4562 Tester = self(), 4563 Deleter = 4564 spawn( 4565 fun() -> 4566 Mref = erlang:monitor(process, Tester), 4567 receive 4568 {'DOWN',Mref,_,_,_} -> ok; 4569 {Tester,done} -> ok 4570 end, 4571 ?FILE_MODULE:delete(Name) 4572 end), 4573 4574 %% Run the test case. 4575 Res = Run(Name), 4576 4577 %% Delete file and finish deleter process. 4578 Mref = erlang:monitor(process, Deleter), 4579 Deleter ! {Tester,done}, 4580 receive {'DOWN',Mref,_,_,_} -> ok end, 4581 4582 Res. 4583 4584disc_free(Path) -> 4585 Data = disksup:get_disk_data(), 4586 4587 %% What partitions could Data be mounted on? 4588 Partitions = 4589 [D || {P, _Tot, _Perc}=D <- Data, 4590 lists:prefix(filename:nativename(P), filename:nativename(Path))], 4591 4592 %% Sorting in descending order places the partition with the most specific 4593 %% path first. 4594 case lists:sort(fun erlang:'>='/2, Partitions) of 4595 [{_,Tot, Perc} | _] -> round(Tot * (1-(Perc/100))); 4596 [] -> error 4597 end. 4598 4599memsize() -> 4600 {Tot,_Used,_} = memsup:get_memory_data(), 4601 Tot. 4602 4603%%%----------------------------------------------------------------- 4604%%% Utilities 4605rm_rf(Mod,Dir) -> 4606 case Mod:read_link_info(Dir) of 4607 {ok, #file_info{type = directory}} -> 4608 {ok, Content} = Mod:list_dir_all(Dir), 4609 [ rm_rf(Mod,filename:join(Dir,C)) || C <- Content ], 4610 Mod:del_dir(Dir), 4611 ok; 4612 {ok, #file_info{}} -> 4613 Mod:delete(Dir); 4614 _ -> 4615 ok 4616 end. 4617