Specialized testing utilities for supervision trees.
This module provides helpers for testing supervisor behavior, restart strategies, and supervision tree structures.
Key Features
- Test restart strategies (one_for_one, one_for_all, rest_for_one)
- Trace supervision events
- Verify supervision tree structure
- Wait for supervisor stabilization
Usage
import Supertester.SupervisorHelpers
test "supervisor restart strategy" do
{:ok, supervisor} = setup_isolated_supervisor(MySupervisor)
result = test_restart_strategy(supervisor, :one_for_one, {:kill_child, :worker_1})
assert result.restarted == [:worker_1]
assert result.not_restarted == [:worker_2, :worker_3]
end
Summary
Functions
Asserts supervision tree matches expected structure.
Gets the count of active children in a supervisor.
Tests supervisor restart strategies with various failure scenarios.
Traces all supervision events for verification.
Waits until supervisor has all children running and stable.
Types
@type child_structure() :: {child_id :: term(), module :: module()} | {child_id :: term(), tree_structure()}
@type tree_structure() :: %{ supervisor: module(), strategy: atom(), children: [child_structure()] }
Functions
@spec assert_supervision_tree_structure(Supervisor.supervisor(), tree_structure()) :: :ok
Asserts supervision tree matches expected structure.
Parameters
supervisor- The supervisor PIDexpected- Expected tree structure
Examples
test "supervision tree structure" do
{:ok, root} = setup_isolated_supervisor(RootSupervisor)
assert_supervision_tree_structure(root, %{
supervisor: RootSupervisor,
strategy: :one_for_one,
children: [
{:cache, CacheServer},
{:workers, %{
supervisor: WorkerSupervisor,
strategy: :one_for_all,
children: [
{:worker_1, Worker},
{:worker_2, Worker}
]
}}
]
})
end
@spec get_active_child_count(Supervisor.supervisor()) :: non_neg_integer()
Gets the count of active children in a supervisor.
Parameters
supervisor- The supervisor PID
Returns
Number of active children
Examples
count = get_active_child_count(supervisor)
assert count == 3
@spec test_restart_strategy(Supervisor.supervisor(), atom(), restart_scenario()) :: test_result()
Tests supervisor restart strategies with various failure scenarios.
Parameters
supervisor- The supervisor PID or namestrategy- Expected strategy (:one_for_one, :one_for_all, :rest_for_one)scenario- The failure scenario to test
Examples
test "one_for_one restarts only failed child" do
{:ok, supervisor} = setup_isolated_supervisor(MySupervisor)
result = test_restart_strategy(supervisor, :one_for_one, {:kill_child, :worker_1})
assert result.restarted == [:worker_1]
assert result.not_restarted == [:worker_2, :worker_3]
end
@spec trace_supervision_events( Supervisor.supervisor(), keyword() ) :: {:ok, (-> [supervision_event()])}
Traces all supervision events for verification.
Parameters
supervisor- The supervisor PID to traceopts- Options (currently unused)
Returns
{:ok, stop_fn} where stop_fn returns the list of traced events
Examples
test "supervisor restart behavior" do
{:ok, supervisor} = setup_isolated_supervisor(MySupervisor)
{:ok, stop_trace} = trace_supervision_events(supervisor)
# Cause some failures
children = Supervisor.which_children(supervisor)
Enum.each(children, fn {_id, pid, _type, _mods} ->
Process.exit(pid, :kill)
end)
# Get events
events = stop_trace.()
# Verify restart sequence
assert length(events) >= 6 # 3 terminates + 3 restarts
end
@spec wait_for_supervisor_stabilization(Supervisor.supervisor(), timeout()) :: :ok | {:error, :timeout}
Waits until supervisor has all children running and stable.
Parameters
supervisor- The supervisor PIDtimeout- Timeout in milliseconds (default: 5000)
Examples
test "supervisor recovery" do
{:ok, supervisor} = setup_isolated_supervisor(MySupervisor)
# Cause chaos
children = Supervisor.which_children(supervisor)
Enum.each(children, fn {_id, pid, _type, _mods} ->
Process.exit(pid, :kill)
end)
# Wait for stabilization
assert :ok = wait_for_supervisor_stabilization(supervisor)
# All should be alive
assert_all_children_alive(supervisor)
end