%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

-module(ram_file_SUITE).

-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	 init_per_group/2,end_per_group/2,
	 %% init/1, fini/1,
	 init_per_testcase/2, end_per_testcase/2]).
-export([open_modes/1, open_old_modes/1, pread_pwrite/1, position/1,
	 truncate/1, sync/1, get_set_file/1, compress/1, uuencode/1,
	 large_file_errors/1, large_file_light/1, large_file_heavy/1]).

-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").

-define(FILE_MODULE, file).         % Name of module to test
-define(RAM_FILE_MODULE, ram_file). % Name of module to test

%%--------------------------------------------------------------------------

suite() -> [{ct_hooks,[ts_install_cth]}].

all() -> 
    [open_modes, open_old_modes, pread_pwrite, position,
     truncate, sync, get_set_file, compress, uuencode,
     large_file_errors, large_file_light, large_file_heavy].

groups() -> 
    [].

init_per_suite(Config) ->
    Config.

end_per_suite(_Config) ->
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
    Time = 
	case Func of
	    large_file_heavy -> 
		?t:minutes(5);
	    _ ->
		?t:seconds(10)
	end,
    Dog = ?t:timetrap(Time),
    %% error_logger:info_msg("~p:~p *****~n", [?MODULE, Func]),
    [{watchdog, Dog} | Config].

end_per_testcase(_Func, Config) ->
    %% error_logger:info_msg("~p:~p END *****~n", [?MODULE, Func]),
    Dog = ?config(watchdog, Config),
    ?t:timetrap_cancel(Dog).

%%--------------------------------------------------------------------------
%% Test suites

open_modes(suite) ->
    [];
open_modes(doc) ->
    ["Test that the basic read, write and binary options works for open/2."];
open_modes(Config) when is_list(Config) ->
    ?line Str1 = "The quick brown fox ",
    ?line Str2 = "jumps over a lazy dog ",
    ?line Str  = Str1 ++ Str2,
    ?line Bin1 = list_to_binary(Str1),
    ?line Bin2 = list_to_binary(Str2),
    ?line Bin  = list_to_binary(Str),
    %%
    open_read_write(?FILE_MODULE, Str1, [ram, read, write], Str2),
    open_read(?FILE_MODULE, Str, [ram]),
    open_read_write(?FILE_MODULE, Bin1, [ram, binary, read, write], Bin2),
    open_read(?FILE_MODULE, Bin, [ram, binary, read]),
    %%
    ok.

open_old_modes(suite) ->
    [];
open_old_modes(doc) ->
    ["Test that the old style read, write and binary options ", 
     "works for open/2."];
open_old_modes(Config) when is_list(Config) ->
    ?line Str1 = "The quick brown fox ",
    ?line Str2 = "jumps over a lazy dog ",
    ?line Str  = Str1 ++ Str2,
    ?line Bin1 = list_to_binary(Str1),
    ?line Bin2 = list_to_binary(Str2),
    ?line Bin  = list_to_binary(Str),
    %%
    open_read_write(?RAM_FILE_MODULE, Str1, read_write, Str2),
    open_read(?RAM_FILE_MODULE, Str, read),
    open_read_write(?RAM_FILE_MODULE, Bin1, {binary, read_write}, Bin2),
    open_read(?RAM_FILE_MODULE, Bin, {binary, read}),
    %%
    ok.

open_read_write(Module, Data1, Options, Data2) ->
    ?line io:format("~p:open_read_write(~p, ~p, ~p, ~p)~n", 
		    [?MODULE, Module, Data1, Options, Data2]),
    %%
    ?line Size1 = sizeof(Data1),
    ?line Size2 = sizeof(Data2),
    ?line Data  = append(Data1, Data2),
    ?line Size  = Size1 + Size2,
    %%
    ?line {ok, Fd}    = Module:open(Data1, Options),
    ?line {ok, Data1} = Module:read(Fd, Size1),
    ?line eof         = Module:read(Fd, 1),
    ?line {ok, Zero}  = Module:read(Fd, 0),
    ?line 0           = sizeof(Zero),
    ?line ok          = Module:write(Fd, Data2),
    ?line {ok, 0}     = Module:position(Fd, bof),
    ?line {ok, Data}  = Module:read(Fd, Size),
    ?line eof         = Module:read(Fd, 1),
    ?line {ok, Zero}  = Module:read(Fd, 0),
    ?line ok          = Module:close(Fd),
    %%
    ?line ok.

