aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/test/slask/diameter_persistent_table_test.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/test/slask/diameter_persistent_table_test.erl')
-rw-r--r--lib/diameter/test/slask/diameter_persistent_table_test.erl495
1 files changed, 495 insertions, 0 deletions
diff --git a/lib/diameter/test/slask/diameter_persistent_table_test.erl b/lib/diameter/test/slask/diameter_persistent_table_test.erl
new file mode 100644
index 0000000000..25bbe41347
--- /dev/null
+++ b/lib/diameter/test/slask/diameter_persistent_table_test.erl
@@ -0,0 +1,495 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Verify the persistent-table component of the Diameter application
+%%----------------------------------------------------------------------
+%%
+-module(diameter_persistent_table_test).
+
+-export([
+ init_per_testcase/2, fin_per_testcase/2,
+
+ all/1,
+ suite_init/1, suite_fin/1,
+
+ simple_start_and_stop/1,
+ table_create_and_delete/1
+
+ ]).
+
+-export([t/0, t/1]).
+
+-include("diameter_test_lib.hrl").
+
+-record(command, {id, desc, cmd, verify}).
+
+
+t() -> diameter_test_server:t(?MODULE).
+t(Case) -> diameter_test_server:t({?MODULE, Case}).
+
+
+%% Test server callbacks
+init_per_testcase(Case, Config) ->
+ diameter_test_server:init_per_testcase(Case, Config).
+
+fin_per_testcase(Case, Config) ->
+ diameter_test_server:fin_per_testcase(Case, Config).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all(suite) ->
+ Cases =
+ [
+ simple_start_and_stop,
+ table_create_and_delete
+ ],
+ {req, [], {conf, suite_init, Cases, suite_fin}}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+suite_init(suite) -> [];
+suite_init(doc) -> [];
+suite_init(Config) when is_list(Config) ->
+ Config.
+
+
+suite_fin(suite) -> [];
+suite_fin(doc) -> [];
+suite_fin(Config) when is_list(Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Test case(s)
+%%
+
+simple_start_and_stop(suite) ->
+ [];
+simple_start_and_stop(doc) ->
+ [];
+simple_start_and_stop(Config) when is_list(Config) ->
+ diameter:enable_trace(100, io),
+ case diameter_persistent_table:start_link() of
+ {ok, Pid} ->
+ unlink(Pid);
+ {error, Reason} ->
+ exit({failed_starting, Reason})
+ end,
+
+ ok = diameter_persistent_table:stop(),
+ ok.
+
+
+table_create_and_delete(suite) ->
+ [];
+table_create_and_delete(doc) ->
+ [];
+table_create_and_delete(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+
+ %% Command range values
+ Initial = 100,
+ ClientCreation = 200,
+ Nice = 300,
+ Evil = 400,
+ End = 500,
+
+ Verbosity = min,
+ %% Verbosity = max,
+
+ Data01 = lists:sort([{a, 10}, {b, 20}, {c, 30}]),
+ Data02 = lists:sort([{x, 100}, {y, 200}, {z, 300}]),
+
+ Commands =
+ [
+ %% Initial commands
+ initial_command( Initial + 0,
+ "enable trace",
+ fun() -> diameter:enable_trace(Verbosity, io) end,
+ ok),
+ initial_command( Initial + 1,
+ "start persistent-table process",
+ fun() ->
+ case diameter_persistent_table:start_link() of
+ {ok, Pid} when is_pid(Pid) ->
+ ok;
+ Error ->
+ Error
+ end
+ end,
+ ok),
+
+ client_create_command( ClientCreation + 1,
+ "1",
+ client01),
+
+ client_create_command( ClientCreation + 2,
+ "2",
+ client02),
+
+ nice_command( Nice + 1,
+ "client 1 create table 1",
+ fun() ->
+ create_table(client01, tab01, []),
+ diameter_persistent_table:which_tables()
+ end,
+ fun([tab01] = Tabs) ->
+ {ok, Tabs};
+ (Unexpected) ->
+ {error, {bad_tables, Unexpected}}
+ end),
+
+ nice_command( Nice + 2,
+ "client 1 create table 2",
+ fun() ->
+ create_table(client01, tab02, []),
+ diameter_persistent_table:which_tables()
+ end,
+ fun([tab01, tab02] = Tabs) ->
+ {ok, Tabs};
+ ([tab02, tab01] = Tabs) ->
+ {ok, Tabs};
+ (Unexpected) ->
+ {error, {bad_tables, Unexpected}}
+ end),
+
+ nice_command( Nice + 3,
+ "client 2 create table 1",
+ fun() ->
+ create_table(client02, tab03, []),
+ diameter_persistent_table:which_tables(whereis(client02))
+ end,
+ fun([tab03] = Tabs) ->
+ {ok, Tabs};
+ (Unexpected) ->
+ {error, {bad_tables, Unexpected}}
+ end),
+
+ nice_command( Nice + 4,
+ "client 1 delete table 1",
+ fun() ->
+ delete_table(client01, tab01),
+ diameter_persistent_table:which_tables(whereis(client01))
+ end,
+ fun([tab02] = Tabs) ->
+ {ok, Tabs};
+ (Unexpected) ->
+ {error, {bad_tables, Unexpected}}
+ end),
+
+ nice_command( Nice + 5,
+ "client 1 fill in some data in tab02",
+ fun() ->
+ populate_table(client01, tab02, Data01),
+ lists:sort(ets:tab2list(tab02))
+ end,
+ fun(Data) when Data =:= Data01 ->
+ {ok, Data};
+ (Unexpected) ->
+ {error, {bad_data, Unexpected}}
+ end),
+
+ nice_command( Nice + 6,
+ "client 2 fill in some data in tab03",
+ fun() ->
+ populate_table(client02, tab03, Data02),
+ lists:sort(ets:tab2list(tab03))
+ end,
+ fun(Data) when Data =:= Data02 ->
+ {ok, Data};
+ (Unexpected) ->
+ {error, {bad_data, Unexpected}}
+ end),
+
+ nice_command( Nice + 7,
+ "simulate client 1 crash",
+ fun() ->
+ simulate_crash(client01)
+ end,
+ fun(ok) ->
+ {ok, crashed};
+ (Unexpected) ->
+ {error, {bad_simulation_result, Unexpected}}
+ end),
+
+ client_create_command( Nice + 8,
+ "1 restarted",
+ client01),
+
+ nice_command( Nice + 9,
+ "client 1 create tab02 - verify data",
+ fun() ->
+ create_table(client01, tab02, []),
+ lists:sort(ets:tab2list(tab02))
+ end,
+ fun(Data) when Data =:= Data01 ->
+ {ok, Data};
+ (Unexpected) ->
+ {error, {bad_data, Unexpected}}
+ end),
+
+ evil_command( Evil + 1,
+ "try (and fail) to delete the non-existing table tab04",
+ fun() ->
+ delete_table(client02, tab04)
+ end,
+ fun({error, {unknown_table, tab04}}) ->
+ {ok, tab04};
+ (X) ->
+ {error, {bad_result, X}}
+ end),
+
+ evil_command( Evil + 2,
+ "try (and fail) to delete a not owned table tab02",
+ fun() ->
+ delete_table(client02, tab02)
+ end,
+ fun({error, {not_owner, tab02}}) ->
+ {ok, tab02};
+ (X) ->
+ {error, {bad_result, X}}
+ end),
+
+ evil_command( Evil + 3,
+ "try (and fail) to create an already existing *and* owned table - tab03",
+ fun() ->
+ create_table(client02, tab03, [])
+ end,
+ fun({error, {already_owner, tab03}}) ->
+ {ok, tab03};
+ (X) ->
+ {error, {bad_result, X}}
+ end),
+
+ evil_command( Evil + 4,
+ "try (and fail) to create an already existing not owned table - tab02",
+ fun() ->
+ create_table(client02, tab02, [])
+ end,
+ fun({error, {not_owner, _Owner, tab02}}) ->
+ {ok, tab02};
+ (X) ->
+ {error, {bad_result, X}}
+ end),
+
+ end_command( End + 1,
+ "stop client01",
+ fun() -> stop_client(client01) end),
+
+ end_command( End + 2,
+ "stop client02",
+ fun() -> stop_client(client02) end),
+
+ end_command( End + 2,
+ "stop persistent-table",
+ fun() -> diameter_persistent_table:stop() end),
+
+ evil_command( Evil + 5,
+ "try (and fail) to stop a not running persistent-table process",
+ fun() ->
+ diameter_persistent_table:stop()
+ end,
+ fun({'EXIT', {noproc, _}}) ->
+ {ok, not_running};
+ (X) ->
+ {error, {bad_result, X}}
+ end)
+
+ ],
+
+ exec(Commands).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%
+%% Command engine
+%%
+
+exec([]) ->
+ ok;
+exec([#command{id = No,
+ desc = Desc,
+ cmd = Cmd,
+ verify = Verify}|Commands]) ->
+ io:format("Executing command ~2w: ~s: ", [No, Desc]),
+ case (catch Verify((catch Cmd()))) of
+ {ok, OK} ->
+ io:format("ok => ~p~n", [OK]),
+ exec(Commands);
+ {error, Reason} ->
+ io:format("error => ~p~n", [Reason]),
+ {error, {bad_result, No, Reason}};
+ Error ->
+ io:format("exit => ~p~n", [Error]),
+ {error, {unexpected_result, No, Error}}
+ end.
+
+initial_command(No, Desc0, Cmd, VerifyVal) when is_function(Cmd) ->
+ Desc = lists:flatten(io_lib:format("Initial - ~s", [Desc0])),
+ command(No, Desc, Cmd, VerifyVal).
+
+client_create_command(No, Desc0, Name) ->
+ Desc = lists:flatten(io_lib:format("Client create - ~s", [Desc0])),
+ Self = self(),
+ Cmd = fun() -> start_client(Self, Name) end,
+ command(No, Desc, Cmd, ok).
+
+nice_command(No, Desc0, Cmd, Verify)
+ when is_function(Cmd) andalso is_function(Verify) ->
+ Desc = lists:flatten(io_lib:format("Nice - ~s", [Desc0])),
+ command(No, Desc, Cmd, Verify).
+
+evil_command(No, Desc0, Cmd, Verify)
+ when is_function(Cmd) andalso is_function(Verify) ->
+ Desc = lists:flatten(io_lib:format("Evil - ~s", [Desc0])),
+ command(No, Desc, Cmd, Verify).
+
+end_command(No, Desc0, Cmd) when is_function(Cmd) ->
+ Desc = lists:flatten(io_lib:format("End - ~s", [Desc0])),
+ command(No, Desc, Cmd, ok).
+
+command(No, Desc, Cmd, Verify)
+ when (is_integer(No) andalso
+ is_list(Desc) andalso
+ is_function(Cmd) andalso
+ is_function(Verify)) ->
+ #command{id = No,
+ desc = Desc,
+ cmd = Cmd,
+ verify = Verify};
+command(No, Desc, Cmd, VerifyVal)
+ when (is_integer(No) andalso
+ is_list(Desc) andalso
+ is_function(Cmd)) ->
+ Verify =
+ fun(Val) ->
+ case Val of
+ VerifyVal ->
+ {ok, Val};
+ _ ->
+ {error, Val}
+ end
+ end,
+ #command{id = No,
+ desc = Desc,
+ cmd = Cmd,
+ verify = Verify}.
+
+
+start_client(Parent, Name) ->
+ ClientPid = spawn_link(fun() -> client_init(Parent, Name) end),
+ receive
+ {ClientPid, started} ->
+ ClientPid,
+ ok;
+ {'EXIT', ClientPid, Reason} ->
+ {error, {failed_starting_client, Reason}}
+ end.
+
+stop_client(Client) ->
+ Pid = whereis(Client),
+ Pid ! stop,
+ receive
+ {'EXIT', Pid, normal} ->
+ ok
+ end.
+
+create_table(Client, Tab, Opts) ->
+ Self = self(),
+ Pid = whereis(Client),
+ Pid ! {create_table, Tab, Opts, Self},
+ receive
+ {Pid, created} ->
+ ok;
+ {Pid, {create_failed, Error}} ->
+ Error
+ end.
+
+delete_table(Client, Tab) ->
+ Self = self(),
+ Pid = whereis(Client),
+ Pid ! {delete_table, Tab, Self},
+ receive
+ {Pid, deleted} ->
+ ok;
+ {Pid, {delete_failed, Error}} ->
+ Error
+ end.
+
+populate_table(Client, Tab, Data) ->
+ Self = self(),
+ Pid = whereis(Client),
+ Pid ! {populate_table, Tab, Data, Self},
+ receive
+ {Pid, populated} ->
+ ok
+ end.
+
+simulate_crash(Client) ->
+ Pid = whereis(Client),
+ Pid ! simulate_crash,
+ receive
+ {'EXIT', Pid, simulated_crash} ->
+ ok
+ end.
+
+client_init(Parent, Name) ->
+ erlang:register(Name, self()),
+ process_flag(trap_exit, true),
+ Parent ! {self(), started},
+ client_loop().
+
+client_loop() ->
+ receive
+ stop ->
+ exit(normal);
+
+ {create_table, T, Opts, From} when is_atom(T) andalso is_list(Opts) ->
+ case diameter_persistent_table:create(T, Opts) of
+ ok ->
+ From ! {self(), created};
+ Error ->
+ From ! {self(), {create_failed, Error}}
+ end,
+ client_loop();
+
+ {delete_table, T, From} ->
+ case diameter_persistent_table:delete(T) of
+ ok ->
+ From ! {self(), deleted};
+ Error ->
+ From ! {self(), {delete_failed, Error}}
+ end,
+ client_loop();
+
+ {populate_table, Tab, Data, From} ->
+ ets:insert(Tab, Data),
+ From ! {self(), populated},
+ client_loop();
+
+ simulate_crash ->
+ exit(simulated_crash)
+ end.
+