Files
n8n-openai-adapter/lib/elixir/test/elixir/dynamic_supervisor_test.exs
T

779 lines
29 KiB
Elixir

Code.require_file("test_helper.exs", __DIR__)
defmodule DynamicSupervisorTest do
use ExUnit.Case, async: true
defmodule Simple do
use DynamicSupervisor
def init(args), do: args
end
test "can be supervised directly" do
children = [{DynamicSupervisor, name: :dyn_sup_spec_test}]
assert {:ok, _} = Supervisor.start_link(children, strategy: :one_for_one)
assert DynamicSupervisor.which_children(:dyn_sup_spec_test) == []
end
test "multiple supervisors can be supervised and identified with simple child spec" do
{:ok, _} = Registry.start_link(keys: :unique, name: DynSup.Registry)
children = [
{DynamicSupervisor, name: :simple_name},
{DynamicSupervisor, name: {:global, :global_name}},
{DynamicSupervisor, name: {:via, Registry, {DynSup.Registry, "via_name"}}}
]
assert {:ok, supsup} = Supervisor.start_link(children, strategy: :one_for_one)
assert {:ok, no_name_dynsup} =
Supervisor.start_child(supsup, {DynamicSupervisor, strategy: :one_for_one})
assert DynamicSupervisor.which_children(:simple_name) == []
assert DynamicSupervisor.which_children({:global, :global_name}) == []
assert DynamicSupervisor.which_children({:via, Registry, {DynSup.Registry, "via_name"}}) == []
assert DynamicSupervisor.which_children(no_name_dynsup) == []
assert Supervisor.start_child(supsup, {DynamicSupervisor, strategy: :one_for_one}) ==
{:error, {:already_started, no_name_dynsup}}
end
describe "use/2" do
test "generates child_spec/1" do
assert Simple.child_spec([:hello]) == %{
id: Simple,
start: {Simple, :start_link, [[:hello]]},
type: :supervisor
}
defmodule Custom do
use DynamicSupervisor,
id: :id,
restart: :temporary,
shutdown: :infinity,
start: {:foo, :bar, []}
def init(arg), do: {:producer, arg}
end
assert Custom.child_spec([:hello]) == %{
id: :id,
restart: :temporary,
shutdown: :infinity,
start: {:foo, :bar, []},
type: :supervisor
}
end
end
describe "init/1" do
test "set default options" do
assert DynamicSupervisor.init([]) ==
{:ok,
%{
strategy: :one_for_one,
intensity: 3,
period: 5,
max_children: :infinity,
extra_arguments: []
}}
end
end
describe "start_link/3" do
test "with non-ok init" do
Process.flag(:trap_exit, true)
assert DynamicSupervisor.start_link(Simple, {:ok, %{strategy: :unknown}}) ==
{:error, {:supervisor_data, {:invalid_strategy, :unknown}}}
assert DynamicSupervisor.start_link(Simple, {:ok, %{intensity: -1}}) ==
{:error, {:supervisor_data, {:invalid_intensity, -1}}}
assert DynamicSupervisor.start_link(Simple, {:ok, %{period: 0}}) ==
{:error, {:supervisor_data, {:invalid_period, 0}}}
assert DynamicSupervisor.start_link(Simple, {:ok, %{max_children: -1}}) ==
{:error, {:supervisor_data, {:invalid_max_children, -1}}}
assert DynamicSupervisor.start_link(Simple, {:ok, %{extra_arguments: -1}}) ==
{:error, {:supervisor_data, {:invalid_extra_arguments, -1}}}
assert DynamicSupervisor.start_link(Simple, {:ok, %{auto_shutdown: :any_significant}}) ==
{:error, {:supervisor_data, {:invalid_auto_shutdown, :any_significant}}}
assert DynamicSupervisor.start_link(Simple, :unknown) ==
{:error, {:bad_return, {Simple, :init, :unknown}}}
assert DynamicSupervisor.start_link(Simple, :ignore) == :ignore
end
test "with registered process" do
{:ok, pid} = DynamicSupervisor.start_link(Simple, {:ok, %{}}, name: __MODULE__)
# Sets up a link
{:links, links} = Process.info(self(), :links)
assert pid in links
# A name
assert Process.whereis(__MODULE__) == pid
# And the initial call
assert {:supervisor, DynamicSupervisorTest.Simple, 1} =
:proc_lib.translate_initial_call(pid)
# And shuts down
assert DynamicSupervisor.stop(__MODULE__) == :ok
end
test "with spawn_opt" do
{:ok, pid} =
DynamicSupervisor.start_link(strategy: :one_for_one, spawn_opt: [priority: :high])
assert Process.info(pid, :priority) == {:priority, :high}
end
test "sets initial call to the same as a regular supervisor" do
{:ok, pid} = Supervisor.start_link([], strategy: :one_for_one)
assert :proc_lib.initial_call(pid) == {:supervisor, Supervisor.Default, [:Argument__1]}
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert :proc_lib.initial_call(pid) == {:supervisor, Supervisor.Default, [:Argument__1]}
end
test "returns the callback module" do
{:ok, pid} = Supervisor.start_link([], strategy: :one_for_one)
assert :supervisor.get_callback_module(pid) == Supervisor.Default
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert :supervisor.get_callback_module(pid) == Supervisor.Default
end
end
## Code change
describe "code_change/3" do
test "with non-ok init" do
{:ok, pid} = DynamicSupervisor.start_link(Simple, {:ok, %{}})
assert fake_upgrade(pid, {:ok, %{strategy: :unknown}}) ==
{:error, {:error, {:supervisor_data, {:invalid_strategy, :unknown}}}}
assert fake_upgrade(pid, {:ok, %{intensity: -1}}) ==
{:error, {:error, {:supervisor_data, {:invalid_intensity, -1}}}}
assert fake_upgrade(pid, {:ok, %{period: 0}}) ==
{:error, {:error, {:supervisor_data, {:invalid_period, 0}}}}
assert fake_upgrade(pid, {:ok, %{max_children: -1}}) ==
{:error, {:error, {:supervisor_data, {:invalid_max_children, -1}}}}
assert fake_upgrade(pid, :unknown) == {:error, :unknown}
assert fake_upgrade(pid, :ignore) == :ok
end
test "with ok init" do
{:ok, pid} = DynamicSupervisor.start_link(Simple, {:ok, %{}})
{:ok, _} = DynamicSupervisor.start_child(pid, sleepy_worker())
assert %{active: 1} = DynamicSupervisor.count_children(pid)
assert fake_upgrade(pid, {:ok, %{max_children: 1}}) == :ok
assert %{active: 1} = DynamicSupervisor.count_children(pid)
assert DynamicSupervisor.start_child(pid, {Task, fn -> :ok end}) == {:error, :max_children}
end
defp fake_upgrade(pid, init_arg) do
:ok = :sys.suspend(pid)
:sys.replace_state(pid, fn state -> %{state | args: init_arg} end)
res = :sys.change_code(pid, :gen_server, 123, :extra)
:ok = :sys.resume(pid)
res
end
end
describe "start_child/2" do
test "supports old child spec" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = {Task, {Task, :start_link, [fn -> :ok end]}, :temporary, 5000, :worker, [Task]}
assert {:ok, pid} = DynamicSupervisor.start_child(pid, child)
assert is_pid(pid)
end
test "supports new child spec as tuple" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = %{id: Task, restart: :temporary, start: {Task, :start_link, [fn -> :ok end]}}
assert {:ok, pid} = DynamicSupervisor.start_child(pid, child)
assert is_pid(pid)
end
test "supports new child spec" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = {Task, fn -> Process.sleep(:infinity) end}
assert {:ok, pid} = DynamicSupervisor.start_child(pid, child)
assert is_pid(pid)
end
test "supports extra arguments" do
parent = self()
fun = fn -> send(parent, :from_child) end
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, extra_arguments: [fun])
child = %{id: Task, restart: :temporary, start: {Task, :start_link, []}}
assert {:ok, pid} = DynamicSupervisor.start_child(pid, child)
assert is_pid(pid)
assert_receive :from_child
end
test "with invalid child spec" do
assert DynamicSupervisor.start_child(:not_used, %{}) == {:error, {:invalid_child_spec, %{}}}
assert DynamicSupervisor.start_child(:not_used, {1, 2, 3, 4, 5, 6}) ==
{:error, {:invalid_mfa, 2}}
assert DynamicSupervisor.start_child(:not_used, %{id: 1, start: {Task, :foo, :bar}}) ==
{:error, {:invalid_mfa, {Task, :foo, :bar}}}
assert DynamicSupervisor.start_child(:not_used, %{
id: 1,
start: {Task, :foo, [:bar]},
shutdown: -1
}) ==
{:error, {:invalid_shutdown, -1}}
assert DynamicSupervisor.start_child(:not_used, %{
id: 1,
start: {Task, :foo, [:bar]},
significant: true
}) ==
{:error, {:invalid_significant, true}}
end
test "with different returns" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert {:ok, _, :extra} = DynamicSupervisor.start_child(pid, current_module_worker([:ok3]))
assert {:ok, _} = DynamicSupervisor.start_child(pid, current_module_worker([:ok2]))
assert :ignore = DynamicSupervisor.start_child(pid, current_module_worker([:ignore]))
assert {:error, :found} =
DynamicSupervisor.start_child(pid, current_module_worker([:error]))
assert {:error, :unknown} =
DynamicSupervisor.start_child(pid, current_module_worker([:unknown]))
end
test "with throw/error/exit" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert {:error, {{:nocatch, :oops}, [_ | _]}} =
DynamicSupervisor.start_child(pid, current_module_worker([:non_local, :throw]))
assert {:error, {%RuntimeError{}, [_ | _]}} =
DynamicSupervisor.start_child(pid, current_module_worker([:non_local, :error]))
assert {:error, :oops} =
DynamicSupervisor.start_child(pid, current_module_worker([:non_local, :exit]))
end
test "with max_children" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, max_children: 0)
assert {:error, :max_children} =
DynamicSupervisor.start_child(pid, current_module_worker([:ok2]))
end
test "temporary child is not restarted regardless of reason" do
child = current_module_worker([:ok2], restart: :temporary)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(pid)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :whatever)
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(pid)
end
test "transient child is restarted unless normal/shutdown/{shutdown, _}" do
child = current_module_worker([:ok2], restart: :transient)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(pid)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, {:shutdown, :signal})
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(pid)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :whatever)
assert %{workers: 1, active: 1} = DynamicSupervisor.count_children(pid)
end
test "permanent child is restarted regardless of reason" do
child = current_module_worker([:ok2], restart: :permanent)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, max_restarts: 100_000)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert %{workers: 1, active: 1} = DynamicSupervisor.count_children(pid)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, {:shutdown, :signal})
assert %{workers: 2, active: 2} = DynamicSupervisor.count_children(pid)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :whatever)
assert %{workers: 3, active: 3} = DynamicSupervisor.count_children(pid)
end
test "child is restarted with different values" do
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, max_restarts: 100_000)
assert {:ok, child1} =
DynamicSupervisor.start_child(pid, current_module_worker([:restart, :ok2]))
assert [{:undefined, ^child1, :worker, [DynamicSupervisorTest]}] =
DynamicSupervisor.which_children(pid)
assert_kill(child1, :shutdown)
assert %{workers: 1, active: 1} = DynamicSupervisor.count_children(pid)
assert {:ok, child2} =
DynamicSupervisor.start_child(pid, current_module_worker([:restart, :ok3]))
assert [
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, ^child2, :worker, [DynamicSupervisorTest]}
] = DynamicSupervisor.which_children(pid)
assert_kill(child2, :shutdown)
assert %{workers: 2, active: 2} = DynamicSupervisor.count_children(pid)
assert {:ok, child3} =
DynamicSupervisor.start_child(pid, current_module_worker([:restart, :ignore]))
assert [
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, _, :worker, [DynamicSupervisorTest]}
] = DynamicSupervisor.which_children(pid)
assert_kill(child3, :shutdown)
assert %{workers: 2, active: 2} = DynamicSupervisor.count_children(pid)
assert {:ok, child4} =
DynamicSupervisor.start_child(pid, current_module_worker([:restart, :error]))
assert [
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, _, :worker, [DynamicSupervisorTest]}
] = DynamicSupervisor.which_children(pid)
assert_kill(child4, :shutdown)
assert %{workers: 3, active: 2} = DynamicSupervisor.count_children(pid)
assert {:ok, child5} =
DynamicSupervisor.start_child(pid, current_module_worker([:restart, :unknown]))
assert [
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, _, :worker, [DynamicSupervisorTest]},
{:undefined, :restarting, :worker, [DynamicSupervisorTest]},
{:undefined, _, :worker, [DynamicSupervisorTest]}
] = DynamicSupervisor.which_children(pid)
assert_kill(child5, :shutdown)
assert %{workers: 4, active: 2} = DynamicSupervisor.count_children(pid)
end
test "restarting on init children counted in max_children" do
child = current_module_worker([:restart, :error], restart: :permanent)
opts = [strategy: :one_for_one, max_children: 1, max_restarts: 100_000]
{:ok, pid} = DynamicSupervisor.start_link(opts)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert %{workers: 1, active: 0} = DynamicSupervisor.count_children(pid)
child = current_module_worker([:restart, :ok2], restart: :permanent)
assert {:error, :max_children} = DynamicSupervisor.start_child(pid, child)
end
test "restarting on exit children counted in max_children" do
child = current_module_worker([:ok2], restart: :permanent)
opts = [strategy: :one_for_one, max_children: 1, max_restarts: 100_000]
{:ok, pid} = DynamicSupervisor.start_link(opts)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert %{workers: 1, active: 1} = DynamicSupervisor.count_children(pid)
child = current_module_worker([:ok2], restart: :permanent)
assert {:error, :max_children} = DynamicSupervisor.start_child(pid, child)
end
test "restarting a child with extra_arguments successfully restarts child" do
parent = self()
fun = fn ->
send(parent, :from_child)
Process.sleep(:infinity)
end
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one, extra_arguments: [fun])
child = %{id: Task, restart: :transient, start: {Task, :start_link, []}}
assert {:ok, child} = DynamicSupervisor.start_child(sup, child)
assert is_pid(child)
assert_receive :from_child
assert %{active: 1, workers: 1} = DynamicSupervisor.count_children(sup)
assert_kill(child, :oops)
assert_receive :from_child
assert %{workers: 1, active: 1} = DynamicSupervisor.count_children(sup)
end
test "child is restarted when trying again" do
child = current_module_worker([:try_again, self()], restart: :permanent)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, max_restarts: 2)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_received {:try_again, true}
assert_kill(child_pid, :shutdown)
assert_receive {:try_again, false}
assert_receive {:try_again, true}
assert %{workers: 1, active: 1} = DynamicSupervisor.count_children(pid)
end
test "child triggers maximum restarts" do
Process.flag(:trap_exit, true)
child = current_module_worker([:restart, :error], restart: :permanent)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, max_restarts: 1)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert_receive {:EXIT, ^pid, :shutdown}
end
test "child triggers maximum intensity when trying again" do
Process.flag(:trap_exit, true)
child = current_module_worker([:restart, :error], restart: :permanent)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, max_restarts: 10)
assert {:ok, child_pid} = DynamicSupervisor.start_child(pid, child)
assert_kill(child_pid, :shutdown)
assert_receive {:EXIT, ^pid, :shutdown}
end
test "with valid shutdown" do
Process.flag(:trap_exit, true)
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one)
for n <- 0..1 do
assert {:ok, child_pid} =
DynamicSupervisor.start_child(pid, %{
id: n,
start: {Task, :start_link, [fn -> Process.sleep(:infinity) end]},
shutdown: n
})
assert_kill(child_pid, :shutdown)
end
end
test "with invalid valid shutdown" do
assert DynamicSupervisor.start_child(:not_used, %{
id: 1,
start: {Task, :start_link, [fn -> :ok end]},
shutdown: -1
}) == {:error, {:invalid_shutdown, -1}}
end
def start_link(:ok3), do: {:ok, spawn_link(fn -> Process.sleep(:infinity) end), :extra}
def start_link(:ok2), do: {:ok, spawn_link(fn -> Process.sleep(:infinity) end)}
def start_link(:error), do: {:error, :found}
def start_link(:ignore), do: :ignore
def start_link(:unknown), do: :unknown
def start_link(:non_local, :throw), do: throw(:oops)
def start_link(:non_local, :error), do: raise("oops")
def start_link(:non_local, :exit), do: exit(:oops)
def start_link(:try_again, notify) do
if Process.get(:try_again) do
Process.put(:try_again, false)
send(notify, {:try_again, false})
{:error, :try_again}
else
Process.put(:try_again, true)
send(notify, {:try_again, true})
start_link(:ok2)
end
end
def start_link(:restart, value) do
if Process.get({:restart, value}) do
start_link(value)
else
Process.put({:restart, value}, true)
start_link(:ok2)
end
end
end
describe "terminate/2" do
test "terminates children with brutal kill" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = sleepy_worker(shutdown: :brutal_kill)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child)
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, :killed}
assert_receive {:DOWN, _, :process, ^child2, :killed}
assert_receive {:DOWN, _, :process, ^child3, :killed}
end
test "terminates children with infinity shutdown" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = sleepy_worker(shutdown: :infinity)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child)
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, :shutdown}
assert_receive {:DOWN, _, :process, ^child2, :shutdown}
assert_receive {:DOWN, _, :process, ^child3, :shutdown}
end
test "terminates children with infinity shutdown and abnormal reason" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
parent = self()
fun = fn ->
Process.flag(:trap_exit, true)
send(parent, :ready)
receive(do: (_ -> exit({:shutdown, :oops})))
end
child = Supervisor.child_spec({Task, fun}, shutdown: :infinity)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child)
assert_receive :ready
assert_receive :ready
assert_receive :ready
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, {:shutdown, :oops}}
assert_receive {:DOWN, _, :process, ^child2, {:shutdown, :oops}}
assert_receive {:DOWN, _, :process, ^child3, {:shutdown, :oops}}
end
test "terminates children with integer shutdown" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = sleepy_worker(shutdown: 1000)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child)
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, :shutdown}
assert_receive {:DOWN, _, :process, ^child2, :shutdown}
assert_receive {:DOWN, _, :process, ^child3, :shutdown}
end
test "terminates children with integer shutdown and abnormal reason" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
parent = self()
fun = fn ->
Process.flag(:trap_exit, true)
send(parent, :ready)
receive(do: (_ -> exit({:shutdown, :oops})))
end
child = Supervisor.child_spec({Task, fun}, shutdown: 1000)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child)
assert_receive :ready
assert_receive :ready
assert_receive :ready
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, {:shutdown, :oops}}
assert_receive {:DOWN, _, :process, ^child2, {:shutdown, :oops}}
assert_receive {:DOWN, _, :process, ^child3, {:shutdown, :oops}}
end
test "terminates children with expired integer shutdown" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
parent = self()
fun = fn ->
Process.sleep(:infinity)
end
tmt = fn ->
Process.flag(:trap_exit, true)
send(parent, :ready)
Process.sleep(:infinity)
end
child_fun = Supervisor.child_spec({Task, fun}, shutdown: 1)
child_tmt = Supervisor.child_spec({Task, tmt}, shutdown: 1)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child_fun)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child_tmt)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child_fun)
assert_receive :ready
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, :shutdown}
assert_receive {:DOWN, _, :process, ^child2, :killed}
assert_receive {:DOWN, _, :process, ^child3, :shutdown}
end
test "terminates children with permanent restart and normal reason" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
parent = self()
fun = fn ->
Process.flag(:trap_exit, true)
send(parent, :ready)
receive(do: (_ -> exit(:normal)))
end
child = Supervisor.child_spec({Task, fun}, shutdown: :infinity, restart: :permanent)
assert {:ok, child1} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child2} = DynamicSupervisor.start_child(sup, child)
assert {:ok, child3} = DynamicSupervisor.start_child(sup, child)
assert_receive :ready
assert_receive :ready
assert_receive :ready
Process.monitor(child1)
Process.monitor(child2)
Process.monitor(child3)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, :normal}
assert_receive {:DOWN, _, :process, ^child2, :normal}
assert_receive {:DOWN, _, :process, ^child3, :normal}
end
test "terminates with mixed children" do
Process.flag(:trap_exit, true)
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
assert {:ok, child1} =
DynamicSupervisor.start_child(sup, sleepy_worker(shutdown: :infinity))
assert {:ok, child2} =
DynamicSupervisor.start_child(sup, sleepy_worker(shutdown: :brutal_kill))
Process.monitor(child1)
Process.monitor(child2)
assert_kill(sup, :shutdown)
assert_receive {:DOWN, _, :process, ^child1, :shutdown}
assert_receive {:DOWN, _, :process, ^child2, :killed}
end
end
describe "terminate_child/2" do
test "terminates child with brutal kill" do
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = sleepy_worker(shutdown: :brutal_kill)
assert {:ok, child_pid} = DynamicSupervisor.start_child(sup, child)
Process.monitor(child_pid)
assert :ok = DynamicSupervisor.terminate_child(sup, child_pid)
assert_receive {:DOWN, _, :process, ^child_pid, :killed}
assert {:error, :not_found} = DynamicSupervisor.terminate_child(sup, child_pid)
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(sup)
end
test "terminates child with integer shutdown" do
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one)
child = sleepy_worker(shutdown: 1000)
assert {:ok, child_pid} = DynamicSupervisor.start_child(sup, child)
Process.monitor(child_pid)
assert :ok = DynamicSupervisor.terminate_child(sup, child_pid)
assert_receive {:DOWN, _, :process, ^child_pid, :shutdown}
assert {:error, :not_found} = DynamicSupervisor.terminate_child(sup, child_pid)
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(sup)
end
test "terminates restarting child" do
{:ok, sup} = DynamicSupervisor.start_link(strategy: :one_for_one, max_restarts: 100_000)
child = current_module_worker([:restart, :error], restart: :permanent)
assert {:ok, child_pid} = DynamicSupervisor.start_child(sup, child)
assert_kill(child_pid, :shutdown)
assert :ok = DynamicSupervisor.terminate_child(sup, child_pid)
assert {:error, :not_found} = DynamicSupervisor.terminate_child(sup, child_pid)
assert %{workers: 0, active: 0} = DynamicSupervisor.count_children(sup)
end
end
defp sleepy_worker(opts \\ []) do
mfa = {Task, :start_link, [Process, :sleep, [:infinity]]}
Supervisor.child_spec(%{id: Task, start: mfa}, opts)
end
defp current_module_worker(args, opts \\ []) do
Supervisor.child_spec(%{id: __MODULE__, start: {__MODULE__, :start_link, args}}, opts)
end
defp assert_kill(pid, reason) do
ref = Process.monitor(pid)
Process.exit(pid, reason)
assert_receive {:DOWN, ^ref, _, _, _}
end
end