Skip to content

Commit

Permalink
Add tests for amoc_controller
Browse files Browse the repository at this point in the history
  • Loading branch information
NelsonVides committed Dec 14, 2023
1 parent 5bdf14a commit f1e3d32
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 0 deletions.
39 changes: 39 additions & 0 deletions test/async_helper.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-module(async_helper).

-export([wait_until/2, wait_until/3]).

% @doc Waits `TimeLeft` for `Fun` to return `Expected Value`, then returns `ExpectedValue`
% If no value is returned or the result doesn't match `ExpectedValue` error is raised
wait_until(Fun, ExpectedValue) ->
wait_until(Fun, ExpectedValue, #{}).

%% Example: wait_until(fun () -> ... end, SomeVal, #{time_left => timer:seconds(2)})
%% if expected value is a function with arity 1, it's treated as a validation function.
wait_until(Fun, ValidatorFn, Opts0) when is_function(ValidatorFn, 1) ->
Defaults = #{time_left => timer:seconds(5), sleep_time => 50},
Opts1 = maps:merge(Defaults, Opts0),
Opts = Opts1#{validator => ValidatorFn, history => []},
do_wait_until(Fun, Opts);
wait_until(Fun, ExpectedValue, Opts) ->
ValidatorFn = fun(Value) -> Value =:= ExpectedValue end,
wait_until(Fun, ValidatorFn, Opts).

do_wait_until(_Fun, #{time_left := TimeLeft, history := History}) when TimeLeft =< 0 ->
error({badmatch, lists:reverse(History)});

do_wait_until(Fun, #{validator := Validator} = Opts) ->
try Fun() of
Value -> case Validator(Value) of
true -> {ok, Value};
_ -> wait_and_continue(Fun, Value, Opts)
end
catch Error:Reason:Stacktrace ->
wait_and_continue(Fun, {Error, Reason, Stacktrace}, Opts)
end.

wait_and_continue(Fun, FunResult, #{time_left := TimeLeft,
sleep_time := SleepTime,
history := History} = Opts) ->
timer:sleep(SleepTime),
do_wait_until(Fun, Opts#{time_left => TimeLeft - SleepTime,
history => [FunResult | History]}).
191 changes: 191 additions & 0 deletions test/controller_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
-module(controller_SUITE).
-compile([export_all, nowarn_export_all]).

-include_lib("stdlib/include/assert.hrl").

all() ->
[
{group, all_tests}
].

groups() ->
[
{all_tests, [sequence], all_tests()}
].

all_tests() ->
[
no_scenario_running_status_is_idle,
disable_controller_returns_status_disable,
disable_controller_then_cant_start_scenario,
start_non_existing_scenario_fails,
start_scenario_without_vars_fails,
start_scenario_runs_fine,
start_scenario_check_status,
add_users_to_started_scenario,
add_bad_range_of_users_to_started_scenario_fails,
add_users_scenario_not_started_fails,
remove_users_scenario_not_started_fails,
check_status_with_running_users_is_correct,
check_status_after_killing_one_user_is_correct,
stop_non_running_scenario_fails,
stop_running_scenario_with_no_users_immediately_terminates,
stop_running_scenario_with_users_stays_in_finished,
stop_running_scenario_with_users_eventually_terminates,
scenario_with_state_and_crashing_in_terminate_run_fine
].


%% setup and teardown
init_per_suite(Config) ->
Config.

end_per_suite(Config) ->
Config.

init_per_testcase(_TestCase, Config) ->
application:ensure_all_started(amoc),
Config.

end_per_testcase(_TestCase, Config) ->
application:stop(amoc),
Config.

%% test cases
no_scenario_running_status_is_idle(_) ->
Status = amoc_controller:get_status(),
?assertMatch(idle, Status).

disable_controller_returns_status_disable(_) ->
amoc_controller:disable(),
Status = amoc_controller:get_status(),
?assertMatch(disabled, Status).

disable_controller_then_cant_start_scenario(_) ->
amoc_controller:disable(),
Ret = do_start_scenario(testing_scenario),
?assertMatch({error, {invalid_status, disabled}}, Ret).

start_non_existing_scenario_fails(_) ->
Scenario = 'non_existing_come_on_dont_do_this_#(*&$%*(@_))',
Ret = do_start_scenario(Scenario),
?assertMatch({error, {no_such_scenario, Scenario}}, Ret).

start_scenario_without_vars_fails(_) ->
Ret = do_start_scenario(testing_scenario),
?assertMatch({error, {parameters_verification_failed, [{testing_var1, _, _}]}}, Ret).

start_scenario_runs_fine(_) ->
Ret = do_start_scenario(testing_scenario, regular_vars()),
?assertMatch(ok, Ret).

start_scenario_check_status(_) ->
do_start_scenario(testing_scenario, regular_vars()),
Status = amoc_controller:get_status(),
?assertMatch(
{running, #{scenario := testing_scenario,
currently_running_users := 0,
highest_user_id := 0}},
Status).

add_users_to_started_scenario(_) ->
do_start_scenario(testing_scenario, regular_vars()),
Ret = amoc_controller:add_users(1, 10),
?assertMatch(ok, Ret).

add_bad_range_of_users_to_started_scenario_fails(_) ->
do_start_scenario(testing_scenario, regular_vars()),
Ret = amoc_controller:add_users(10, 1),
?assertMatch({error, invalid_range}, Ret).

add_users_scenario_not_started_fails(_) ->
Ret = amoc_controller:add_users(1, 10),
?assertMatch({error, {invalid_status, idle}}, Ret).

remove_users_scenario_not_started_fails(_) ->
Ret = amoc_controller:add_users(1, 10),
?assertMatch({error, {invalid_status, idle}}, Ret).

check_status_with_running_users_is_correct(_) ->
do_start_scenario(testing_scenario, regular_vars()),
amoc_controller:add_users(1, 10),
Status = amoc_controller:get_status(),
?assertMatch(
{running, #{scenario := testing_scenario,
currently_running_users := _,
highest_user_id := 10}},
Status).

