mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
179 lines
5.3 KiB
Erlang
179 lines
5.3 KiB
Erlang
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-module(process_registry_integration_tests).
|
|
-typing([eqwalizer]).
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-define(REGISTRY_TABLE, process_registry_table).
|
|
|
|
integration_full_lifecycle_test() ->
|
|
process_registry:init(),
|
|
Id = 12345,
|
|
Key = process_registry:build_process_key(guild, Id),
|
|
?assertEqual({guild, 12345}, Key),
|
|
|
|
Pid = spawn_sleep(200),
|
|
{_Ref, Map1} = register_and_assert_count(Key, Pid, 1, #{}),
|
|
|
|
Map2 = maps:put(guild_67890, loading, Map1),
|
|
?assertEqual(1, process_registry:get_count(Map2)),
|
|
|
|
OtherKey = process_registry:build_process_key(channel, 67890),
|
|
OtherPid = spawn_sleep(200),
|
|
ets:insert(?REGISTRY_TABLE, {OtherKey, OtherPid}),
|
|
{ok, OtherPid, _OtherRef, Map3} = process_registry:lookup_or_monitor(
|
|
OtherKey, channel_67890, Map2
|
|
),
|
|
?assertEqual(2, process_registry:get_count(Map3)),
|
|
|
|
Map4 = process_registry:cleanup_on_down(Pid, Map3),
|
|
?assertEqual(1, process_registry:get_count(Map4)),
|
|
?assertEqual(loading, maps:get(guild_67890, Map4)),
|
|
|
|
process_registry:safe_unregister(Key),
|
|
process_registry:safe_unregister(OtherKey),
|
|
|
|
?assertEqual(undefined, process_registry:registry_whereis(Key)),
|
|
?assertEqual(undefined, process_registry:registry_whereis(OtherKey)).
|
|
|
|
integration_process_death_test_() ->
|
|
{timeout, 10, fun() ->
|
|
process_registry:init(),
|
|
Key = {test_integration, 5001},
|
|
|
|
Pid = spawn(fun death_probe_loop/0),
|
|
|
|
{Ref, Map} = register_and_assert_count(Key, Pid, 1, #{}),
|
|
|
|
Pid ! die,
|
|
|
|
receive
|
|
{'DOWN', Ref, process, Pid, _Reason} ->
|
|
Map2 = process_registry:cleanup_on_down(Pid, Map),
|
|
?assertEqual(0, process_registry:get_count(Map2)),
|
|
process_registry:safe_unregister(Key)
|
|
after 500 ->
|
|
?assert(false)
|
|
end
|
|
end}.
|
|
|
|
integration_race_conditions_test_() ->
|
|
{timeout, 10, fun() ->
|
|
process_registry:init(),
|
|
Key = {test_integration, 5002},
|
|
|
|
Parent = self(),
|
|
|
|
FirstPid = spawn_sleep(300),
|
|
{_FirstRef, _Map1} = register_and_assert_count(Key, FirstPid, 1, #{}),
|
|
|
|
Workers = [spawn_register_worker(Parent, Key) || _ <- lists:seq(1, 3)],
|
|
|
|
Results = [
|
|
receive
|
|
{register_result, R} -> R
|
|
after 1000 -> timeout
|
|
end
|
|
|| _ <- Workers
|
|
],
|
|
AllGotFirstPid = lists:all(
|
|
fun
|
|
({ok, P, _, _}) -> P =:= FirstPid;
|
|
(_) -> false
|
|
end,
|
|
Results
|
|
),
|
|
?assert(AllGotFirstPid),
|
|
|
|
process_registry:safe_unregister(Key)
|
|
end}.
|
|
|
|
integration_rapid_cycles_test_() ->
|
|
{timeout, 10, fun() ->
|
|
process_registry:init(),
|
|
lists:foreach(fun rapid_cycle/1, lists:seq(1, 10))
|
|
end}.
|
|
|
|
force_stop_process_normal_test_() ->
|
|
{timeout, 15, fun() ->
|
|
Pid = spawn(fun wait_for_stop_loop/0),
|
|
?assert(process_liveness:is_alive(Pid)),
|
|
process_registry:force_stop_process(Pid),
|
|
timer:sleep(50),
|
|
?assertEqual(false, process_liveness:is_alive(Pid))
|
|
end}.
|
|
|
|
force_stop_process_already_dead_test() ->
|
|
Pid = spawn(fun() -> ok end),
|
|
timer:sleep(10),
|
|
?assertEqual(false, process_liveness:is_alive(Pid)),
|
|
process_registry:force_stop_process(Pid).
|
|
|
|
force_stop_process_kills_unresponsive_test_() ->
|
|
{timeout, 15, fun() ->
|
|
Pid = spawn(fun trapped_wait_loop/0),
|
|
?assert(process_liveness:is_alive(Pid)),
|
|
process_registry:force_stop_process(Pid),
|
|
timer:sleep(100),
|
|
?assertEqual(false, process_liveness:is_alive(Pid))
|
|
end}.
|
|
|
|
register_and_monitor_duplicate_stops_loser_test_() ->
|
|
{timeout, 15, fun() ->
|
|
process_registry:init(),
|
|
Key = {test_reg, 1007},
|
|
WinnerPid = spawn_sleep(5000),
|
|
ets:insert(?REGISTRY_TABLE, {Key, WinnerPid}),
|
|
LoserPid = spawn(fun trapped_wait_loop/0),
|
|
?assert(process_liveness:is_alive(LoserPid)),
|
|
Result = process_registry:register_and_monitor(Key, LoserPid, #{}),
|
|
?assertMatch({ok, WinnerPid, _, _}, Result),
|
|
timer:sleep(100),
|
|
?assertEqual(false, process_liveness:is_alive(LoserPid)),
|
|
process_registry:registry_unregister(Key)
|
|
end}.
|
|
|
|
spawn_sleep(Milliseconds) ->
|
|
spawn(fun() -> timer:sleep(Milliseconds) end).
|
|
|
|
death_probe_loop() ->
|
|
receive
|
|
die -> exit(normal)
|
|
after 100 ->
|
|
exit(normal)
|
|
end.
|
|
|
|
wait_for_stop_loop() ->
|
|
receive
|
|
stop -> ok
|
|
after infinity ->
|
|
ok
|
|
end.
|
|
|
|
trapped_wait_loop() ->
|
|
process_flag(trap_exit, true),
|
|
receive
|
|
never_arrives -> ok
|
|
after infinity ->
|
|
ok
|
|
end.
|
|
|
|
spawn_register_worker(Parent, Key) ->
|
|
spawn(fun() ->
|
|
NewPid = spawn_sleep(100),
|
|
Result = process_registry:register_and_monitor(Key, NewPid, #{}),
|
|
Parent ! {register_result, Result}
|
|
end).
|
|
|
|
rapid_cycle(N) ->
|
|
Key = {test_rapid, N},
|
|
Pid = spawn_sleep(50),
|
|
{_Ref, _Map} = register_and_assert_count(Key, Pid, 1, #{}),
|
|
process_registry:safe_unregister(Key),
|
|
?assertEqual(undefined, process_registry:registry_whereis(Key)).
|
|
|
|
register_and_assert_count(Key, Pid, Count, ProcessMap) ->
|
|
{ok, Pid, Ref, Map} = process_registry:register_and_monitor(Key, Pid, ProcessMap),
|
|
?assertEqual(Count, process_registry:get_count(Map)),
|
|
{Ref, Map}.
|