open_read(Module, Data, Options) ->
    ?line io:format("~p:open_read(~p, ~p, ~p)~n", 
		    [?MODULE, Module, Data, Options]),
    %%
    ?line Size = sizeof(Data),
    %%
    ?line {ok, Fd}         = Module:open(Data, Options),
    ?line {ok, Data}       = Module:read(Fd, Size),
    ?line eof              = Module:read(Fd, 1),
    ?line {ok, Zero}       = Module:read(Fd, 0),
    ?line 0                = sizeof(Zero),
    ?line {error, ebadf}   = Module:write(Fd, Data),
    ?line {ok, 0}          = Module:position(Fd, bof),
    ?line {ok, Data}       = Module:read(Fd, Size),
    ?line eof              = Module:read(Fd, 1),
    ?line {ok, Zero}       = Module:read(Fd, 0),
    ?line ok               = Module:close(Fd),
    %%
    ?line ok.



pread_pwrite(suite) ->
    [];
pread_pwrite(doc) ->
    ["Test that pread/2,3 and pwrite/2,3 works."];
pread_pwrite(Config) when is_list(Config) ->
    ?line Str = "Flygande bäckaziner söka hwila på mjuqa tuvor x",
    ?line Bin = list_to_binary(Str),
    %%
    pread_pwrite_test(?FILE_MODULE, Str, [ram, read, write]),
    pread_pwrite_test(?FILE_MODULE, Bin, [ram, binary, read, write]),
    pread_pwrite_test(?RAM_FILE_MODULE, Str, [read, write]),
    pread_pwrite_test(?RAM_FILE_MODULE, Bin, {binary, read_write}),
    %%
    ok.

pread_pwrite_test(Module, Data, Options) ->
    ?line io:format("~p:pread_pwrite_test(~p, ~p, ~p)~n", 
		    [?MODULE, Module, Data, Options]),
    %%
    ?line Size = sizeof(Data),
    %%
    ?line {ok, Fd}         = Module:open([], Options),
    ?line ok               = Module:pwrite(Fd, 0, Data),
    ?line {ok, Data}       = Module:pread(Fd, 0, Size+1),
    ?line eof              = Module:pread(Fd, Size+1, 1),
    ?line {ok, Zero}       = Module:pread(Fd, Size+1, 0),
    ?line 0                = sizeof(Zero),
    ?line ok               = Module:pwrite(Fd, [{0, Data}, {Size+17, Data}]),
    ?line {ok, [Data, 
		eof, 
		Data,
		Zero]}     = Module:pread(Fd, [{Size+17, Size+1},
					       {2*Size+17+1, 1},
					       {0, Size},
					       {2*Size+17+1, 0}]),
    ?line ok               = Module:close(Fd),
    %%
    ?line ok.

position(suite) ->
    [];
position(doc) ->
    ["Test that position/2 works."];
position(Config) when is_list(Config) ->
    ?line Str = "Att vara eller icke vara, det är frågan. ",
    ?line Bin = list_to_binary(Str),
    %%
    position_test(?FILE_MODULE, Str, [ram, read]),
    position_test(?FILE_MODULE, Bin, [ram, binary]),
    position_test(?RAM_FILE_MODULE, Str, [read]),
    position_test(?RAM_FILE_MODULE, Bin, {binary, read}),
    %%
    ok.