check_status_after_killing_one_user_is_correct(_) ->
do_start_scenario(testing_scenario, regular_vars()),
NumOfUsers = 10,
amoc_controller:add_users(1, NumOfUsers),
wait_until_scenario_has_users(testing_scenario, NumOfUsers, NumOfUsers),
Ret = amoc_controller:remove_users(1, true),
?assertMatch({ok, 1}, Ret),
wait_until_scenario_has_users(testing_scenario, NumOfUsers - 1, NumOfUsers).

stop_non_running_scenario_fails(_) ->
Ret = amoc_controller:stop_scenario(),
?assertMatch({error, {invalid_status, idle}}, Ret).

stop_running_scenario_with_no_users_immediately_terminates(_) ->
do_start_scenario(testing_scenario, regular_vars()),
Ret = amoc_controller:stop_scenario(),
?assertMatch(ok, Ret),
Status = amoc_controller:get_status(),
?assertMatch({finished, testing_scenario}, Status).

stop_running_scenario_with_users_stays_in_finished(_) ->
do_start_scenario(testing_scenario, regular_vars()),
NumOfUsers = 10,
amoc_controller:add_users(1, NumOfUsers),
wait_until_scenario_has_users(testing_scenario, NumOfUsers, NumOfUsers),
Ret = amoc_controller:stop_scenario(),
Status = amoc_controller:get_status(),
?assertMatch(ok, Ret),
?assertMatch({terminating, testing_scenario}, Status).

stop_running_scenario_with_users_eventually_terminates(_) ->
do_start_scenario(testing_scenario, regular_vars()),
NumOfUsers = 10,
amoc_controller:add_users(1, NumOfUsers),
wait_until_scenario_has_users(testing_scenario, NumOfUsers, NumOfUsers),
amoc_controller:stop_scenario(),
WaitUntilFun = fun amoc_controller:get_status/0,
WaitUntilValue = {finished, testing_scenario},
async_helper:wait_until(WaitUntilFun, WaitUntilValue).

scenario_with_state_and_crashing_in_terminate_run_fine(_) ->
do_start_scenario(testing_scenario_with_state, regular_vars_with_state()),
NumOfUsers = 10,
amoc_controller:add_users(1, NumOfUsers),
wait_until_scenario_has_users(testing_scenario_with_state, NumOfUsers, NumOfUsers),
amoc_controller:stop_scenario(),
WaitUntilFun = fun amoc_controller:get_status/0,
WaitUntilValue = {finished, testing_scenario_with_state},
async_helper:wait_until(WaitUntilFun, WaitUntilValue).

%% helpers
do_start_scenario(Scenario) ->
do_start_scenario(Scenario, []).

do_start_scenario(Scenario, Config) ->
Vars = other_vars_to_keep_quiet() ++ Config,
amoc_controller:start_scenario(Scenario, Vars).

regular_vars() ->
[{interarrival, 1}, {testing_var1, def1}].

regular_vars_with_state() ->
[{interarrival, 1}, {testing_state_var1, def1}].

other_vars_to_keep_quiet() ->
[{config_scenario_var1, unused_value}].

wait_until_scenario_has_users(Scenario, Current, Total) ->
WaitUntilFun = fun amoc_controller:get_status/0,
WaitUntilValue = {running, #{scenario => Scenario,
currently_running_users => Current,
highest_user_id => Total}},
async_helper:wait_until(WaitUntilFun, WaitUntilValue).
27 changes: 27 additions & 0 deletions test/testing_scenario.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-module(testing_scenario).

-behaviour(amoc_scenario).

-required_variable(#{name => testing_var1, description => "description1",
verification => [def1, another_value]}).

%% amoc_scenario behaviour
-export([init/0, start/1, terminate/0]).

-spec init() -> ok.
init() ->
ok.

-spec start(amoc_scenario:user_id()) -> any().
start(_Id) ->
%% Wait for any message to be send
receive
Msg ->
ct:pal("Msg ~p~n", [Msg]),
timer:sleep(100),
Msg
end.

-spec terminate() -> term().
terminate() ->
ok.
30 changes: 30 additions & 0 deletions test/testing_scenario_with_state.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-module(testing_scenario_with_state).

-behaviour(amoc_scenario).

-required_variable(#{name => testing_state_var1, description => "description1",
verification => [def1, another_value]}).

%% amoc_scenario behaviour
-export([init/0, start/2, terminate/1]).

-type state() :: #{_ := _}.
-export_type([state/0]).

-spec init() -> {ok, state()}.
init() ->
{ok, #{some_state => this_has_state}}.

-spec start(amoc_scenario:user_id(), state()) -> any().
start(_Id, #{some_state := this_has_state}) ->
%% Wait for anymessage to be send
receive
Msg ->
ct:pal("Msg ~p~n", [Msg]),
timer:sleep(100),
Msg
end.

-spec terminate(state()) -> any().
terminate(#{some_state := GiveState}) ->
throw(GiveState).

0 comments on commit f1e3d32

Please sign in to comment.