WIP: more projection refactoring, eunit tests pass for the moment

This commit is contained in:
Scott Lystig Fritchie 2015-04-09 12:16:58 +09:00
parent 0b2866d102
commit ce67fb662a
10 changed files with 248 additions and 164 deletions

View file

@ -18,23 +18,27 @@
%%
%% -------------------------------------------------------------------
-include("machi_projection.hrl").
-define(NOT_FLAPPING, {0,0,0}).
-type projection() :: #projection_v1{}.
-record(ch_mgr, {
init_finished :: boolean(),
active_p :: boolean(),
name :: pv1_server(),
proj :: projection(),
proj_history :: queue(),
myflu :: pid() | atom(),
flap_limit :: non_neg_integer(),
%%
runenv :: list(), %proplist()
opts :: list(), %proplist()
flaps=0 :: integer(),
flap_start = ?NOT_FLAPPING
:: erlang:now(),
runenv :: list(), %proplist()
opts :: list(), %proplist()
members_dict :: p_srvr_dict(),
%% Deprecated ... TODO: remove when old test unit test code is removed
proj_proposed :: 'none' | projection()

View file

@ -18,19 +18,33 @@
%%
%% -------------------------------------------------------------------
-ifndef(MACHI_PROJECTION_HRL).
-define(MACHI_PROJECTION_HRL, true).
-type pv1_csum() :: binary().
-type pv1_epoch() :: {pv1_epoch_n(), pv1_csum()}.
-type pv1_epoch_n() :: non_neg_integer().
-type pv1_server() :: atom() | binary().
-type pv1_timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
-record(p_srvr, {
name :: pv1_server(),
proto = 'ipv4' :: 'ipv4' | 'disterl', % disterl? Hrm.
address :: term(), % Protocol-specific
port :: term(), % Protocol-specific
props = [] :: list() % proplist for other related info
}).
-type p_srvr() :: #p_srvr{}.
-type p_srvr_dict() :: orddict:orddict(pv1_server(), p_srvr()).
-define(DUMMY_PV1_EPOCH, {0,<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}).
-record(projection_v1, {
epoch_number :: pv1_epoch_n(),
epoch_csum :: pv1_csum(),
all_members :: [pv1_server()],
member_dict :: orddict:orddict(),
members_dict :: p_srvr_dict(),
down :: [pv1_server()],
creation_time :: pv1_timestamp(),
author_server :: pv1_server(),
@ -42,12 +56,6 @@
-define(MACHI_DEFAULT_TCP_PORT, 50000).
-record(p_srvr, {
name :: pv1_server(),
proto = 'ipv4' :: 'ipv4' | 'disterl', % disterl? Hrm.
address :: term(), % Protocol-specific
port :: term(), % Protocol-specific
props = [] :: list() % proplist for other related info
}).
-define(SHA_MAX, (1 bsl (20*8))).
-endif. % !MACHI_PROJECTION_HRL

View file

@ -1,3 +1,5 @@
{require_otp_vsn, "17"}.
%%% {erl_opts, [warnings_as_errors, {parse_transform, lager_transform}, debug_info]}.
{erl_opts, [{parse_transform, lager_transform}, debug_info]}.
{edoc_opts, [{dir, "./edoc"}]}.

View file

@ -132,7 +132,7 @@ test_react_to_env(Pid) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init({MyName, All_list, MyFLUPid, MgrOpts}) ->
init({MyName, All_list, MembersDict, MgrOpts}) ->
RunEnv = [%% {seed, Seed},
{seed, now()},
{network_partitions, []},
@ -144,10 +144,12 @@ init({MyName, All_list, MyFLUPid, MgrOpts}) ->
NoneProj = make_initial_projection(MyName, All_list, [],
[], []),
S = #ch_mgr{init_finished=false,
active_p=proplists:get_value(active_mode, MgrOpts, true),
name=MyName,
proj=NoneProj,
proj_history=queue:new(),
myflu=MyFLUPid, % pid or atom local name
myflu=MyName,
members_dict=MembersDict,
%% TODO 2015-03-04: revisit, should this constant be bigger?
%% Yes, this should be bigger, but it's a hack. There is
%% no guarantee that all parties will advance to a minimum
@ -170,6 +172,10 @@ init({MyName, All_list, MyFLUPid, MgrOpts}) ->
self() ! {finish_init, BestProj},
{ok, S}.
handle_call({ping}, _From, S) ->
{reply, pong, S};
handle_call({stop}, _From, S) ->
{stop, normal, ok, S};
handle_call(_Call, _From, #ch_mgr{init_finished=false} = S) ->
{reply, not_initialized, S};
handle_call({test_write_proposed_projection}, _From, S) ->
@ -179,10 +185,6 @@ handle_call({test_write_proposed_projection}, _From, S) ->
{Res, S2} = do_cl_write_proposed_proj(S),
{reply, Res, S2}
end;
handle_call({ping}, _From, S) ->
{reply, pong, S};
handle_call({stop}, _From, S) ->
{stop, normal, ok, S};
handle_call({test_calc_projection, KeepRunenvP}, _From,
#ch_mgr{name=MyName}=S) ->
RelativeToServer = MyName,
@ -226,10 +228,12 @@ code_change(_OldVsn, S, _Extra) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
finish_init(BestProj, #ch_mgr{active_p=false}=S) ->
_ = erlang:send_after(1000, self(), {finish_init, BestProj}),
S;
finish_init(BestProj, #ch_mgr{init_finished=false, myflu=MyFLU} = S) ->
case ?FLU_PC:read_latest_projection(MyFLU, private) of
{error, not_written} ->
Epoch = BestProj#projection_v1.epoch_number,
case ?FLU_PC:write_projection(MyFLU, private, BestProj) of
ok ->
S#ch_mgr{init_finished=true, proj=BestProj};

View file

@ -29,44 +29,36 @@
update_projection_checksum/1,
update_projection_dbg2/2,
compare/2,
make_projection_summary/1
make_projection_summary/1,
make_members_dict/1
]).
%% @doc Create a new projection record.
new(MyName, All_list, UPI_list, Down_list, Repairing_list, Ps) ->
new(0, MyName, All_list, Down_list, UPI_list, Repairing_list, Ps).
new(MyName, MemberDict, UPI_list, Down_list, Repairing_list, Ps) ->
new(0, MyName, MemberDict, Down_list, UPI_list, Repairing_list, Ps).
%% @doc Create a new projection record.
new(EpochNum, MyName, All_list, Down_list, UPI_list, Repairing_list, Dbg) ->
new(EpochNum, MyName, All_list, Down_list, UPI_list, Repairing_list,
new(EpochNum, MyName, MemberDict, Down_list, UPI_list, Repairing_list, Dbg) ->
new(EpochNum, MyName, MemberDict, Down_list, UPI_list, Repairing_list,
Dbg, []).
%% @doc Create a new projection record.
%%
%% The `MemberDict0' argument may be a true `p_srvr_dict()' (i.e, it
%% is a well-formed `orddict' with the correct 2-tuple key-value form)
%% or it may be simply `list(p_srvr())', in which case we'll convert it
%% to a `p_srvr_dict()'.
new(EpochNum, MyName, All_list0, Down_list, UPI_list, Repairing_list,
new(EpochNum, MyName, MemberDict0, Down_list, UPI_list, Repairing_list,
Dbg, Dbg2)
when is_integer(EpochNum), EpochNum >= 0,
is_atom(MyName) orelse is_binary(MyName),
is_list(All_list0), is_list(Down_list), is_list(UPI_list),
is_list(MemberDict0), is_list(Down_list), is_list(UPI_list),
is_list(Repairing_list), is_list(Dbg), is_list(Dbg2) ->
{All_list, MemberDict} =
case lists:all(fun(P) when is_record(P, p_srvr) -> true;
(_) -> false
end, All_list0) of
true ->
All = [S#p_srvr.name || S <- All_list0],
TmpL = [{S#p_srvr.name, S} || S <- All_list0],
{All, orddict:from_list(TmpL)};
false ->
All_list1 = lists:zip(All_list0,lists:seq(0,length(All_list0)-1)),
All_list2 = [#p_srvr{name=S, address="localhost",
port=?MACHI_DEFAULT_TCP_PORT+I} ||
{S, I} <- All_list1],
TmpL = [{S#p_srvr.name, S} || S <- All_list2],
{All_list0, orddict:from_list(TmpL)}
end,
MembersDict = make_members_dict(MemberDict0),
All_list = [Name || {Name, _P} <- MembersDict],
true = lists:all(fun(X) when is_atom(X) orelse is_binary(X) -> true;
(_) -> false
end, All_list),
@ -87,7 +79,7 @@ new(EpochNum, MyName, All_list0, Down_list, UPI_list, Repairing_list,
creation_time=now(),
author_server=MyName,
all_members=All_list,
member_dict=MemberDict,
members_dict=MembersDict,
down=Down_list,
upi=UPI_list,
repairing=Repairing_list,
@ -134,3 +126,31 @@ make_projection_summary(#projection_v1{epoch_number=EpochNum,
[{epoch,EpochNum},{author,Author},
{upi,UPI_list},{repair,Repairing_list},{down,Down_list},
{d,Dbg}, {d2,Dbg2}].
%% @doc Make a `p_srvr_dict()' out of a list of `p_srvr()' or out of a
%% `p_srvr_dict()'.
%%
%% If `Ps' is a `p_srvr_dict()', then this function is usually a
%% no-op. However, if someone has tampered with the list and screwed
%% up its order, then we should fix it so `orddict' can work
%% correctly.
%%
%% If `Ps' is simply `list(p_srvr())', in which case we'll convert it
%% to a `p_srvr_dict()'.
-spec make_members_dict(list(p_srvr()) | p_srvr_dict()) ->
p_srvr_dict().
make_members_dict(Ps) ->
case lists:all(fun(P) when is_record(P, p_srvr) -> true;
(_) -> false
end, Ps) of
true ->
orddict:from_list([{P#p_srvr.name, P} || P <- Ps]);
false ->
case lists:all(fun({_K, P}) when is_record(P, p_srvr) -> true;
(_) -> false
end, Ps) of
true ->
orddict:from_list(Ps)
end % No false clause, crash it!
end.

View file

@ -307,104 +307,3 @@ disconnect(#state{sock=Sock,
S#state{sock=undefined};
disconnect(S) ->
S.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ifdef(TEST).
dummy_server(Parent, TcpPort) ->
spawn_link(fun() ->
{ok, LSock} = gen_tcp:listen(TcpPort,
[{reuseaddr,true},
{packet, line},
{mode, binary},
{active, false}]),
dummy_ack(Parent),
{ok, Sock} = gen_tcp:accept(LSock),
ok = inet:setopts(Sock, [{packet, line}]),
{ok, _Line} = gen_tcp:recv(Sock, 0),
ok = gen_tcp:send(Sock, "ERROR BADARG\n"),
(catch gen_tcp:close(Sock)),
unlink(Parent),
exit(normal)
end).
dummy_ack(Parent) ->
Parent ! go.
dummy_wait_for_ack() ->
receive go -> ok end.
smoke_test() ->
TcpPort = 57123,
Me = self(),
_ServerPid = dummy_server(Me, TcpPort),
dummy_wait_for_ack(),
I = #p_srvr{name=smoke, proto=ipv4, address="localhost", port=TcpPort},
S0 = #state{i=I},
false = connected_p(S0),
S1 = try_connect(S0),
true = connected_p(S1),
gen_tcp:send(S1#state.sock, "yo dawg\n"),
{ok, _Answer} = gen_tcp:recv(S1#state.sock, 0),
_S2 = disconnect(S1),
ok.
api_smoke_test() ->
RegName = api_smoke_flu,
Host = "localhost",
TcpPort = 57124,
DataDir = "./data.api_smoke_flu",
FLU1 = machi_flu1_test:setup_test_flu(RegName, TcpPort, DataDir),
erase(flu_pid),
try
I = #p_srvr{name=RegName, proto=ipv4, address=Host, port=TcpPort},
{ok, Prox1} = start_link(I),
try
FakeEpoch = {-1, <<0:(20*8)/big>>},
[{ok, {_,_,_}} = append_chunk(Prox1,
FakeEpoch, <<"prefix">>, <<"data">>,
infinity) || _ <- lists:seq(1,5)],
%% Stop the FLU, what happens?
machi_flu1:stop(FLU1),
{error,_} = append_chunk(Prox1,
FakeEpoch, <<"prefix">>, <<"data">>,
infinity),
{error,not_connected} = append_chunk(Prox1,
FakeEpoch, <<"prefix">>, <<"data">>,
infinity),
%% Start the FLU again, we should be able to do stuff immediately
FLU1b = machi_flu1_test:setup_test_flu(RegName, TcpPort, DataDir,
[save_data_dir]),
put(flu_pid, FLU1b),
MyChunk = <<"my chunk data">>,
{ok, {MyOff,MySize,MyFile}} =
append_chunk(Prox1, FakeEpoch, <<"prefix">>, MyChunk,
infinity),
{ok, MyChunk} = read_chunk(Prox1, FakeEpoch, MyFile, MyOff, MySize),
%% Alright, now for the rest of the API, whee
BadFile = <<"no-such-file">>,
{error, no_such_file} = checksum_list(Prox1, FakeEpoch, BadFile),
{ok, [_]} = list_files(Prox1, FakeEpoch),
{ok, FakeEpoch} = get_latest_epoch(Prox1, public),
{error, not_written} = read_latest_projection(Prox1, public),
{error, not_written} = read_projection(Prox1, public, 44),
P1 = machi_projection:new(1, a, [a], [], [a], [], []),
ok = write_projection(Prox1, public, P1),
{ok, P1} = read_projection(Prox1, public, 1),
{ok, [P1]} = get_all_projections(Prox1, public),
{ok, [1]} = list_all_projections(Prox1, public),
ok
after
_ = (catch quit(Prox1))
end
after
(catch machi_flu1:stop(FLU1)),
(catch machi_flu1:stop(get(flu_pid)))
end.
-endif. % TEST

View file

@ -135,15 +135,27 @@ chain_to_projection(MyName, Epoch, UPI_list, Repairing_list, All_list) ->
-ifndef(PULSE).
smoke0_test() ->
%% TODO attack list:
%% 0. Add start option to chain manager to be "passive" only, i.e.,
%% not immediately go to work on
%% 1. Start FLUs with full complement of FLU+proj+chmgr.
%% 2. Put each of them under a supervisor?
%% - Sup proc could be a created-specifically-for-test thing, perhaps?
%% Rather than relying on a supervisor with reg name + OTP app started
%% plus plus more more yaddayadda?
%% 3. Add projection catalog/orddict of #p_srvr records??
%% 4. Backport the changes to smoke0_test().
%% 5. Do it to smoke1 test, yadda...
{ok, _} = machi_partition_simulator:start_link({1,2,3}, 50, 50),
Host = "localhost",
TcpPort = 6623,
{ok, FLUa} = machi_flu1:start_link([{a,TcpPort,"./data.a"}]),
Pa = #p_srvr{name=a, proto=ipv4, address=Host, port=TcpPort},
P_Srvr_Dict = machi_projection:make_members_dict([Pa]),
%% Egadz, more racing on startup, yay. TODO fix.
timer:sleep(1),
{ok, FLUaP} = ?FLU_PC:start_link(Pa),
{ok, M0} = ?MGR:start_link(a, [a,b,c], FLUaP),
{ok, M0} = ?MGR:start_link(a, [a,b,c], P_Srvr_Dict, [{active_mode, false}]),
_SockA = machi_util:connect(Host, TcpPort),
try
pong = ?MGR:ping(M0)

View file

@ -136,7 +136,8 @@ flu_projection_smoke_test() ->
{ok, []} = ?FLU_C:list_all_projections(Host, TcpPort, T),
{ok, []} = ?FLU_C:get_all_projections(Host, TcpPort, T),
P1 = machi_projection:new(1, a, [a], [], [a], [], []),
P_a = #p_srvr{name=a},
P1 = machi_projection:new(1, a, [P_a], [], [a], [], []),
ok = ?FLU_C:write_projection(Host, TcpPort, T, P1),
{error, written} = ?FLU_C:write_projection(Host, TcpPort, T, P1),
{ok, P1} = ?FLU_C:read_projection(Host, TcpPort, T, 1),

View file

@ -25,36 +25,45 @@
-include("machi_projection.hrl").
new_fake(Name) ->
#p_srvr{name=Name}.
%% Bleh, hey QuickCheck ... except that any model probably equals
%% code under test, bleh.
new_test() ->
%% Bleh, hey QuickCheck ... except that any model probably equals
%% code under test, bleh.
true = try_it(a, [a,b,c], [a,b], [], [c], []),
true = try_it(<<"a">>, [<<"a">>,b,c], [<<"a">>,b], [], [c], []),
Servers = [#p_srvr{name=a}, #p_srvr{name=b}, #p_srvr{name=c}],
Servers_bad1 = [#p_srvr{name= <<"a">>}, #p_srvr{name=b}, #p_srvr{name=c}],
Servers_bad2 = [#p_srvr{name=z}, #p_srvr{name=b}, #p_srvr{name=c}],
All0 = [new_fake(X) || X <- [a,b,c]],
All_binA = [new_fake(<<"a">>)] ++ [new_fake(X) || X <- [b,c]],
true = try_it(a, All0, [a,b], [], [c], []),
true = try_it(<<"a">>, All_binA, [<<"a">>,b], [], [c], []),
Servers = All0,
Servers_bad1 = [new_fake(X) || X <- [<<"a">>,b,c]],
Servers_bad2 = [new_fake(X) || X <- [z,b,c]],
true = try_it(a, Servers, [a,b], [], [c], []),
false = try_it(a, not_list, [a,b], [], [c], []),
false = try_it(a, [a,b,c], not_list, [], [c], []),
false = try_it(a, [a,b,c], [a,b], not_list, [c], []),
false = try_it(a, [a,b,c], [a,b], [], not_list, []),
false = try_it(a, [a,b,c], [a,b], [], [c], not_list),
false = try_it(a, All0, not_list, [], [c], []),
false = try_it(a, All0, [a,b], not_list, [c], []),
false = try_it(a, All0, [a,b], [], not_list, []),
false = try_it(a, All0, [a,b], [], [c], not_list),
false = try_it(<<"x">>, [a,b,c], [a,b], [], [c], []),
false = try_it(a, [a,b,c], [a,b,c], [], [c], []),
false = try_it(a, [a,b,c], [a,b], [c], [c], []),
false = try_it(a, [a,b,c], [a,b], [], [c,c], []),
false = try_it(<<"x">>, All0, [a,b], [], [c], []),
false = try_it(a, All0, [a,b,c], [], [c], []),
false = try_it(a, All0, [a,b], [c], [c], []),
false = try_it(a, All0, [a,b], [], [c,c], []),
false = try_it(a, Servers_bad1, [a,b], [], [c], []),
false = try_it(a, Servers_bad2, [a,b], [], [c], []),
ok.
compare_test() ->
P0 = machi_projection:new(0, a, [a,b,c], [a,b], [], [c], []),
P1a = machi_projection:new(1, a, [a,b,c], [a,b], [], [c], []),
P1b = machi_projection:new(1, b, [a,b,c], [a,b], [], [c], []),
P2 = machi_projection:new(2, a, [a,b,c], [a,b], [], [c], []),
All0 = [new_fake(X) || X <- [a,b,c]],
P0 = machi_projection:new(0, a, All0, [a,b], [], [c], []),
P1a = machi_projection:new(1, a, All0, [a,b], [], [c], []),
P1b = machi_projection:new(1, b, All0, [a,b], [], [c], []),
P2 = machi_projection:new(2, a, All0, [a,b], [], [c], []),
0 = machi_projection:compare(P0, P0),
-1 = machi_projection:compare(P0, P1a),

View file

@ -0,0 +1,125 @@
%% -------------------------------------------------------------------
%%
%% Copyright (c) 2007-2015 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
-module(machi_proxy_flu1_client_test).
-include("machi_projection.hrl").
-define(MUT, machi_proxy_flu1_client).
-ifdef(TEST).
%% dummy_server(Parent, TcpPort) ->
%% spawn_link(fun() ->
%% {ok, LSock} = gen_tcp:listen(TcpPort,
%% [{reuseaddr,true},
%% {packet, line},
%% {mode, binary},
%% {active, false}]),
%% dummy_ack(Parent),
%% {ok, Sock} = gen_tcp:accept(LSock),
%% ok = inet:setopts(Sock, [{packet, line}]),
%% {ok, _Line} = gen_tcp:recv(Sock, 0),
%% ok = gen_tcp:send(Sock, "ERROR BADARG\n"),
%% (catch gen_tcp:close(Sock)),
%% unlink(Parent),
%% exit(normal)
%% end).
%% dummy_ack(Parent) ->
%% Parent ! go.
%% dummy_wait_for_ack() ->
%% receive go -> ok end.
%% smoke_test() ->
%% TcpPort = 57123,
%% Me = self(),
%% _ServerPid = dummy_server(Me, TcpPort),
%% dummy_wait_for_ack(),
%% I = #p_srvr{name=smoke, proto=ipv4, address="localhost", port=TcpPort},
%% S0 = #state{i=I},
%% false = connected_p(S0),
%% S1 = try_connect(S0),
%% true = connected_p(S1),
%% gen_tcp:send(S1#state.sock, "yo dawg\n"),
%% {ok, _Answer} = gen_tcp:recv(S1#state.sock, 0),
%% _S2 = disconnect(S1),
%% ok.
api_smoke_test() ->
RegName = api_smoke_flu,
Host = "localhost",
TcpPort = 57124,
DataDir = "./data.api_smoke_flu",
FLU1 = machi_flu1_test:setup_test_flu(RegName, TcpPort, DataDir),
erase(flu_pid),
try
I = #p_srvr{name=RegName, proto=ipv4, address=Host, port=TcpPort},
{ok, Prox1} = ?MUT:start_link(I),
try
FakeEpoch = {-1, <<0:(20*8)/big>>},
[{ok, {_,_,_}} = ?MUT:append_chunk(Prox1,
FakeEpoch, <<"prefix">>, <<"data">>,
infinity) || _ <- lists:seq(1,5)],
%% Stop the FLU, what happens?
machi_flu1:stop(FLU1),
{error,_} = ?MUT:append_chunk(Prox1,
FakeEpoch, <<"prefix">>, <<"data">>,
infinity),
{error,not_connected} = ?MUT:append_chunk(Prox1,
FakeEpoch, <<"prefix">>, <<"data">>,
infinity),
%% Start the FLU again, we should be able to do stuff immediately
FLU1b = machi_flu1_test:setup_test_flu(RegName, TcpPort, DataDir,
[save_data_dir]),
put(flu_pid, FLU1b),
MyChunk = <<"my chunk data">>,
{ok, {MyOff,MySize,MyFile}} =
?MUT:append_chunk(Prox1, FakeEpoch, <<"prefix">>, MyChunk,
infinity),
{ok, MyChunk} = ?MUT:read_chunk(Prox1, FakeEpoch, MyFile, MyOff, MySize),
%% Alright, now for the rest of the API, whee
BadFile = <<"no-such-file">>,
{error, no_such_file} = ?MUT:checksum_list(Prox1, FakeEpoch, BadFile),
{ok, [_]} = ?MUT:list_files(Prox1, FakeEpoch),
{ok, FakeEpoch} = ?MUT:get_latest_epoch(Prox1, public),
{error, not_written} = ?MUT:read_latest_projection(Prox1, public),
{error, not_written} = ?MUT:read_projection(Prox1, public, 44),
P_a = #p_srvr{name=a, address="localhost", port=6622},
P1 = machi_projection:new(1, a, [P_a], [], [a], [], []),
ok = ?MUT:write_projection(Prox1, public, P1),
{ok, P1} = ?MUT:read_projection(Prox1, public, 1),
{ok, [P1]} = ?MUT:get_all_projections(Prox1, public),
{ok, [1]} = ?MUT:list_all_projections(Prox1, public),
ok
after
_ = (catch ?MUT:quit(Prox1))
end
after
(catch machi_flu1:stop(FLU1)),
(catch machi_flu1:stop(get(flu_pid)))
end.
-endif. % TEST