position_test(Module, Data, Options) ->
    ?line io:format("~p:position_test(~p, ~p, ~p)~n", 
		    [?MODULE, Module, Data, Options]),
    %%
    ?line Size = sizeof(Data),
    ?line Size_7 = Size+7,
    %%
    ?line Slice_0_2 = slice(Data, 0, 2),
    ?line Slice_0_3 = slice(Data, 0, 3),
    ?line Slice_2_5 = slice(Data, 2, 5),
    ?line Slice_3_4 = slice(Data, 3, 4),
    ?line Slice_5   = slice(Data, 5, Size),
    %%
    ?line {ok, Fd}          = Module:open(Data, Options),
    %%
    ?line io:format("CUR positions"),
    ?line {ok, Slice_0_2}   = Module:read(Fd, 2),
    ?line {ok, 2}           = Module:position(Fd, cur),
    ?line {ok, Slice_2_5}   = Module:read(Fd, 5),
    ?line {ok, 3}           = Module:position(Fd, {cur, -4}),
    ?line {ok, Slice_3_4}   = Module:read(Fd, 4),
    ?line {ok, 0}           = Module:position(Fd, {cur, -7}),
    ?line {ok, Slice_0_3}   = Module:read(Fd, 3),
    ?line {ok, 0}           = Module:position(Fd, {cur, -3}),
    ?line {error, einval}   = Module:position(Fd, {cur, -1}),
    ?line {ok, 0}           = Module:position(Fd, 0),
    ?line {ok, 2}           = Module:position(Fd, {cur, 2}),
    ?line {ok, Slice_2_5}   = Module:read(Fd, 5),
    ?line {ok, Size_7}      = Module:position(Fd, {cur, Size}),
    ?line {ok, Zero}        = Module:read(Fd, 0),
    ?line 0                 = sizeof(Zero),
    ?line eof               = Module:read(Fd, 1),
    %%
    ?line io:format("Absolute and BOF positions"),
    ?line {ok, Size}        = Module:position(Fd, Size),
    ?line eof               = Module:read(Fd, 1),
    ?line {ok, 5}           = Module:position(Fd, 5),
    ?line {ok, Slice_5}     = Module:read(Fd, Size),
    ?line {ok, 2}           = Module:position(Fd, {bof, 2}),
    ?line {ok, Slice_2_5}   = Module:read(Fd, 5),
    ?line {ok, 3}           = Module:position(Fd, 3),
    ?line {ok, Slice_3_4}   = Module:read(Fd, 4),
    ?line {ok, 0}           = Module:position(Fd, bof),
    ?line {ok, Slice_0_2}   = Module:read(Fd, 2),
    ?line {ok, Size_7}      = Module:position(Fd, {bof, Size_7}),
    ?line {ok, Zero}        = Module:read(Fd, 0),
    %%
    ?line io:format("EOF positions"),
    ?line {ok, Size}        = Module:position(Fd, eof),
    ?line eof               = Module:read(Fd, 1),
    ?line {ok, 5}           = Module:position(Fd, {eof, -Size+5}),
    ?line {ok, Slice_5}     = Module:read(Fd, Size),
    ?line {ok, 2}           = Module:position(Fd, {eof, -Size+2}),
    ?line {ok, Slice_2_5}   = Module:read(Fd, 5),
    ?line {ok, 3}           = Module:position(Fd, {eof, -Size+3}),
    ?line {ok, Slice_3_4}   = Module:read(Fd, 4),
    ?line {ok, 0}           = Module:position(Fd, {eof, -Size}),
    ?line {ok, Slice_0_2}   = Module:read(Fd, 2),
    ?line {ok, Size_7}      = Module:position(Fd, {eof, 7}),
    ?line {ok, Zero}        = Module:read(Fd, 0),
    ?line eof               = Module:read(Fd, 1),
    %%
    ?line ok.



truncate(suite) ->
    [];
truncate(doc) ->
    ["Test that truncate/1 works."];
truncate(Config) when is_list(Config) ->
    ?line Str = "Mån ädlare att lida och fördraga " 
	++ "ett bittert ödes stygn av pilar, ",
    ?line Bin = list_to_binary(Str),
    %%
    ok = truncate_test(?FILE_MODULE, Str, [ram, read, write]),
    ok = truncate_test(?FILE_MODULE, Bin, [ram, binary, read, write]),
    ok = truncate_test(?RAM_FILE_MODULE, Str, read_write),
    ok = truncate_test(?RAM_FILE_MODULE, Bin, [binary, read, write]),
    %%
    {error, eacces} = truncate_test(?FILE_MODULE, Str, [ram]),
    {error, eacces} = truncate_test(?FILE_MODULE, Bin, [ram, binary, read]),
    {error, eacces} = truncate_test(?RAM_FILE_MODULE, Str, read),
    {error, eacces} = truncate_test(?RAM_FILE_MODULE, Bin, {binary, read}),
    %%
    ok.

truncate_test(Module, Data, Options) ->
    ?line io:format("~p:truncate_test(~p, ~p, ~p)~n", 
		    [?MODULE, Module, Data, Options]),
    %%
    ?line Size = sizeof(Data),
    ?line Size1 = Size-2,
    ?line Data1 = slice(Data, 0, Size1),
    %%
    ?line {ok, Fd}    = Module:open(Data, Options),
    ?line {ok, Size1} = Module:position(Fd, Size1),
    ?line case Module:truncate(Fd) of
	      ok ->
		  ?line {ok, 0}     = Module:position(Fd, 0),
		  ?line {ok, Data1} = Module:read(Fd, Size),
		  ?line ok          = Module:close(Fd),
		  ?line ok;
	      Error ->
		  ?line ok      = Module:close(Fd),
		  ?line Error
	  end.



