%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2013. 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% %% %% %% Test example code under ../examples/code. %% -module(diameter_examples_SUITE). -export([suite/0, all/0]). %% testcases -export([dict/1, dict/0, code/1, enslave/1, start/1, traffic/1, stop/1]). -export([install/1, call/1]). -include("diameter.hrl"). %% =========================================================================== -define(util, diameter_util). %% The order here is significant and causes the server to listen %% before the clients connect. -define(NODES, [compile, server, client]). %% Options to ct_slave:start/2. -define(TIMEOUTS, [{T, 15000} || T <- [boot_timeout, init_timeout, start_timeout]]). %% =========================================================================== suite() -> [{timetrap, {seconds, 45}}]. all() -> [dict, code, enslave, start, traffic, stop]. %% =========================================================================== %% dict/1 %% %% Compile example dictionaries in examples/dict. -define(EXAMPLES, [rfc4004_mip, rfc4005_nas, rfc4006_cc, rfc4072_eap, rfc4590_digest, rfc4740_sip]). dict() -> [{timetrap, {minutes, length(?EXAMPLES)}}]. dict(_Config) -> Dir = filename:join([code:lib_dir(diameter, examples), "dict"]), [] = [RC || D <- ?EXAMPLES, RC <- [dict(atom_to_list(D), Dir)], RC /= ok]. dict(Dict, Dir) -> {Name, Pre} = make_name(Dict), try ok = make(filename:join([Dir, Dict ++ ".dia"]), [{name, Name}, {prefix, Pre}, inherits("rfc3588_base") | opts(Dict)]), ok = compile(Name) catch throw: {_,_} = E -> E end. make(File, Opts) -> case diameter_make:codec(File, Opts) of ok -> ok; No -> throw({make, No}) end. compile(Name) -> case compile:file(Name ++ ".erl", [return]) of {ok, _, _} -> ok; No -> throw({compile, No}) end. opts(M) when M == "rfc4006_cc"; M == "rfc4072_eap" -> [inherits("rfc4005_nas")]; opts("rfc4740_sip") -> [inherits("rfc4590_digest")]; opts(_) -> []. inherits(File) -> {Name, _} = make_name(File), {inherits, File ++ "/" ++ Name}. make_name(File) -> {R, [$_|N]} = lists:splitwith(fun(C) -> C /= $_ end, File), {string:join(["diameter_gen", N, R], "_"), "diameter_" ++ N}. %% =========================================================================== %% code/1 %% %% Compile example code under examples/code. code(Config) -> Node = slave(hd(?NODES), here()), [] = rpc:call(Node, ?MODULE, install, [proplists:get_value(priv_dir, Config)]). %% Compile on another node since the code path may be modified. install(PrivDir) -> Top = install(here(), PrivDir), Src = filename:join([Top, "examples", "code"]), Files = find_files(Src, ".*\\.erl"), [] = [{F,E} || F <- Files, {error, _, _} = E <- [compile:file(F, [warnings_as_errors, return_errors])]]. %% Copy include files into a temporary directory and adjust the code %% path in order for example code to be able to include them with %% include_lib. This is really only required when running in the reop %% since generated includes, that the example code wants to %% include_lib, are under src/gen and there's no way to get get the %% preprocessor to find these otherwise. Generated hrls are only be %% under include in an installation. ("Installing" them locally is %% anathema.) install(Dir, PrivDir) -> %% Remove the path added by slave/1 (needed for the rpc:call/4 in %% compile/1 to find ?MODULE) so the call to code:lib_dir/2 below %% returns the installed path. [Ebin | _] = code:get_path(), true = code:del_path(Ebin), Top = top(Dir, code:lib_dir(diameter)), %% Create a new diameter/include in priv_dir, copy all includes %% there: first the installed includes, then any we find in %% ../include and ../src/gen. Tmp = filename:join([PrivDir, "diameter"]), TmpInc = filename:join([PrivDir, "diameter", "include"]), TmpEbin = filename:join([PrivDir, "diameter", "ebin"]), [] = [{T,E} || T <- [Tmp, TmpInc, TmpEbin], {error, E} <- [file:make_dir(T)]], Inc = filename:join([Top, "include"]), Gen = filename:join([Top, "src", "gen"]), Files = lists:append([find_files(F, ".*\\.hrl") || F <- [Inc, Gen]]), [] = [{F,E} || F <- Files, B <- [filename:basename(F)], D <- [filename:join([TmpInc, B])], {error, E} <- [file:copy(F,D)]], %% Prepend the created directory just so that code:lib_dir/1 finds %% it when compile:file/2 tries to resolve include_lib. true = code:add_patha(TmpEbin), Tmp = code:lib_dir(diameter), %% assert %% Return the top directory containing examples/code. Top. find_files(Dir, RE) -> filelib:fold_files(Dir, RE, false, fun cons/2, []). cons(H,T) -> [H|T]. %% =========================================================================== %% enslave/1 %% %% Start two nodes: one for the server, one for the client. enslave(Config) -> Dir = here(), Nodes = [{N, slave(N, Dir)} || N <- tl(?NODES)], ?util:write_priv(Config, nodes, Nodes). slave(Name, Dir) -> {ok, Node} = ct_slave:start(Name, ?TIMEOUTS), ok = rpc:call(Node, code, add_pathsa, [[Dir, filename:join([Dir, "..", "ebin"])]]), Node. here() -> filename:dirname(code:which(?MODULE)). top(Dir, LibDir) -> File = filename:join([Dir, "depend.sed"]), %% only in the repo case filelib:is_regular(File) of true -> filename:join([Dir, ".."]); false -> LibDir end. %% start/1 start(server) -> ok = diameter:start(), ok = server:start(), {ok, Ref} = server:listen(tcp), [_] = ?util:lport(tcp, Ref, 20), ok; start(client) -> ok = diameter:start(), true = diameter:subscribe(client), ok = client:start(), {ok, Ref} = client:connect(tcp), receive #diameter_event{info = {up, Ref, _, _, _}} -> ok end; start(Config) -> Nodes = ?util:read_priv(Config, nodes), [] = [RC || {T,N} <- Nodes, RC <- [rpc:call(N, ?MODULE, start, [T])], RC /= ok]. %% traffic/1 %% %% Send successful messages from client to server. traffic(server) -> ok; traffic(client) -> {_, MRef} = spawn_monitor(fun() -> call(100) end), receive {'DOWN', MRef, process, _, Reason} -> Reason end; traffic(Config) -> Nodes = ?util:read_priv(Config, nodes), [] = [RC || {T,N} <- Nodes, RC <- [rpc:call(N, ?MODULE, traffic, [T])], RC /= ok]. call(0) -> exit(ok); call(N) -> {ok, _} = client:call(), call(N-1). %% stop/1 stop(Name) when is_atom(Name) -> {ok, _Node} = ct_slave:stop(Name), ok; stop(_Config) -> [] = [RC || N <- ?NODES, RC <- [stop(N)], RC /= ok].