blob: cc2c0cc1a50d51856d5af0bd0a67986bcef58972 [file]
% Licensed 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(rexi_tests).
-export([
rpc_test_fun/1
]).
-include_lib("couch/include/couch_eunit.hrl").
rexi_buffer_test_() ->
{
foreach,
fun setup/0,
fun teardown/1,
[
?TDEF_FE(t_server_pid),
?TDEF_FE(t_cast),
?TDEF_FE(t_cast_explicit_caller),
?TDEF_FE(t_cast_ref),
?TDEF_FE(t_sync_cast),
?TDEF_FE(t_stream2),
?TDEF_FE(t_stream2_acks),
?TDEF_FE(t_stream2_cancel),
?TDEF_FE(t_kill),
?TDEF_FE(t_cast_error),
?TDEF_FE(t_metrics),
?TDEF_FE(t_ping)
]
}.
setup() ->
test_util:start_couch([rexi]).
teardown(Ctx) ->
test_util:stop_couch(Ctx).
rpc_test_fun({sleep, MSec}) ->
rexi:reply({sleeping, self()}),
timer:sleep(MSec);
rpc_test_fun({error, Error}) ->
error(Error);
rpc_test_fun(ping) ->
rexi:ping();
rpc_test_fun(stream2_init) ->
rexi:stream2(stream1),
rexi:stream_last(stream2);
rpc_test_fun(stream2_acks) ->
rexi:stream2(a),
rexi:stream2(b),
rexi:stream2(c),
rexi:stream2(d),
rexi:stream2(e),
rexi:stream2(f),
rexi:stream2(g),
rexi:stream_last(h);
rpc_test_fun(Arg) ->
rexi:reply({Arg, get()}).
t_server_pid(_) ->
?assertMatch({RexiServer, node42} when is_atom(RexiServer), rexi_utils:server_pid(node42)).
t_cast(_) ->
put(nonce, yup),
Ref = rexi:cast(node(), {?MODULE, rpc_test_fun, [potato]}),
{Res, Dict} =
receive
{Ref, {R, D}} -> {R, maps:from_list(D)}
end,
?assertEqual(potato, Res),
?assertMatch(
#{
nonce := yup,
'$initial_call' := {?MODULE, rpc_test_fun, 1},
rexi_from := {_Pid, _Ref}
},
Dict
).
t_cast_explicit_caller(_) ->
put(nonce, yep),
{CallerPid, CallerRef} = spawn_monitor(fun() ->
receive
Msg -> exit(Msg)
end
end),
Ref = rexi:cast(node(), CallerPid, {?MODULE, rpc_test_fun, [potato]}),
Result =
receive
{'DOWN', CallerRef, _, _, Exit} -> Exit
end,
?assertMatch({Ref, {potato, [_ | _]}}, Result).
t_cast_ref(_) ->
put(nonce, yesh),
Ref = make_ref(),
Ref2 = rexi:cast_ref(Ref, node(), {?MODULE, rpc_test_fun, [potato]}),
?assertEqual(Ref, Ref2),
{Res, Dict} = recv(Ref),
?assertEqual(potato, Res),
?assertMatch(
#{
nonce := yesh,
'$initial_call' := {?MODULE, rpc_test_fun, 1},
rexi_from := {_Pid, Ref}
},
maps:from_list(Dict)
).
t_sync_cast(_) ->
put(nonce, yup),
Ref = rexi:cast(node(), self(), {?MODULE, rpc_test_fun, [potato]}, [sync]),
{Res, Dict} = recv(Ref),
?assertEqual(potato, Res),
?assertMatch(
#{
nonce := yup,
'$initial_call' := {?MODULE, rpc_test_fun, 1},
rexi_from := {_Pid, _Ref}
},
maps:from_list(Dict)
).
t_stream2(_) ->
% We act as the coordinator
Ref = rexi:cast(node(), {?MODULE, rpc_test_fun, [stream2_init]}),
rexi:stream_start(stream_init(Ref)),
?assertEqual(stream1, recv(Ref)),
?assertEqual(stream2, recv(Ref)),
% No more messages
?assertEqual(timeout, recv(Ref)).
t_stream2_acks(_) ->
Ref = rexi:cast(node(), {?MODULE, rpc_test_fun, [stream2_acks]}),
{WPid, _Tag} = From = stream_init(Ref),
Mon = monitor(process, WPid),
rexi:stream_start(From),
?assertEqual(a, recv(Ref)),
?assertEqual(b, recv(Ref)),
?assertEqual(c, recv(Ref)),
?assertEqual(d, recv(Ref)),
?assertEqual(e, recv(Ref)),
?assertEqual(timeout, recv(Ref)),
rexi:stream_ack(WPid),
?assertEqual(f, recv(Ref)),
?assertEqual(timeout, recv(Ref)),
rexi:stream_ack(WPid),
?assertEqual(g, recv(Ref)),
?assertEqual(h, recv(Ref)),
% Done streaming. Ensure worker is dead.
?assertEqual(timeout, recv(Ref)),
Res =
receive
{'DOWN', Mon, _, _, Exit} -> Exit
end,
?assertEqual(normal, Res).
t_stream2_cancel(_) ->
Ref = rexi:cast(node(), {?MODULE, rpc_test_fun, [stream2_init]}),
{WPid, _Tag} = From = stream_init(Ref),
Mon = monitor(process, WPid),
rexi:stream_cancel(From),
Res =
receive
{'DOWN', Mon, _, _, Exit} -> Exit
end,
?assertEqual(normal, Res).
t_cast_error(_) ->
Ref = rexi:cast(node(), self(), {?MODULE, rpc_test_fun, [{error, tomato}]}, []),
Res =
receive
{Ref, RexiExit} -> RexiExit
end,
?assertMatch({rexi_EXIT, {tomato, [{?MODULE, rpc_test_fun, 1, _} | _]}}, Res).
t_kill(_) ->
Ref = rexi:cast(node(), {?MODULE, rpc_test_fun, [{sleep, 10000}]}),
WorkerPid =
receive
{Ref, {sleeping, Pid}} -> Pid
end,
?assert(is_process_alive(WorkerPid)),
Mon = monitor(process, WorkerPid),
rexi:kill_all([{node(), Ref}]),
KillReason =
receive
{'DOWN', Mon, _, _, Res} -> Res
end,
?assertEqual(killed, KillReason).
t_metrics(_) ->
?assertEqual(0, rexi:aggregate_buffer_queue_len()),
?assertEqual(0, rexi:aggregate_server_queue_len()).
t_ping(_) ->
rexi:cast(node(), {?MODULE, rpc_test_fun, [ping]}),
Res =
receive
{rexi, Ping} -> Ping
end,
?assertEqual('$rexi_ping', Res).
stream_init(Ref) ->
receive
{Ref, From, rexi_STREAM_INIT} ->
From
end.
recv(Ref) when is_reference(Ref) ->
receive
{Ref, _, Msg} -> Msg;
{Ref, Msg} -> Msg
after 500 -> timeout
end.