sync(suite) ->
    [];
sync(doc) ->
    ["Test that sync/1 at least does not crash."];
sync(Config) when is_list(Config) ->
    ?line Str = "än att ta till vapen mot ett hav av kval. ",
    ?line Bin = list_to_binary(Str),
    %%
    sync_test(?FILE_MODULE, Str, [ram, read, write]),
    sync_test(?FILE_MODULE, Bin, [ram, binary, read, write]),
    sync_test(?RAM_FILE_MODULE, Str, read_write),
    sync_test(?RAM_FILE_MODULE, Bin, [binary, read, write]),
    %%
    sync_test(?FILE_MODULE, Str, [ram]),
    sync_test(?FILE_MODULE, Bin, [ram, binary, read]),
    sync_test(?RAM_FILE_MODULE, Str, read),
    sync_test(?RAM_FILE_MODULE, Bin, {binary, read}),
    %%
    ok.

sync_test(Module, Data, Options) ->
    ?line io:format("~p:sync_test(~p, ~p, ~p)~n", 
		    [?MODULE, Module, Data, Options]),
    %%
    ?line Size = sizeof(Data),
    %%
    ?line {ok, Fd}    = Module:open(Data, Options),
    ?line ok          = Module:sync(Fd),
    ?line {ok, Data}  = Module:read(Fd, Size+1),
    ?line ok.



get_set_file(suite) ->
    [];
get_set_file(doc) ->
    ["Tests get_file/1, set_file/2, get_file_close/1 and get_size/1."];
get_set_file(Config) when is_list(Config) ->
    %% These two strings should not be of equal length.
    ?line Str  = "När högan nord blir snöbetäckt, ",
    ?line Str2 = "får alla harar byta dräkt. ",
    ?line Bin  = list_to_binary(Str),
    ?line Bin2 = list_to_binary(Str2),
    %%
    ok = get_set_file_test(Str, read_write, Str2),
    ok = get_set_file_test(Bin, [binary, read, write], Bin2),
    ok = get_set_file_test(Str, read, Str2),
    ok = get_set_file_test(Bin, [binary, read], Bin2),
    %%
    ok.

get_set_file_test(Data, Options, Data2) ->
    ?line io:format("~p:get_set_file_test(~p, ~p, ~p)~n", 
		    [?MODULE, Data, Options, Data2]),
    %%
    ?line Size  = sizeof(Data),
    ?line Size2 = sizeof(Data2),
    %%
    ?line {ok, Fd}        = ?RAM_FILE_MODULE:open(Data, Options),
    ?line {ok, Size}      = ?RAM_FILE_MODULE:get_size(Fd),
    ?line {ok, Data}      = ?RAM_FILE_MODULE:get_file(Fd),
    ?line {ok, Data}      = ?RAM_FILE_MODULE:get_file_close(Fd),
    ?line {error, einval} = ?RAM_FILE_MODULE:get_size(Fd),
    ?line {ok, Fd2}       = ?RAM_FILE_MODULE:open(Data, Options),
    ?line case ?RAM_FILE_MODULE:set_file(Fd2, Data2) of
	      {ok, Size2} ->
		  ?line {ok, Size2} = ?RAM_FILE_MODULE:get_size(Fd2),
		  ?line {ok, Data2} = ?RAM_FILE_MODULE:get_file(Fd2),
		  ?line {ok, Data2} = ?RAM_FILE_MODULE:get_file_close(Fd2),
		  ?line ok;
	      {error, _} = Error ->
		  ?line {ok, Data}  = ?RAM_FILE_MODULE:get_file_close(Fd2),
		  ?line Error
	  end.
    

    
compress(suite) ->
    [];
compress(doc) ->
    ["Test that compress/1 and uncompress/1 works."];
