-module(gm_ctflow_SUITE). -export([ all/0 , groups/0 , init_per_suite/1 , end_per_suite/1 , init_per_group/2 , end_per_group/2 , init_per_testcase/2 , end_per_testcase/2 ]). %% test cases -export([ put_a/1 , get_a/1 , erase_a/1 , init_counter/1 , incr/1 , check1/1 , check2/1 , check3/1 , init_worker/1 , append/1 , checkl1/1 , checkl2/1 , checkl3/1 , reset/1 , reset_flow/1 , status/1 ]). -include_lib("stdlib/include/assert.hrl"). all() -> [ {group, state} , {group, statefuns} , {group, workers} , {group, reset} ]. groups() -> [ {state, [sequence], [ put_a , get_a , erase_a ]} , {statefuns, [sequence], [ init_counter , incr , check1 %% state = 1 , incr , check2 %% state = 2 , incr , check3 %% state = 3 , status ]} , {workers, [sequence], [ init_counter , init_worker , append , checkl1 %% [A0] , append , checkl2 %% [A0, A1] , append , checkl3 %% [A0, A1, A2] , status ]} , {reset, [sequence], [ init_counter , init_worker , append %% also increments , check1 %% so, state = 1 , checkl1 , reset_flow , checkl1 %% flow 'my_list' unaffected , init_counter , incr , check1 , status ]} ]. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_Grp, Config) -> ok = application:start(gm_ctflow, permanent), Config. end_per_group(_Grp, _Config) -> ok = application:stop(gm_ctflow), ok. init_per_testcase(_Case, Config) -> gm_ctflow:status(), Config. end_per_testcase(_Case, _Config) -> gm_ctflow:status(), ok. %% ====================================================================== %% State update commands put_a(_Cfg) -> ok = gm_ctflow:put(a, 1), ?assertMatch(1, gm_ctflow:get(a)). get_a(_Cfg) -> ?assertMatch(1, gm_ctflow:get(a)). erase_a(_Cfg) -> ok = gm_ctflow:erase(a), ?assertException(error, {no_such_key,a}, gm_ctflow:get(a)), ?assertMatch(undefined, gm_ctflow:get(a, undefined)). %% ====================================================================== %% This is our 'ctflow_fun' instance, which floats above some simple %% test cases. In this test suite, we only exercise the flow funs, but %% in a normal test suite, we would use them for information that isn't %% easily threaded through the sequence of cases otherwise. %% init_counter(_Cfg) -> gm_ctflow:status(), gm_ctflow_fun:new(my_counter, fun ctr/2, 0), ok. incr(_Cfg) -> Res = gm_ctflow_fun:call(my_counter, incr), ?assert(is_integer(Res)), ok. check1(_Cfg) -> check_(1). check2(_Cfg) -> check_(2). check3(_Cfg) -> check_(3). check_(N) -> ?assertEqual(N, gm_ctflow_fun:get_state(my_counter)), ok. %% This is the actual fun ctr(incr, N) -> NewN = N+1, {ok, NewN, NewN}. reset_flow(_Cfg) -> gm_ctflow:reset_flow(my_counter). status(_Cfg) -> gm_ctflow:status(). %% ====================================================================== %% Here, we make use of the `gm_ctflow_worker` it's a complete process %% (a gen_server, whose logic we instrument with the fun we provice). %% For a bit of variety, we re-use the `ctr` fun, and at the same time, %% ensure that stopping and restarting the `gm_ctflow` app clears the state. %% (See `init_per_group/2` and `end_per_group/2`). %% %% Note that the worker is initiated using a fun/0, which in its turn is %% expected to provide the fun/3 for the gen_server callback logic. %% This is so that we can actually perform some actions at init time. %% init_worker(_Cfg) -> {ok, Pid} = gm_ctflow_worker:start(my_list, fun() -> {ok, fun lfun/3, []} end), true = is_pid(Pid), ok. append(_Cfg) -> C = gm_ctflow_fun:call(my_counter, incr), Res = gm_ctflow_worker:call(my_list, {append, {a, C}}), ?assert(is_list(Res)), ok. checkl1(_Cfg) -> checkl_([{a,1}]). checkl2(_Cfg) -> checkl_([{a,1},{a,2}]). checkl3(_Cfg) -> checkl_([{a,1},{a,2},{a,3}]). checkl_(L) -> ?assertEqual(L, gm_ctflow_worker:get_state(my_list)), ok. lfun({call,_From}, {append, Item}, L) -> NewL = L ++ [Item], {reply, NewL, NewL}. reset(_Cfg) -> gm_ctflow:reset().