aboutsummaryrefslogtreecommitdiffstats
path: root/erts/test/nt_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/test/nt_SUITE.erl')
-rw-r--r--erts/test/nt_SUITE.erl551
1 files changed, 551 insertions, 0 deletions
diff --git a/erts/test/nt_SUITE.erl b/erts/test/nt_SUITE.erl
new file mode 100644
index 0000000000..7ff5c908e6
--- /dev/null
+++ b/erts/test/nt_SUITE.erl
@@ -0,0 +1,551 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%% Purpose: Test NT specific utilities
+-module(nt_SUITE).
+-include("test_server.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-export([all/1,init_per_testcase/2,fin_per_testcase/2,nt/1,handle_eventlog/2,
+ middleman/1,service_basic/1, service_env/1, user_env/1, synced/1,
+ service_prio/1,
+ logout/1, debug/1, restart/1, restart_always/1,stopaction/1,
+ shutdown_io/0,do_shutdown_io/0]).
+-define(TEST_TIMEOUT, ?t:seconds(180)).
+
+-define(TEST_SERVICES, [1,2,3,4,5,6,7,8,9,10,11]).
+
+all(suite) ->
+ case os:type() of
+ {win32,nt} ->
+ [nt, service_basic, service_env, user_env, synced, service_prio,
+ logout, debug,
+ restart, restart_always, stopaction];
+ _ -> [nt] %%% Just to give a little hint why they are skipped...
+ end.
+
+init_per_testcase(_Func, Config) ->
+ Dog = test_server:timetrap(?TEST_TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+fin_per_testcase(_Func, Config) ->
+ lists:foreach(fun(X) ->
+ catch remove_service("test_service_" ++
+ integer_to_list(X)) end,
+ ?TEST_SERVICES),
+ Dog = ?config(watchdog, Config),
+ catch test_server:timetrap_cancel(Dog),
+ ok.
+
+erlsrv() ->
+ os:find_executable(erlsrv).
+
+
+recv_prog_output(Port) ->
+ receive
+ {Port, {data, {eol,Data}}} ->
+ %%io:format("Got data: ~s~n", [Data]),
+ [ Data | recv_prog_output(Port)];
+ _X ->
+ %%io:format("Got data: ~p~n", [_X]),
+ Port ! {self(), close},
+ receive
+ _ ->
+ []
+ end
+ end.
+
+
+%%% X == parameters to erlsrv
+%%% returns command output without stderr
+do_command(X) ->
+ %%io:format("Command: ~s~n", [erlsrv() ++ " " ++ X]),
+ Port = open_port({spawn, erlsrv() ++ " " ++ X}, [stream, {line, 100}, eof, in]),
+ Res = recv_prog_output(Port),
+ case Res of
+ [] ->
+ failed;
+ _Y ->
+ %%io:format("~p~n",[_Y]),
+ ok
+ end.
+
+
+create_service(Name) ->
+ ok = do_command("add " ++ Name).
+
+start_service(Name) ->
+ ok = do_command("start " ++ Name).
+
+stop_service(Name) ->
+ ok = do_command("stop " ++ Name).
+
+remove_service(Name) ->
+ ok = do_command("remove " ++ Name).
+do_wait_for_it(_,0) ->
+ false;
+do_wait_for_it(FullName,N) ->
+ case net_adm:ping(FullName) of
+ pong ->
+ true;
+ _ ->
+ receive
+ after 1000 ->
+ do_wait_for_it(FullName,N-1)
+ end
+ end.
+
+wait_for_node(Name) ->
+ FullName = make_full_name(Name),
+ do_wait_for_it(FullName,30).
+
+make_full_name(Name) ->
+ [_,Suffix] = string:tokens(atom_to_list(node()),"@"),
+ list_to_atom(Name ++ "@" ++ Suffix).
+
+
+%%% The following tests are only run on NT:
+
+service_basic(doc) ->
+ ["Check some basic (cosmetic) service parameters"];
+service_basic(suite) -> [];
+service_basic(Config) when is_list(Config) ->
+ ?line Name = "test_service_20",
+ ?line IntName = Name++"_internal",
+ ?line Service = [{servicename,Name},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]},
+ {internalservicename,IntName},
+ {comment,"Epic comment"}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line S2 = erlsrv:get_service(Name),
+ ?line {value,{comment,"Epic comment"}} = lists:keysearch(comment,1,S2),
+ ?line {value,{internalservicename,IntName}} =
+ lists:keysearch(internalservicename,1,S2),
+ ?line S3 = lists:keyreplace(comment,1,S2,{comment,"Basic comment"}),
+ ?line S4 = lists:keyreplace(internalservicename,1,S3,
+ {internalservicename,"WillNotHappen"}),
+ ?line ok = erlsrv:store_service(S4),
+ ?line S5 = erlsrv:get_service(Name),
+ ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S5),
+ ?line {value,{internalservicename,IntName}} =
+ lists:keysearch(internalservicename,1,S5),
+ ?line NewName = "test_service_21",
+ ?line S6 = erlsrv:new_service(NewName,S5,[]), % should remove
+ % internalservicename
+ ?line ok = erlsrv:store_service(S6),
+ ?line S7 = erlsrv:get_service(NewName),
+ ?line {value,{comment,"Basic comment"}} = lists:keysearch(comment,1,S7),
+ ?line {value,{internalservicename,[$t,$e,$s,$t | _]}} =
+ lists:keysearch(internalservicename,1,S7),
+ ?line remove_service(Name),
+ ?line remove_service(NewName),
+ ok.
+
+service_env(doc) ->
+ ["Check that service name and executable is in the environment of the " ++
+ "erlang process created by erlsrv."];
+service_env(suite) -> [];
+service_env(Config) when is_list(Config) ->
+ ?line Name = "test_service_2",
+ ?line Service = [{servicename,Name},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line Name = rpc:call(make_full_name(Name),os,getenv,
+ ["ERLSRV_SERVICE_NAME"]),
+ ?line "erlsrv.exe" = filename:basename(
+ hd(
+ string:tokens(
+ rpc:call(make_full_name(Name),
+ os,
+ getenv,
+ ["ERLSRV_EXECUTABLE"]),
+ "\""))),
+ ?line remove_service(Name),
+ ok.
+user_env(doc) ->
+ ["Check that the user defined environment is ADDED to the service's"++
+ " normal dito."];
+user_env(suite) -> [];
+user_env(Config) when is_list(Config) ->
+ ?line Name = "test_service_3",
+ ?line Service = [{servicename,Name},{env,[{"HUBBA","BUBBA"}]},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line true = rpc:call(make_full_name(Name),os,getenv,
+ ["SystemDrive"]) =/= false,
+ ?line "BUBBA" = rpc:call(make_full_name(Name),os,getenv,["HUBBA"]),
+ ?line remove_service(Name),
+ ok.
+synced(doc) ->
+ ["Check that services are stopped and started syncronous and that"++
+ " failed stopactions kill the erlang machine anyway."];
+synced(suite) -> [];
+synced(Config) when is_list(Config) ->
+ ?line Name0 = "test_service_4",
+ ?line Service0 = [{servicename,Name0},
+ {machine, "N:\\nickeNyfikenPaSjukhus"}],
+ ?line ok = erlsrv:store_service(Service0),
+ ?line true = (catch start_service(Name0)) =/= ok,
+ ?line remove_service(Name0),
+ ?line Name = "test_service_5",
+ ?line Service = [{servicename,Name},
+ {stopaction,"erlang:info(garbage_collection)."},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line T1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ ?line stop_service(Name),
+ ?line Diff1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - T1,
+ ?line true = Diff1 > 30,
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line T2 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ ?line remove_service(Name),
+ ?line Diff2 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - T2,
+ ?line true = Diff2 > 30,
+ ok.
+service_prio(doc) ->
+ ["Check that a service with higher prio create port programs with "
+ "higher prio."];
+service_prio(suite) -> [];
+service_prio(Config) when is_list(Config) ->
+ ?line Name = "test_service_6",
+ ?line Service = [{servicename,Name},{prio,"high"},
+ {env, [{"HEART_COMMAND","echo off"}]},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie()),
+ "-heart"]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line {ok, OldProcs} = get_current_procs(Config),
+ ?line start_service(Name),
+ ?line {ok, NewProcs} = get_current_procs(Config),
+ ?line remove_service(Name),
+ ?line Diff = arrived_procs(OldProcs,NewProcs),
+ %% Not really correct, could fail if another heart is
+ %% started at the same time...
+ ?line {value, {"heart.exe",_,"high"}} =
+ lists:keysearch("heart.exe",1,Diff),
+ ok.
+logout(doc) ->
+ ["Check that logout does not kill services"];
+logout(suite) -> [];
+logout(Config) when is_list(Config) ->
+ ?line {comment, "Have to be run manually by registering a service with " ++
+ "heart, logout and log in again and then examine that the heart " ++
+ "process id is not changed."}.
+debug(doc) ->
+ ["Check the debug options to erlsrv."];
+debug(suite) -> [];
+debug(Config) when is_list(Config) ->
+ ?line Name0 = "test_service_7",
+
+ %% We used to set the privdir as temporary directory, but for some
+ %% reason we don't seem to have write access to that directory,
+ %% so we'll use the directory specified in the next line.
+ ?line TempDir = "C:/TEMP",
+ ?line Service0 = [{servicename,Name0},
+ {workdir,filename:nativename(TempDir)},
+ {debugtype,"reuse"},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service0),
+ ?line T1 = calendar:datetime_to_gregorian_seconds(
+ calendar:local_time()),
+ %% sleep a little
+ ?line receive after 2000 -> ok end,
+ ?line start_service(Name0),
+ ?line true = wait_for_node(Name0),
+ ?line LF = filename:join(TempDir, Name0++".debug"),
+ ?line {ok,Info0} = file:read_file_info(LF),
+ ?line T2 = calendar:datetime_to_gregorian_seconds(
+ Info0#file_info.mtime),
+ ?line true = T2 > T1,
+ ?line remove_service(Name0),
+ ?line file:delete(LF),
+ ?line Name1 = "test_service_8",
+ ?line Service1 = [{servicename,Name1},
+ {workdir, filename:nativename(TempDir)},
+ {debugtype,"new"},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service1),
+ ?line T3 = calendar:datetime_to_gregorian_seconds(
+ calendar:local_time()),
+ %% sleep a little
+ ?line receive after 2000 -> ok end,
+ ?line NF = next_logfile(TempDir, Name1),
+ ?line start_service(Name1),
+ ?line true = wait_for_node(Name1),
+ ?line {ok,Info1} = file:read_file_info(NF),
+ ?line T4 = calendar:datetime_to_gregorian_seconds(
+ Info1#file_info.mtime),
+ ?line true = T4 > T3,
+ ?line remove_service(Name1),
+ ?line file:delete(NF),
+ ok.
+
+restart(doc) ->
+ ["Check the restart options to erlsrv"];
+restart(suite) -> [];
+restart(Config) when is_list(Config) ->
+ ?line Name = "test_service_9",
+ ?line Service = [{servicename,Name},
+ {workdir, filename:nativename(logdir(Config))},
+ {onfail,"restart"},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line receive after 20000 -> ok end,
+ ?line rpc:call(make_full_name(Name),erlang,halt,[]),
+ ?line receive after 1000 -> ok end,
+ ?line true = wait_for_node(Name),
+ ?line rpc:call(make_full_name(Name),erlang,halt,[]),
+ ?line receive after 1000 -> ok end,
+ ?line false = wait_for_node(Name),
+ ?line remove_service(Name),
+ ok.
+
+restart_always(doc) ->
+ ["Check the restart options to erlsrv"];
+restart_always(suite) -> [];
+restart_always(Config) when is_list(Config) ->
+ ?line Name = "test_service_10",
+ ?line Service = [{servicename,Name},
+ {workdir, filename:nativename(logdir(Config))},
+ {onfail,"restart_always"},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie())]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line rpc:call(make_full_name(Name),erlang,halt,[]),
+ ?line receive after 1000 -> ok end,
+ ?line true = wait_for_node(Name),
+ ?line rpc:call(make_full_name(Name),erlang,halt,[]),
+ ?line receive after 1000 -> ok end,
+ ?line true = wait_for_node(Name),
+ ?line remove_service(Name),
+ ok.
+stopaction(doc) ->
+ ["Check that stopaction does not hang output while shutting down"];
+stopaction(suite) -> [];
+stopaction(Config) when is_list(Config) ->
+ ?line Name = "test_service_11",
+ %% Icky, I prepend the first element in the codepath, cause
+ %% I "suppose" it's the one to where I am.
+ ?line Service = [{servicename,Name},
+ {stopaction,atom_to_list(?MODULE) ++ ":shutdown_io()."},
+ {args, ["-setcookie",
+ atom_to_list(erlang:get_cookie()),
+ "-pa", hd(code:get_path())]}],
+ ?line ok = erlsrv:store_service(Service),
+ ?line start_service(Name),
+ ?line true = wait_for_node(Name),
+ ?line T1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()),
+ ?line stop_service(Name),
+ ?line Diff1 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - T1,
+ ?line true = Diff1 < 30,
+ ?line remove_service(Name),
+ ok.
+
+
+%%% This test is run on all platforms, but just gives a comment on
+%%% other platforms than NT.
+
+nt(doc) ->
+ ["Run NT specific tests."];
+nt(suite) ->
+ [];
+nt(Config) when is_list(Config) ->
+ case os:type() of
+ {win32,nt} ->
+ nt_run();
+ _ ->
+ {skipped, "This test case is intended for Win NT only."}
+ end.
+
+
+nt_run() ->
+ ?line start_all(),
+ ?line create_service("test_service_1"),
+ ?line R = start_look_for_single("System","ErlSrv","Informational",
+ ".*test_service_1.*started.*"),
+ ?line start_service("test_service_1"),
+ ?line Res = look_for_single(R),
+ ?line io:format("Result from eventlog: ~p~n",
+ [Res]),
+ ?line remove_service("test_service_1"),
+ ?line stop_all(),
+ ok.
+
+start_all() ->
+ Pid1 = spawn_link(?MODULE,middleman,[[]]),
+ register(?MODULE,Pid1),
+ _Pid2 = nteventlog:start("log_testing",
+ {?MODULE,handle_eventlog,[Pid1]}).
+
+stop_all() ->
+ ?MODULE ! stop,
+ nteventlog:stop().
+
+start_look_for_single(Cat,Fac,Sev,MessRE) ->
+ Ref = make_ref(),
+ ?MODULE ! {lookfor, {self(), Ref, {Cat,Fac,Sev,MessRE}}},
+ Ref.
+
+look_for_single(Ref) ->
+ receive
+ {Ref,Time,Mes} ->
+ {Time,Mes}
+ after 60000 ->
+ timeout
+ end.
+
+
+%%% Mes = {Time,Category,Facility,Severity,Message}
+handle_eventlog(Mes,Pid) ->
+ Pid ! Mes.
+
+%%% Waitfor = [{Pid, Ref, {Category,Facility,Severity,MessageRE}} ...]
+middleman(Waitfor) ->
+ receive
+ {Time,Category,Facility,Severity,Message} ->
+ io:format("Middleman got ~s...", [Message]),
+ case match_event({Time,Category,Facility,Severity,Message},
+ Waitfor) of
+ {ok, {Pid,Ref,Time,Mes}, Rest} ->
+ io:format("matched~n"),
+ Pid ! {Ref,Time,Mes},
+ middleman(Rest);
+ _ ->
+ io:format("no match~n"),
+ middleman(Waitfor)
+ end;
+ {lookfor, X} ->
+ io:format("Middleman told to look for ~p~n", [X]),
+ middleman([X|Waitfor]);
+ stop ->
+ stopped;
+ _ ->
+ middleman(Waitfor)
+ end.
+
+
+%%% Matches events, not tail recursive.
+match_event(_X, []) ->
+ nomatch;
+match_event({Time,Cat,Fac,Sev,Mes},[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Tail]) ->
+ case regexp:match(Mes,MesRE) of
+ {match,_,_} ->
+ %%io:format("Match!~n"),
+ {ok,{Pid,Ref,Time,Mes},Tail};
+ _Z ->
+ %%io:format("No match (~p)~n",[_Z]),
+ case match_event({Time,Cat,Fac,Sev,Mes},Tail) of
+ {ok,X,Rest} ->
+ {ok,X,[{Pid,Ref,{Cat,Fac,Sev,MesRE}} | Rest]};
+ X ->
+ X
+ end
+ end;
+match_event(X,[Y | T]) ->
+ %%io:format("X == ~p, Y == ~p~n",[X,Y]),
+ case match_event(X,T) of
+ {ok,Z,R} ->
+ {ok,Z,[Y|R]};
+ XX ->
+ XX
+ end.
+
+arrived_procs(_,[]) ->
+ [];
+arrived_procs(OldProcs,[{Executable, Pid, Priority} | TNewProcs]) ->
+ case lists:keysearch(Pid,2,OldProcs) of
+ {value, _} ->
+ arrived_procs(OldProcs, TNewProcs);
+ false ->
+ [{Executable, Pid, Priority} | arrived_procs(OldProcs, TNewProcs)]
+ end.
+
+
+get_current_procs(Config) ->
+ ?line P = open_port({spawn,nt_info(Config) ++ " -E"},
+ [{line,10000}]),
+ ?line L = receive
+ {P,{data,{eol,D}}} ->
+ D;
+ _ -> "error. "
+ end,
+ ?line P ! {self(), close},
+ ?line receive
+ {P, closed} -> ok
+ end,
+ ?line {done,{ok,Tok,_},_} = erl_scan:tokens([],L,0),
+ ?line erl_parse:parse_term(Tok).
+
+nt_info(Config) when is_list(Config) ->
+ ?line filename:join(?config(data_dir, Config), "nt_info").
+
+
+logdir(Config) ->
+ ?line ?config(priv_dir, Config).
+
+look_for_next(Template,L,N) ->
+ ?line FN = Template ++ integer_to_list(N),
+ ?line case lists:member(FN,L) of
+ true ->
+ ?line look_for_next(Template,L,N+1);
+ false ->
+ ?line FN
+ end.
+
+next_logfile(LD, Servicename) ->
+ ?line {ok, Files} = file:list_dir(LD),
+ ?line Ftmpl = Servicename ++ ".debug.",
+ ?line filename:join(LD,look_for_next(Ftmpl,Files,1)).
+
+%%% Functions run by the service
+
+do_shutdown_io() ->
+ receive
+ after 2000 ->
+ io:format("IO in shutting down...~n"),
+ erlang:halt()
+ end.
+
+shutdown_io() ->
+ spawn(?MODULE,do_shutdown_io,[]).