compress(Config) when is_list(Config) ->
    ?line Data   = ?config(data_dir, Config),
    ?line Real   = filename:join(Data, "realmen.html"),
    ?line RealGz = filename:join(Data, "realmen.html.gz"),
    %%
    %% Uncompress test
    %%
    ?line {ok, FdReal}   = ?FILE_MODULE:open(Real, []),
    ?line {ok, Fd}       = ?FILE_MODULE:open([], [ram, read, write]),
    ?line {ok, FdRealGz} = ?FILE_MODULE:open(RealGz, []),
    %%
    ?line {ok, SzGz}     = ?FILE_MODULE:copy(FdRealGz, Fd),
    ?line {ok, Sz}       = ?RAM_FILE_MODULE:uncompress(Fd),
    ?line {ok, 0}        = ?FILE_MODULE:position(Fd, bof),
    ?line true           = compare(FdReal, Fd),
    %%
    ?line true           = (SzGz =< Sz),
    %%
    %% Compress and uncompress test
    %%
    ?line {ok, 0}        = ?FILE_MODULE:position(FdReal, bof),
    ?line {ok, 0}        = ?FILE_MODULE:position(Fd, bof),
    ?line ok             = ?FILE_MODULE:truncate(Fd),
    ?line {ok, Sz}       = ?FILE_MODULE:copy(FdReal, Fd),
    ?line {ok, SzGz}     = ?RAM_FILE_MODULE:compress(Fd),
    ?line {ok, Sz}       = ?RAM_FILE_MODULE:uncompress(Fd),
    ?line {ok, 0}        = ?FILE_MODULE:position(Fd, bof),
    ?line {ok, 0}        = ?FILE_MODULE:position(FdReal, bof),
    ?line true           = compare(FdReal, Fd),
    %%
    ?line ok             = ?FILE_MODULE:close(FdReal),
    ?line ok             = ?FILE_MODULE:close(Fd),
    ?line ok             = ?FILE_MODULE:close(FdRealGz),


    %% Test uncompressing data that will be expanded many times.
    ?line Huge = iolist_to_binary(mk_42(18)),
    ?line HugeSize = byte_size(Huge),
    ?line HugeGz = zlib:gzip(Huge),

    ?line {ok,HugeFd} = ?FILE_MODULE:open([], [ram,read,write,binary]),
    ?line ok = ?FILE_MODULE:write(HugeFd, HugeGz),
    ?line {ok,HugeSize} = ?RAM_FILE_MODULE:uncompress(HugeFd),
    ?line {ok,0} = ?FILE_MODULE:position(HugeFd, bof),
    ?line {ok,Huge} = ?FILE_MODULE:read(HugeFd, HugeSize),

    %% Uncompressing again should do nothing.
    ?line {ok,HugeSize} = ?RAM_FILE_MODULE:uncompress(HugeFd),
    ?line {ok,0} = ?FILE_MODULE:position(HugeFd, bof),
    ?line {ok,Huge} = ?FILE_MODULE:read(HugeFd, HugeSize),

    ?line ok = ?FILE_MODULE:close(HugeFd),

    ok.

mk_42(0) ->
    [42];
mk_42(N) ->
    B = mk_42(N-1),
    [B|B].

uuencode(suite) ->
    [];
uuencode(doc) ->
    ["Test that uuencode/1 and uudecode/1 works."];
uuencode(Config) when is_list(Config) ->
    ?line Data   = ?config(data_dir, Config),
    ?line Real   = filename:join(Data, "realmen.html"),
    ?line RealUu = filename:join(Data, "realmen.html.uu"),
    %%
    %% Uudecode test
    %%
    ?line {ok, FdReal}   = ?FILE_MODULE:open(Real, []),
    ?line {ok, Fd}       = ?FILE_MODULE:open([], [ram, read, write]),
    ?line {ok, FdRealUu} = ?FILE_MODULE:open(RealUu, []),
    %%
    ?line {ok, SzUu}     = ?FILE_MODULE:copy(FdRealUu, Fd),
    ?line {ok, Sz}       = ?RAM_FILE_MODULE:uudecode(Fd),
    ?line true           = (Sz =< SzUu),
    ?line {ok, 0}        = ?FILE_MODULE:position(Fd, bof),
    ?line true           = compare(FdReal, Fd),
    %%
    %% Uuencode and decode test
    %%
    F = fun(Offs) ->
		Size = Sz - Offs,
		?line {ok, Offs}     = ?FILE_MODULE:position(FdReal, {bof,Offs}),
		?line {ok, 0}        = ?FILE_MODULE:position(Fd, bof),
		?line ok             = ?FILE_MODULE:truncate(Fd),
		?line {ok, Size}     = ?FILE_MODULE:copy(FdReal, Fd),
		?line {ok, SizeUu}   = ?RAM_FILE_MODULE:uuencode(Fd),
		?line true           = (Size =< SizeUu),
		?line {ok, Size}     = ?RAM_FILE_MODULE:uudecode(Fd),
		?line {ok, Offs}     = ?FILE_MODULE:position(FdReal, {bof,Offs}),
		?line {ok, 0}        = ?FILE_MODULE:position(Fd, bof),
		?line true           = compare(FdReal, Fd)
	end,
    lists:foreach(F, lists:seq(0,Sz-1, 43)),

    ?line ok             = ?FILE_MODULE:close(FdReal),
    ?line ok             = ?FILE_MODULE:close(Fd),
    ?line ok             = ?FILE_MODULE:close(FdRealUu),
    %%
    ok.



