diff options
Diffstat (limited to 'lib/diameter/test/slask/diameter_persistent_table_test.erl')
-rw-r--r-- | lib/diameter/test/slask/diameter_persistent_table_test.erl | 495 |
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..bb907a5777 --- /dev/null +++ b/lib/diameter/test/slask/diameter_persistent_table_test.erl @@ -0,0 +1,495 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-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% +%% + +%% +%%---------------------------------------------------------------------- +%% 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. + |