%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2010. 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(ftp_suite_lib).
-include("test_server.hrl").
-include("test_server_line.hrl").
-include("inets_test_lib.hrl").
%% Test server specific exports
% -export([init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
-record(progress, {
current = 0,
total
}).
-define(FTP_USER, "anonymous").
-define(FTP_PASS, passwd()).
-define(FTP_PORT, 21).
-define(BAD_HOST, "badhostname").
-define(BAD_USER, "baduser").
-define(BAD_DIR, "baddirectory").
-ifdef(ftp_debug_client).
-define(ftp_open(Host, Flags),
do_ftp_open(Host, [debug, {timeout, timer:seconds(15)}] ++ Flags)).
-else.
-ifdef(ftp_trace_client).
-define(ftp_open(Host, Flags),
do_ftp_open(Host, [trace, {timeout, timer:seconds(15)}] ++ Flags)).
-else.
-define(ftp_open(Host, Flags),
do_ftp_open(Host, [verbose, {timeout, timer:seconds(15)}] ++ Flags)).
-endif.
-endif.
%% -- Tickets --
tickets(doc) ->
"Test cases for reported bugs";
tickets(suite) ->
[ticket_6035].
%% --
ftpd_init(FtpdTag, Config) ->
%% Get the host name(s) of FTP server
Hosts =
case ?config(ftpd_hosts, Config) of
undefined ->
ftpd_hosts(data_dir(Config));
H ->
H
end,
p("ftpd_init -> "
"~n Hosts: ~p"
"~n Config: ~p"
"~n FtpdTag: ~p", [Hosts, Config, FtpdTag]),
%% Get the first host that actually have a running FTP server
case lists:keysearch(FtpdTag, 1, Hosts) of
{value, {_, TagHosts}} when is_list(TagHosts) ->
inets:start(),
case (catch get_ftpd_host(TagHosts)) of
{ok, Host} ->
inets:stop(),
[{ftp_remote_host, Host}|Config];
_ ->
inets:stop(),
Reason = lists:flatten(
io_lib:format("Could not find a valid "
"FTP server for ~p (~p)",
[FtpdTag, TagHosts])),
{skip, Reason}
end;
_ ->
Reason = lists:flatten(
io_lib:format("No host(s) running FTPD server "
"for ~p", [FtpdTag])),
{skip, Reason}
end.
ftpd_fin(Config) ->
lists:keydelete(ftp_remote_host, 1, Config).
get_ftpd_host([]) ->
{error, no_host};
get_ftpd_host([Host|Hosts]) ->
p("get_ftpd_host -> entry with"
"~n Host: ~p"
"~n", [Host]),
case (catch ftp:open({option_list,
[{host, Host}, {port, ?FTP_PORT},
{timeout, 20000}]})) of
{ok, Pid} ->
(catch ftp:close(Pid)),
{ok, Host};
_ ->
get_ftpd_host(Hosts)
end.
%%--------------------------------------------------------------------
dirty_select_ftpd_host(Config) ->
Hosts =
case ?config(ftpd_hosts, Config) of
undefined ->
ftpd_hosts(data_dir(Config));
H ->
H
end,
dirty_select_ftpd_host2(Hosts).
dirty_select_ftpd_host2([]) ->
throw({error, not_found});
dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) ->
case dirty_select_ftpd_host3(Hosts) of
none ->
dirty_select_ftpd_host2(PlatformHosts);
{ok, Host} ->
{PlatformTag, Host}
end.
dirty_select_ftpd_host3([]) ->
none;
dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) ->
case dirty_select_ftpd_host4(Host) of
true ->
{ok, Host};
false ->
dirty_select_ftpd_host3(Hosts)
end;
dirty_select_ftpd_host3([_|Hosts]) ->
dirty_select_ftpd_host3(Hosts).
%% This is a very simple and dirty test that there is a
%% (FTP) deamon on the other end.
dirty_select_ftpd_host4(Host) ->
Port = 21,
IpFam = inet,
Opts = [IpFam, binary, {packet, 0}, {active, false}],
Timeout = ?SECS(5),
case gen_tcp:connect(Host, Port, Opts, Timeout) of
{ok, Sock} ->
gen_tcp:close(Sock),
true;
_Error ->
false
end.
%%--------------------------------------------------------------------
test_filenames() ->
{ok, Host} = inet:gethostname(),
File = Host ++ "_ftp_test.txt",
NewFile = "new_" ++ File,
{File, NewFile}.
%%--------------------------------------------------------------------
%% Function: init_per_testcase(Case, Config) -> Config
%% Case - atom()
%% Name of the test case that is about to be run.
%% Config - [tuple()]
%% A list of key/value pairs, holding the test case configuration.
%%
%% Description: Initiation before each test case
%%
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_testcase(Case, Config)
when (Case =:= open) orelse (Case =:= open_port) ->
io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
inets:start(),
NewConfig = data_dir(Config),
watch_dog(NewConfig);
init_per_testcase(Case, Config) ->
put(ftp_testcase, Case),
inets:enable_trace(max, io, ftpc),
do_init_per_testcase(Case, Config).
do_init_per_testcase(Case, Config)
when (Case =:= passive_user) ->
io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
inets:start(),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
case (catch ?ftp_open(Host, [])) of
{ok, Pid} ->
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
SKIP
end;
do_init_per_testcase(Case, Config)
when (Case =:= active_user) ->
io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
inets:start(),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
case (catch ?ftp_open(Host, [])) of
{ok, Pid} ->
ok = ftp:force_active(Pid),
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
SKIP
end;
do_init_per_testcase(Case, Config)
when (Case =:= progress_report_send) orelse
(Case =:= progress_report_recv) ->
inets:start(),
io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
Opts = [{host, Host},
{port, ?FTP_PORT},
{flags, [verbose]},
{progress, {?MODULE, progress, #progress{}}}],
case ftp:open({option_list, Opts}) of
{ok, Pid} ->
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
SKIP
end;
do_init_per_testcase(Case, Config) ->
io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
inets:start(),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
Flags =
if
((Case =:= passive_ip_v6_disabled) orelse
(Case =:= active_ip_v6_disabled)) ->
[ip_v6_disabled];
true ->
[]
end,
case (catch ?ftp_open(Host, Flags)) of
{ok, Pid} ->
case string:tokens(atom_to_list(Case), [$_]) of
[_, "active"|_] ->
ok = ftp:force_active(Pid);
_ ->
ok
end,
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
SKIP
end.
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
%% Case - atom()
%% Name of the test case that is about to be run.
%% Config - [tuple()]
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
end_per_testcase(_, Config) ->
NewConfig = close_connection(Config),
Dog = ?config(watchdog, NewConfig),
inets:stop(),
test_server:timetrap_cancel(Dog),
ok.
%%-------------------------------------------------------------------------
%% Suites similar for all hosts.
%%-------------------------------------------------------------------------
passive(suite) ->
[
passive_user,
passive_pwd,
passive_cd,
passive_lcd,
passive_ls,
passive_nlist,
passive_rename,
passive_delete,
passive_mkdir,
passive_send,
passive_send_bin,
passive_send_chunk,
passive_append,
passive_append_bin,
passive_append_chunk,
passive_recv,
passive_recv_bin,
passive_recv_chunk,
passive_type,
passive_quote,
passive_ip_v6_disabled
].
active(suite) ->
[
active_user,
active_pwd,
active_cd,
active_lcd,
active_ls,
active_nlist,
active_rename,
active_delete,
active_mkdir,
active_send,
active_send_bin,
active_send_chunk,
active_append,
active_append_bin,
active_append_chunk,
active_recv,
active_recv_bin,
active_recv_chunk,
active_type,
active_quote,
active_ip_v6_disabled
].
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
open(doc) ->
["Open an ftp connection to a host and close the connection."
"Also check that !-messages does not disturbe the connection"];
open(suite) ->
[];
open(Config) when is_list(Config) ->
Host = ftp_host(Config),
(catch tc_open(Host)).
tc_open(Host) ->
{ok, Pid} = ?ftp_open(Host, []),
ok = ftp:close(Pid),
{ok, Pid1} =
ftp:open({option_list, [{host,Host},
{port, ?FTP_PORT},
{flags, [verbose]},
{timeout, 30000}]}),
ok = ftp:close(Pid1),
{error, ehost} = ftp:open({option_list, [{port, ?FTP_PORT},
{flags, [verbose]}]}),
{ok, Pid2} = ftp:open(Host),
ok = ftp:close(Pid2),
{ok, NewHost} = inet:getaddr(Host, inet),
{ok, Pid3} = ftp:open(NewHost),
ftp:user(Pid3, ?FTP_USER, ?FTP_PASS),
Pid3 ! foobar,
test_server:sleep(5000),
{message_queue_len, 0} = process_info(self(), message_queue_len),
["200" ++ _] = ftp:quote(Pid3, "NOOP"),
ok = ftp:close(Pid3),
%% Bad input that has default values are ignored and the defult
%% is used.
{ok, Pid4} =
ftp:open({option_list, [{host, Host}, {port, badarg},
{flags, [verbose]},
{timeout, 30000}]}),
test_server:sleep(100),
ok = ftp:close(Pid4),
{ok, Pid5} =
ftp:open({option_list, [{host, Host}, {port, ?FTP_PORT},
{flags, [verbose]},
{timeout, -42}]}),
test_server:sleep(100),
ok = ftp:close(Pid5),
{ok, Pid6} =
ftp:open({option_list, [{host, Host}, {port, ?FTP_PORT},
{flags, [verbose]},
{mode, cool}]}),
test_server:sleep(100),
ok = ftp:close(Pid6),
ok.
%%-------------------------------------------------------------------------
open_port(doc) ->
["Open an ftp connection to a host with given port number "
"and close the connection."]; % See also OTP-3892
open_port(suite) ->
[];
open_port(Config) when is_list(Config) ->
Host = ftp_host(Config),
{ok, Pid} = ftp:open(Host, ?FTP_PORT),
ok = ftp:close(Pid),
{error, ehost} = ftp:open(?BAD_HOST, []),
ok.
%%-------------------------------------------------------------------------
passive_user(doc) ->
["Open an ftp connection to a host, and logon as anonymous ftp."];
passive_user(suite) ->
[];
passive_user(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
io:format("Pid: ~p~n",[Pid]),
do_user(Pid).
%%-------------------------------------------------------------------------
passive_pwd(doc) ->
["Test ftp:pwd/1 & ftp:lpwd/1"];
passive_pwd(suite) ->
[];
passive_pwd(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_pwd(Pid).
%%-------------------------------------------------------------------------
passive_cd(doc) ->
["Open an ftp connection, log on as anonymous ftp, and cd to the"
"directory \"/pub\" and the to the non-existent directory."];
passive_cd(suite) ->
[];
passive_cd(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_cd(Pid).
%%-------------------------------------------------------------------------
passive_lcd(doc) ->
["Test api function ftp:lcd/2"];
passive_lcd(suite) ->
[];
passive_lcd(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
PrivDir = ?config(priv_dir, Config),
do_lcd(Pid, PrivDir).
%%-------------------------------------------------------------------------
passive_ls(doc) ->
["Open an ftp connection; ls the current directory, and the "
"\"incoming\" directory. We assume that ls never fails, since "
"it's output is meant to be read by humans. "];
passive_ls(suite) ->
[];
passive_ls(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_ls(Pid).
%%-------------------------------------------------------------------------
passive_nlist(doc) ->
["Open an ftp connection; nlist the current directory, and the "
"\"incoming\" directory. Nlist does not behave consistenly over "
"operating systems. On some it is an error to have an empty "
"directory."];
passive_nlist(suite) ->
[];
passive_nlist(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_nlist(Pid).
%%-------------------------------------------------------------------------
passive_rename(doc) ->
["Transfer a file to the server, and rename it; then remove it."];
passive_rename(suite) ->
[];
passive_rename(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_rename(Pid, Config).
%%-------------------------------------------------------------------------
passive_delete(doc) ->
["Transfer a file to the server, and then delete it"];
passive_delete(suite) ->
[];
passive_delete(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_delete(Pid, Config).
%%-------------------------------------------------------------------------
passive_mkdir(doc) ->
["Make a remote directory, cd to it, go to parent directory, and "
"remove the directory."];
passive_mkdir(suite) ->
[];
passive_mkdir(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_mkdir(Pid).
%%-------------------------------------------------------------------------
passive_send(doc) ->
["Create a local file in priv_dir; open an ftp connection to a host; "
"logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
"priv_dir; send the file; get a directory listing and check that "
"the file is on the list;, delete the remote file; get another listing "
"and check that the file is not on the list; close the session; "
"delete the local file."];
passive_send(suite) ->
[];
passive_send(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send(Pid, Config).
%%-------------------------------------------------------------------------
passive_append(doc) ->
["Create a local file in priv_dir; open an ftp connection to a host; "
"logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
"priv_dir; append the file to a file at the remote side that not exits"
"this will create the file at the remote side. Then it append the file "
"again. When this is done it recive the remote file and control that"
"the content is doubled in it.After that it will remove the files"];
passive_append(suite) ->
[];
passive_append(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_append(Pid, Config).
%%-------------------------------------------------------------------------
passive_send_bin(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"send a binary; remove file; close the connection."];
passive_send_bin(suite) ->
[];
passive_send_bin(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send_bin(Pid, Config).
%%-------------------------------------------------------------------------
passive_append_bin(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"append a binary twice; get the file and compare the content"
"remove file; close the connection."];
passive_append_bin(suite) ->
[];
passive_append_bin(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_append_bin(Pid, Config).
%%-------------------------------------------------------------------------
passive_send_chunk(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"send chunks; remove file; close the connection."];
passive_send_chunk(suite) ->
[];
passive_send_chunk(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send_chunk(Pid, Config).
%%-------------------------------------------------------------------------
passive_append_chunk(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"append chunks;control content remove file; close the connection."];
passive_append_chunk(suite) ->
[];
passive_append_chunk(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_append_chunk(Pid, Config).
%%-------------------------------------------------------------------------
passive_recv(doc) ->
["Create a local file and transfer it to the remote host into the "
"the \"incoming\" directory, remove "
"the local file. Then open a new connection; cd to \"incoming\", "
"lcd to the private directory; receive the file; delete the "
"remote file; close connection; check that received file is in "
"the correct directory; cleanup." ];
passive_recv(suite) ->
[];
passive_recv(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_recv(Pid, Config).
%%-------------------------------------------------------------------------
passive_recv_bin(doc) ->
["Send a binary to the remote host; and retreive "
"the file; then remove the file."];
passive_recv_bin(suite) ->
[];
passive_recv_bin(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_recv_bin(Pid, Config).
%%-------------------------------------------------------------------------
passive_recv_chunk(doc) ->
["Send a binary to the remote host; Connect again, and retreive "
"the file; then remove the file."];
passive_recv_chunk(suite) ->
[];
passive_recv_chunk(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_recv_chunk(Pid, Config).
%%-------------------------------------------------------------------------
passive_type(doc) ->
["Test that we can change btween ASCCI and binary transfer mode"];
passive_type(suite) ->
[];
passive_type(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_type(Pid).
%%-------------------------------------------------------------------------
passive_quote(doc) ->
[""];
passive_quote(suite) ->
[];
passive_quote(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_quote(Pid).
%%-------------------------------------------------------------------------
passive_ip_v6_disabled(doc) ->
["Test ipv4 command PASV"];
passive_ip_v6_disabled(suite) ->
[];
passive_ip_v6_disabled(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send(Pid, Config).
%%-------------------------------------------------------------------------
active_user(doc) ->
["Open an ftp connection to a host, and logon as anonymous ftp."];
active_user(suite) ->
[];
active_user(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_user(Pid).
%%-------------------------------------------------------------------------
active_pwd(doc) ->
["Test ftp:pwd/1 & ftp:lpwd/1"];
active_pwd(suite) ->
[];
active_pwd(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_pwd(Pid).
%%-------------------------------------------------------------------------
active_cd(doc) ->
["Open an ftp connection, log on as anonymous ftp, and cd to the"
"directory \"/pub\" and to a non-existent directory."];
active_cd(suite) ->
[];
active_cd(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_cd(Pid).
%%-------------------------------------------------------------------------
active_lcd(doc) ->
["Test api function ftp:lcd/2"];
active_lcd(suite) ->
[];
active_lcd(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
PrivDir = ?config(priv_dir, Config),
do_lcd(Pid, PrivDir).
%%-------------------------------------------------------------------------
active_ls(doc) ->
["Open an ftp connection; ls the current directory, and the "
"\"incoming\" directory. We assume that ls never fails, since "
"it's output is meant to be read by humans. "];
active_ls(suite) ->
[];
active_ls(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_ls(Pid).
%%-------------------------------------------------------------------------
active_nlist(doc) ->
["Open an ftp connection; nlist the current directory, and the "
"\"incoming\" directory. Nlist does not behave consistenly over "
"operating systems. On some it is an error to have an empty "
"directory."];
active_nlist(suite) ->
[];
active_nlist(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_nlist(Pid).
%%-------------------------------------------------------------------------
active_rename(doc) ->
["Transfer a file to the server, and rename it; then remove it."];
active_rename(suite) ->
[];
active_rename(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_rename(Pid, Config).
%%-------------------------------------------------------------------------
active_delete(doc) ->
["Transfer a file to the server, and then delete it"];
active_delete(suite) ->
[];
active_delete(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_delete(Pid, Config).
%%-------------------------------------------------------------------------
active_mkdir(doc) ->
["Make a remote directory, cd to it, go to parent directory, and "
"remove the directory."];
active_mkdir(suite) ->
[];
active_mkdir(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_mkdir(Pid).
%%-------------------------------------------------------------------------
active_send(doc) ->
["Create a local file in priv_dir; open an ftp connection to a host; "
"logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
"priv_dir; send the file; get a directory listing and check that "
"the file is on the list;, delete the remote file; get another listing "
"and check that the file is not on the list; close the session; "
"delete the local file."];
active_send(suite) ->
[];
active_send(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send(Pid, Config).
%%-------------------------------------------------------------------------
active_append(doc) ->
["Create a local file in priv_dir; open an ftp connection to a host; "
"logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
"priv_dir; append the file to a file at the remote side that not exits"
"this will create the file at the remote side. Then it append the file "
"again. When this is done it recive the remote file and control that"
"the content is doubled in it.After that it will remove the files"];
active_append(suite) ->
[];
active_append(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_append(Pid, Config).
%%-------------------------------------------------------------------------
active_send_bin(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"send a binary; remove file; close the connection."];
active_send_bin(suite) ->
[];
active_send_bin(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send_bin(Pid, Config).
%%-------------------------------------------------------------------------
active_append_bin(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"append a binary twice; get the file and compare the content"
"remove file; close the connection."];
active_append_bin(suite) ->
[];
active_append_bin(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_append_bin(Pid, Config).
%%-------------------------------------------------------------------------
active_send_chunk(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"send chunks; remove file; close the connection."];
active_send_chunk(suite) ->
[];
active_send_chunk(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send_chunk(Pid, Config).
%%-------------------------------------------------------------------------
active_append_chunk(doc) ->
["Open a connection to a host; cd to the directory \"incoming\"; "
"append chunks;control content remove file; close the connection."];
active_append_chunk(suite) ->
[];
active_append_chunk(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_append_chunk(Pid, Config).
%%-------------------------------------------------------------------------
active_recv(doc) ->
["Create a local file and transfer it to the remote host into the "
"the \"incoming\" directory, remove "
"the local file. Then open a new connection; cd to \"incoming\", "
"lcd to the private directory; receive the file; delete the "
"remote file; close connection; check that received file is in "
"the correct directory; cleanup." ];
active_recv(suite) ->
[];
active_recv(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_recv(Pid, Config).
%%-------------------------------------------------------------------------
active_recv_bin(doc) ->
["Send a binary to the remote host; and retreive "
"the file; then remove the file."];
active_recv_bin(suite) ->
[];
active_recv_bin(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_recv_bin(Pid, Config).
%%-------------------------------------------------------------------------
active_recv_chunk(doc) ->
["Send a binary to the remote host; Connect again, and retreive "
"the file; then remove the file."];
active_recv_chunk(suite) ->
[];
active_recv_chunk(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_recv_chunk(Pid, Config).
%%-------------------------------------------------------------------------
active_type(doc) ->
["Test that we can change btween ASCCI and binary transfer mode"];
active_type(suite) ->
[];
active_type(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_type(Pid).
%%-------------------------------------------------------------------------
active_quote(doc) ->
[""];
active_quote(suite) ->
[];
active_quote(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_quote(Pid).
%%-------------------------------------------------------------------------
active_ip_v6_disabled(doc) ->
["Test ipv4 command PORT"];
active_ip_v6_disabled(suite) ->
[];
active_ip_v6_disabled(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
do_send(Pid, Config).
%%-------------------------------------------------------------------------
api_missuse(doc)->
["Test that behaviour of the ftp process if the api is abused"];
api_missuse(suite) -> [];
api_missuse(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
Host = ftp_host(Config),
%% Serious programming fault, connetion will be shut down
{error, {connection_terminated, 'API_violation'}} =
gen_server:call(Pid, {self(), foobar, 10}, infinity),
test_server:sleep(500),
undefined = process_info(Pid, status),
{ok, Pid2} = ?ftp_open(Host, []),
%% Serious programming fault, connetion will be shut down
gen_server:cast(Pid2, {self(), foobar, 10}),
test_server:sleep(500),
undefined = process_info(Pid2, status),
{ok, Pid3} = ?ftp_open(Host, []),
%% Could be an innocent misstake the connection lives.
Pid3 ! foobar,
test_server:sleep(500),
{status, _} = process_info(Pid3, status),
ok.
%%-------------------------------------------------------------------------
not_owner(doc) ->
["Test what happens if a process that not owns the connection tries "
"to use it"];
not_owner(suite) ->
[];
not_owner(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]),
receive
{OtherPid, ok} ->
{ok, _} = ftp:pwd(Pid)
end,
ok.
not_owner(FtpPid, Pid) ->
{error, not_connection_owner} = ftp:pwd(FtpPid),
ftp:close(FtpPid),
test_server:sleep(100),
Pid ! {self(), ok}.
%%-------------------------------------------------------------------------
progress_report(doc) ->
["Solaris 8 sparc test the option progress."];
progress_report(suite) ->
[progress_report_send, progress_report_recv].
%% --
progress_report_send(doc) ->
["Test the option progress for ftp:send/[2,3]"];
progress_report_send(suite) ->
[];
progress_report_send(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
ReportPid =
spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
do_send(Pid, Config),
receive
{ReportPid, ok} ->
ok
end.
%% --
progress_report_recv(doc) ->
["Test the option progress for ftp:recv/[2,3]"];
progress_report_recv(suite) ->
[];
progress_report_recv(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
ReportPid =
spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
do_recv(Pid, Config),
receive
{ReportPid, ok} ->
ok
end,
ok.
progress(#progress{} = Progress , _File, {file_size, Total}) ->
progress_report_receiver ! start,
Progress#progress{total = Total};
progress(#progress{total = Total, current = Current}
= Progress, _File, {transfer_size, 0}) ->
progress_report_receiver ! finish,
case Total of
unknown ->
ok;
Current ->
ok;
_ ->
test_server:fail({error, {progress, {total, Total},
{current, Current}}})
end,
Progress;
progress(#progress{current = Current} = Progress, _File,
{transfer_size, Size}) ->
progress_report_receiver ! update,
Progress#progress{current = Current + Size}.
progress_report_receiver_init(Pid, N) ->
register(progress_report_receiver, self()),
receive
start ->
ok
end,
progress_report_receiver_loop(Pid, N-1).
progress_report_receiver_loop(Pid, N) ->
receive
update ->
progress_report_receiver_loop(Pid, N);
finish when N =:= 0 ->
Pid ! {self(), ok};
finish ->
Pid ! {self(), ok},
receive
start ->
ok
end,
progress_report_receiver_loop(Pid, N-1)
end.
%%-------------------------------------------------------------------------
%% Ticket test cases
%%-------------------------------------------------------------------------
ticket_6035(doc) -> ["Test that owning process that exits with reason "
"'shutdown' does not cause an error message."];
ticket_6035(suite) -> [];
ticket_6035(Config) ->
p("ticket_6035 -> entry with"
"~n Config: ~p", [Config]),
PrivDir = ?config(priv_dir, Config),
LogFile = filename:join([PrivDir,"ticket_6035.log"]),
try
begin
Host = dirty_select_ftpd_host(Config),
Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
error_logger:logfile({open, LogFile}),
ok = kill_ftp_proc_6035(Pid,LogFile),
error_logger:logfile(close),
p("ticket_6035 -> done", []),
ok
end
catch
throw:{error, not_found} ->
{skip, "No available FTP servers"}
end.
kill_ftp_proc_6035(Pid, LogFile) ->
p("kill_ftp_proc_6035 -> entry"),
receive
open ->
p("kill_ftp_proc_6035 -> received open: send shutdown"),
exit(Pid, shutdown),
kill_ftp_proc_6035(Pid, LogFile);
{open_failed, Reason} ->
p("kill_ftp_proc_6035 -> received open_failed"
"~n Reason: ~p", [Reason]),
exit({skip, {failed_openening_server_connection, Reason}})
after
5000 ->
p("kill_ftp_proc_6035 -> timeout"),
is_error_report_6035(LogFile)
end.
open_wait_6035(FtpServer, From) ->
p("open_wait_6035 -> try connect to ~s", [FtpServer]),
case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of
{ok, Pid} ->
p("open_wait_6035 -> connected, now login"),
LoginResult = ftp:user(Pid,"anonymous","kldjf"),
p("open_wait_6035 -> login result: ~p", [LoginResult]),
From ! open,
receive
dummy ->
p("open_wait_6035 -> received dummy"),
ok
after
10000 ->
p("open_wait_6035 -> timeout"),
ok
end,
p("open_wait_6035 -> done(ok)"),
ok;
{error, Reason} ->
p("open_wait_6035 -> open failed"
"~n Reason: ~p", [Reason]),
From ! {open_failed, {Reason, FtpServer}},
p("open_wait_6035 -> done(error)"),
ok
end.
is_error_report_6035(LogFile) ->
p("is_error_report_6035 -> entry"),
Res =
case file:read_file(LogFile) of
{ok, Bin} ->
p("is_error_report_6035 -> logfile read"),
read_log_6035(binary_to_list(Bin));
_ ->
ok
end,
p("is_error_report_6035 -> logfile read result: "
"~n ~p", [Res]),
file:delete(LogFile),
Res.
read_log_6035("=ERROR REPORT===="++_Rest) ->
error_report;
read_log_6035([_H|T]) ->
read_log_6035(T);
read_log_6035([]) ->
ok.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
do_user(Pid) ->
{error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS),
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
ok.
do_pwd(Pid) ->
{ok, "/"} = ftp:pwd(Pid),
{ok, Path} = ftp:lpwd(Pid),
{ok, Path} = file:get_cwd(),
ok.
do_cd(Pid) ->
ok = ftp:cd(Pid, "/pub"),
{error, epath} = ftp:cd(Pid, ?BAD_DIR),
ok.
do_lcd(Pid, Dir) ->
ok = ftp:lcd(Pid, Dir),
{error, epath} = ftp:lcd(Pid, ?BAD_DIR),
ok.
do_ls(Pid) ->
{ok, _} = ftp:ls(Pid),
{ok, _} = ftp:ls(Pid, "incoming"),
%% neither nlist nor ls operates on a directory
%% they operate on a pathname, which *can* be a
%% directory, but can also be a filename or a group
%% of files (including wildcards).
{ok, _} = ftp:ls(Pid, "incom*"),
ok.
do_nlist(Pid) ->
{ok, _} = ftp:nlist(Pid),
{ok, _} = ftp:nlist(Pid, "incoming"),
%% neither nlist nor ls operates on a directory
%% they operate on a pathname, which *can* be a
%% directory, but can also be a filename or a group
%% of files (including wildcards).
{ok, _} = ftp:nlist(Pid, "incom*"),
%% {error, epath} = ftp:nlist(Pid, ?BAD_DIR),
ok.
do_rename(Pid, Config) ->
PrivDir = ?config(priv_dir, Config),
LFile = ?config(file, Config),
NewLFile = ?config(new_file, Config),
AbsLFile = filename:absname(LFile, PrivDir),
Contents = "ftp_SUITE test ...",
ok = file:write_file(AbsLFile, list_to_binary(Contents)),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:lcd(Pid, PrivDir),
ftp:delete(Pid, LFile), % reset
ftp:delete(Pid, NewLFile), % reset
ok = ftp:send(Pid, LFile),
{error, epath} = ftp:rename(Pid, NewLFile, LFile),
ok = ftp:rename(Pid, LFile, NewLFile),
ftp:delete(Pid, LFile), % cleanup
ftp:delete(Pid, NewLFile), % cleanup
ok.
do_delete(Pid, Config) ->
PrivDir = ?config(priv_dir, Config),
LFile = ?config(file, Config),
AbsLFile = filename:absname(LFile, PrivDir),
Contents = "ftp_SUITE test ...",
ok = file:write_file(AbsLFile, list_to_binary(Contents)),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:lcd(Pid, PrivDir),
ftp:delete(Pid,LFile), % reset
ok = ftp:send(Pid, LFile),
ok = ftp:delete(Pid,LFile),
ok.
do_mkdir(Pid) ->
{A, B, C} = erlang:now(),
NewDir = "nisse_" ++ integer_to_list(A) ++ "_" ++
integer_to_list(B) ++ "_" ++ integer_to_list(C),
ok = ftp:cd(Pid, "incoming"),
{ok, CurrDir} = ftp:pwd(Pid),
ok = ftp:mkdir(Pid, NewDir),
ok = ftp:cd(Pid, NewDir),
ok = ftp:cd(Pid, CurrDir),
ok = ftp:rmdir(Pid, NewDir),
ok.
do_send(Pid, Config) ->
PrivDir = ?config(priv_dir, Config),
LFile = ?config(file, Config),
RFile = LFile ++ ".remote",
AbsLFile = filename:absname(LFile, PrivDir),
Contents = "ftp_SUITE test ...",
ok = file:write_file(AbsLFile, list_to_binary(Contents)),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:lcd(Pid, PrivDir),
ok = ftp:send(Pid, LFile, RFile),
{ok, RFilesString} = ftp:nlist(Pid),
RFiles = split(RFilesString),
true = lists:member(RFile, RFiles),
ok = ftp:delete(Pid, RFile),
case ftp:nlist(Pid) of
{error, epath} ->
ok; % No files
{ok, RFilesString1} ->
RFiles1 = split(RFilesString1),
false = lists:member(RFile, RFiles1)
end,
ok = file:delete(AbsLFile).
do_append(Pid, Config) ->
PrivDir = ?config(priv_dir, Config),
LFile = ?config(file, Config),
RFile = ?config(new_file, Config),
AbsLFile = filename:absname(LFile, PrivDir),
Contents = "ftp_SUITE test:appending\r\n",
ok = file:write_file(AbsLFile, list_to_binary(Contents)),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:lcd(Pid, PrivDir),
%% remove files from earlier failed test case
ftp:delete(Pid, RFile),
ftp:delete(Pid, LFile),
ok = ftp:append(Pid, LFile, RFile),
ok = ftp:append(Pid, LFile, RFile),
ok = ftp:append(Pid, LFile),
%% Control the contents of the file
{ok, Bin1} = ftp:recv_bin(Pid, RFile),
ok = ftp:delete(Pid, RFile),
ok = file:delete(AbsLFile),
ok = check_content(binary_to_list(Bin1), Contents, double),
{ok, Bin2} = ftp:recv_bin(Pid, LFile),
ok = ftp:delete(Pid, LFile),
ok = check_content(binary_to_list(Bin2), Contents, singel),
ok.
do_send_bin(Pid, Config) ->
File = ?config(file, Config),
Contents = "ftp_SUITE test ...",
Bin = list_to_binary(Contents),
ok = ftp:cd(Pid, "incoming"),
{error, enotbinary} = ftp:send_bin(Pid, Contents, File),
ok = ftp:send_bin(Pid, Bin, File),
{ok, RFilesString} = ftp:nlist(Pid),
RFiles = split(RFilesString),
true = lists:member(File, RFiles),
ok = ftp:delete(Pid, File),
ok.
do_append_bin(Pid, Config) ->
File = ?config(file, Config),
Contents = "ftp_SUITE test ...",
Bin = list_to_binary(Contents),
ok = ftp:cd(Pid, "incoming"),
{error, enotbinary} = ftp:append_bin(Pid, Contents, File),
ok = ftp:append_bin(Pid, Bin, File),
ok = ftp:append_bin(Pid, Bin, File),
%% Control the contents of the file
{ok, Bin2} = ftp:recv_bin(Pid, File),
ok = ftp:delete(Pid,File),
ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double).
do_send_chunk(Pid, Config) ->
File = ?config(file, Config),
Contents = "ftp_SUITE test ...",
Bin = list_to_binary(Contents),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:send_chunk_start(Pid, File),
{error, echunk} = ftp:cd(Pid, "incoming"),
{error, enotbinary} = ftp:send_chunk(Pid, Contents),
ok = ftp:send_chunk(Pid, Bin),
ok = ftp:send_chunk(Pid, Bin),
ok = ftp:send_chunk_end(Pid),
{ok, RFilesString} = ftp:nlist(Pid),
RFiles = split(RFilesString),
true = lists:member(File, RFiles),
ok = ftp:delete(Pid, File),
ok.
do_append_chunk(Pid, Config) ->
File = ?config(file, Config),
Contents = ["ER","LE","RL"],
ok = ftp:cd(Pid, "incoming"),
ok = ftp:append_chunk_start(Pid, File),
{error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)),
ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))),
ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))),
ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))),
ok = ftp:append_chunk_end(Pid),
%%Control the contents of the file
{ok, Bin2} = ftp:recv_bin(Pid, File),
ok = check_content(binary_to_list(Bin2),"ERL", double),
ok = ftp:delete(Pid, File),
ok.
do_recv(Pid, Config) ->
PrivDir = ?config(priv_dir, Config),
File = ?config(file, Config),
Newfile = ?config(new_file, Config),
AbsFile = filename:absname(File, PrivDir),
Contents = "ftp_SUITE:recv test ...",
ok = file:write_file(AbsFile, list_to_binary(Contents)),
ok = ftp:cd(Pid, "incoming"),
ftp:delete(Pid, File), % reset
ftp:lcd(Pid, PrivDir),
ok = ftp:send(Pid, File),
ok = file:delete(AbsFile), % cleanup
test_server:sleep(100),
ok = ftp:lcd(Pid, PrivDir),
ok = ftp:recv(Pid, File),
{ok, Files} = file:list_dir(PrivDir),
true = lists:member(File, Files),
ok = file:delete(AbsFile), % cleanup
ok = ftp:recv(Pid, File, Newfile),
ok = ftp:delete(Pid, File), % cleanup
ok.
do_recv_bin(Pid, Config) ->
File = ?config(file, Config),
Contents1 = "ftp_SUITE test ...",
Bin1 = list_to_binary(Contents1),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:send_bin(Pid, Bin1, File),
test_server:sleep(100),
{ok, Bin2} = ftp:recv_bin(Pid, File),
ok = ftp:delete(Pid, File), % cleanup
Contents2 = binary_to_list(Bin2),
Contents1 = Contents2,
ok.
do_recv_chunk(Pid, Config) ->
File = ?config(file, Config),
Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
"IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII",
Contents1 = lists:flatten(lists:duplicate(10, Data)),
Bin1 = list_to_binary(Contents1),
ok = ftp:cd(Pid, "incoming"),
ok = ftp:type(Pid, binary),
ok = ftp:send_bin(Pid, Bin1, File),
test_server:sleep(100),
{error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>),
ok = ftp:recv_chunk_start(Pid, File),
{ok, Contents2} = recv_chunk(Pid, <<>>),
ok = ftp:delete(Pid, File), % cleanup
ok = find_diff(Contents2, Contents1, 1),
ok.
do_type(Pid) ->
ok = ftp:type(Pid, ascii),
ok = ftp:type(Pid, binary),
ok = ftp:type(Pid, ascii),
{error, etype} = ftp:type(Pid, foobar),
ok.
do_quote(Pid) ->
["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
[_| _] = ftp:quote(Pid, "help"),
%% This negativ test causes some ftp servers to hang. This test
%% is not important for the client, so we skip it for now.
%%["425 Can't build data connection: Connection refused."]
%% = ftp:quote(Pid, "list"),
ok.
watch_dog(Config) ->
Dog = test_server:timetrap(inets_test_lib:minutes(1)),
NewConfig = lists:keydelete(watchdog, 1, Config),
[{watchdog, Dog} | NewConfig].
close_connection(Config) ->
case ?config(ftp, Config) of
Pid when is_pid(Pid) ->
ok = ftp:close(Pid),
lists:delete({ftp, Pid}, Config);
_ ->
Config
end.
ftp_host(Config) ->
case ?config(ftp_remote_host, Config) of
undefined ->
exit({skip, "No host specified"});
Host ->
Host
end.
check_content(RContent, LContent, Amount) ->
LContent2 = case Amount of
double ->
LContent ++ LContent;
singel ->
LContent
end,
case string:equal(RContent, LContent2) of
true ->
ok;
false ->
%% Find where the diff is
Where = find_diff(RContent, LContent2, 1),
Where
end.
find_diff(A, A, _) ->
ok;
find_diff([H|T1], [H|T2], Pos) ->
find_diff(T1, T2, Pos+1);
find_diff(RC, LC, Pos) ->
{error, {diff, Pos, RC, LC}}.
recv_chunk(Pid, Acc) ->
case ftp:recv_chunk(Pid) of
ok ->
{ok, binary_to_list(Acc)};
{ok, Bin} ->
recv_chunk(Pid, <<Acc/binary, Bin/binary>>);
Error ->
Error
end.
split(Cs) ->
split(Cs, [], []).
split([$\r, $\n| Cs], I, Is) ->
split(Cs, [], [lists:reverse(I)| Is]);
split([C| Cs], I, Is) ->
split(Cs, [C| I], Is);
split([], I, Is) ->
lists:reverse([lists:reverse(I)| Is]).
do_ftp_open(Host, Flags) ->
io:format("do_ftp_open -> entry with"
"~n Host: ~p"
"~n Flags: ~p", [Host, Flags]),
case ftp:open(Host, Flags) of
{ok, _} = OK ->
OK;
{error, Reason} ->
Str =
lists:flatten(
io_lib:format("Unable to reach test FTP server ~p (~p)",
[Host, Reason])),
throw({skip, Str})
end.
passwd() ->
Host =
case inet:gethostname() of
{ok, H} ->
H;
_ ->
"localhost"
end,
"ftp_SUITE@" ++ Host.
ftpd_hosts(Config) ->
DataDir = ?config(data_dir, Config),
FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]),
io:format("FileName: ~p~n", [FileName]),
case file:consult(FileName) of
{ok, [Hosts]} when is_list(Hosts) ->
Hosts;
_ ->
[]
end.
wrapper(Prefix,doc,Func) ->
Prefix++Func(doc);
wrapper(_,X,Func) ->
Func(X).
data_dir(Config) ->
case ?config(data_dir, Config) of
List when (length(List) > 0) ->
PathList = filename:split(List),
{NewPathList,_} = lists:split((length(PathList)-1), PathList),
DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]),
NewConfig =
lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}),
NewConfig;
_ -> Config
end.
p(F) ->
p(F, []).
p(F, A) ->
case get(ftp_testcase) of
undefined ->
io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]);
TC when is_atom(TC) ->
io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A])
end.