large_file_errors(suite) ->
    [];
large_file_errors(doc) ->
    ["Test error checking of large file offsets."];
large_file_errors(Config) when is_list(Config) ->
    ?line TwoGig = 1 bsl 31,
    ?line {ok,Fd}         = ?RAM_FILE_MODULE:open("1234567890", [read,write]),
    ?line {error, einval} = ?FILE_MODULE:read(Fd, TwoGig),
    ?line {error, badarg} = ?FILE_MODULE:read(Fd, -1),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof,TwoGig}),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof,-TwoGig-1}),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof,-1}),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {cur,TwoGig}),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {cur,-TwoGig-1}),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {eof,TwoGig}),
    ?line {error, einval} = ?FILE_MODULE:position(Fd, {eof,-TwoGig-1}),
    ?line {error, einval} = ?FILE_MODULE:pread(Fd, TwoGig, 1),
    ?line {error, einval} = ?FILE_MODULE:pread(Fd, -TwoGig-1, 1),
    ?line {error, einval} = ?FILE_MODULE:pread(Fd, -1, 1),
    ?line {error, einval} = ?FILE_MODULE:pwrite(Fd, TwoGig, "@"),
    ?line {error, einval} = ?FILE_MODULE:pwrite(Fd, -TwoGig-1, "@"),
    ?line {error, einval} = ?FILE_MODULE:pwrite(Fd, -1, "@"),
    ?line {error, einval} = ?FILE_MODULE:pread(Fd, TwoGig, 0),
    ?line {error, einval} = ?FILE_MODULE:pread(Fd, -TwoGig-1, 0),
    ?line {error, einval} = ?FILE_MODULE:pread(Fd, -1, 0),
    ?line ok              = ?FILE_MODULE:close(Fd),
    ok.



large_file_light(suite) ->
    [];
large_file_light(doc) ->
    ["Test light operations on a \"large\" ram_file."];
large_file_light(Config) when is_list(Config) ->
    ?line PrivDir = ?config(priv_dir, Config),
    %% Marker for next test case that is to heavy to run in a suite.
    ?line ok = ?FILE_MODULE:write_file(
		  filename:join(PrivDir, large_file_light), 
		  <<"TAG">>),
    %%
    ?line Data = "abcdefghijklmnopqrstuvwzyz",
    ?line Size = sizeof(Data),
    ?line Max = (1 bsl 31) - 1,
    ?line Max__1 = Max - 1,
    ?line {ok, Fd}        = ?RAM_FILE_MODULE:open(Data, [read]),
    ?line {ok, Data}      = ?FILE_MODULE:read(Fd, Size+1),
    ?line {ok, Max__1}    = ?FILE_MODULE:position(Fd, {eof, Max-Size-1}),
    ?line eof             = ?FILE_MODULE:read(Fd, 1),
    ?line {ok, Max}       = ?FILE_MODULE:position(Fd, {bof, Max}),
    ?line {ok, Zero}      = ?FILE_MODULE:read(Fd, 0),
    ?line 0               = sizeof(Zero),
    ?line eof             = ?FILE_MODULE:read(Fd, 1),
    ?line eof             = ?FILE_MODULE:pread(Fd, Max__1, 1),
    ?line {ok, Zero}      = ?FILE_MODULE:pread(Fd, Max, 0),
    ?line eof             = ?FILE_MODULE:pread(Fd, Max, 1),
    ok.



large_file_heavy(suite) ->
    [];
large_file_heavy(doc) ->
    ["Test operations on a maximum size (2 GByte - 1) ram_file."];
large_file_heavy(Config) when is_list(Config) ->
    ?line PrivDir = ?config(priv_dir, Config),
    %% Check previous test case marker.
    case ?FILE_MODULE:read_file_info(
	    filename:join(PrivDir, large_file_light)) of
	{ok,_} ->
	    {skipped,"Too heavy for casual testing!"};
	_ ->
	    do_large_file_heavy(Config)
    end.

do_large_file_heavy(_Config) ->
    ?line Data = "qwertyuiopasdfghjklzxcvbnm",
    ?line Size = sizeof(Data),
    ?line Max = (1 bsl 31) - 1,
    ?line Max__1 = Max - 1,
    ?line Max__3 = Max - 3,
    ?line {ok, Fd}        = ?RAM_FILE_MODULE:open(Data, [read,write]),
    ?line {ok, Data}      = ?FILE_MODULE:read(Fd, Size+1),
    ?line {ok, Max}       = ?FILE_MODULE:position(Fd, {eof, Max-Size}),
    ?line eof             = ?FILE_MODULE:read(Fd, 1),
    ?line erlang:display({allocating,2,'GByte',please,be,patient,'...'}),
    ?line ok              = ?FILE_MODULE:write(Fd, ""),
    ?line erlang:display({allocating,2,'GByte',succeeded}),
    ?line {ok, Max__1}    = ?FILE_MODULE:position(Fd, {eof, -1}),
    ?line {ok, [0]}       = ?FILE_MODULE:read(Fd, 1),
    ?line {ok, []}        = ?FILE_MODULE:read(Fd, 0),
    ?line eof             = ?FILE_MODULE:read(Fd, 1),
    ?line ok              = ?FILE_MODULE:pwrite(Fd, Max-3, "TAG"),
    ?line {ok, Max}       = ?FILE_MODULE:position(Fd, cur),
    ?line {ok, Max__3}    = ?FILE_MODULE:position(Fd, {eof, -3}),
    ?line {ok, "TAG"}     = ?FILE_MODULE:read(Fd, 3+1),
    ?line {ok, Max__3}    = ?FILE_MODULE:position(Fd, {cur, -3}),
    ?line ok              = ?FILE_MODULE:write(Fd, "tag"),
    ?line {ok, Max}       = ?FILE_MODULE:position(Fd, cur),
    ?line {ok, 0}         = ?FILE_MODULE:position(Fd, bof),
    ?line {ok, "tag"}     = ?FILE_MODULE:pread(Fd, Max__3, 3+1),
    ?line {ok, 0}         = ?FILE_MODULE:position(Fd, cur),
    ?line ok              = ?FILE_MODULE:close(Fd),
    ok.

%%--------------------------------------------------------------------------
%% Utility functions

compare(FdA, FdB) ->
    Size = 65536,
    case {?FILE_MODULE:read(FdA, Size), ?FILE_MODULE:read(FdB, Size)} of
	{{error, _} = Error, _} ->
	    Error;
	{_, {error, _} = Error} ->
	    Error;
	{{ok, A}, {ok, B}} ->
	    case compare_data(A, B) of
		true ->
		    compare(FdA, FdB);
		false ->
		    false
	    end;
	{eof, eof} ->
	    true;
	_ ->
	    false
    end.

compare_data(A, B) when is_list(A), is_list(B) ->
    list_to_binary(A) == list_to_binary(B);
compare_data(A, B) when is_list(A), is_binary(B) ->
    list_to_binary(A) == B;
compare_data(A, B) when is_binary(A), is_list(B) ->
    A == list_to_binary(B);
compare_data(A, B) when is_binary(A), is_binary(B) ->
    A == B.
	    
sizeof(Data) when is_list(Data) ->
    length(Data);
sizeof(Data) when is_binary(Data) ->
    byte_size(Data).

append(Data1, Data2) when is_list(Data1), is_list(Data2) ->    
    Data1 ++ Data2;
append(Data1, Data2) when is_binary(Data1), is_binary(Data2) ->
    list_to_binary([Data1 | Data2]).

slice(Data, Start, Length) when is_list(Data) ->
    lists:sublist(Data, Start+1, Length);
slice(Data, Start, Length) when is_binary(Data) ->
    {_, Bin} = split_binary(Data, Start),
    if
	Length >= byte_size(Bin) ->
	    Bin;
	true ->
	    {B, _} = split_binary(Bin, Length),
	    B
    end.

