if Phoenix.Sync.sandbox_enabled?() do defmodule Phoenix.Sync.Sandbox.StackRegistry do @moduledoc false use GenServer def start_link(_) do GenServer.start_link(__MODULE__, [], name: __MODULE__) end def register(registry \\ __MODULE__, pid, stack_id) do GenServer.call(registry, {:register, pid, stack_id}) end def lookup(registry \\ __MODULE__, pid) do GenServer.call(registry, {:lookup, pid}) end def configure(registry \\ __MODULE__, stack_id, api, client) do GenServer.call(registry, {:configure, self(), stack_id, api, client}) end # api should be removed when test pid exits # registered pid should be removed when it exits def get_api(registry \\ __MODULE__, stack_id) do GenServer.call(registry, {:get, :api, stack_id}) end def get_client(registry \\ __MODULE__, stack_id) do GenServer.call(registry, {:get, :client, stack_id}) end def shared_mode(registry \\ __MODULE__, owner, stack_id) do GenServer.call(registry, {:shared_mode, owner, stack_id}) end ## callbacks def init(_) do {:ok, %{stack_pids: %{}, stacks: %{}, shared: nil}} end def handle_call({:register, pid, stack_id}, _from, state) do _ref = Process.monitor(pid, tag: {:down, :register}) state = Map.update!(state, :stack_pids, &Map.put(&1, pid, stack_id)) {:reply, :ok, state} end def handle_call({:lookup, _pid}, _from, %{shared: {_owner, stack_id}} = state) do {:reply, stack_id, state} end def handle_call({:lookup, pid}, _from, state) do {:reply, Map.get(state.stack_pids, pid), state} end def handle_call({:configure, pid, stack_id, api, client}, _from, state) do _ref = Process.monitor(pid, tag: {:down, :stack, stack_id}) state = Map.update!(state, :stacks, &Map.put(&1, stack_id, %{api: api, client: client})) {:reply, :ok, state} end def handle_call({:get, key, stack_id}, _from, state) when key in [:api, :client] do {:reply, get_config(state, stack_id, key), state} end # shared mode only works for non-async tests, which means that only # one test is running at any time, so we can set the shared pid globally def handle_call({:shared_mode, owner, stack_id}, _from, %{shared: nil} = state) when is_pid(owner) do _ref = Process.monitor(owner, tag: {:down, :shared, stack_id}) {:reply, :ok, Map.put(state, :shared, {owner, stack_id})} end def handle_call({:shared_mode, owner, stack_id}, _from, %{shared: {owner, stack_id}} = state) do {:reply, :ok, state} end def handle_call( {:shared_mode, _owner, _stack_id}, _from, %{shared: {owner, stack_id}} = state ) do {:reply, {:error, "Shared mode already registered to pid #{inspect(owner)} for stack #{inspect(stack_id)}"}, state} end def handle_info({{:down, :stack, stack_id}, _ref, :process, _pid, _reason}, state) do state = Map.update!(state, :stacks, &Map.delete(&1, stack_id)) {:noreply, state} end def handle_info( {{:down, :shared, stack_id}, _ref, :process, pid, _reason}, %{shared: {pid, stack_id}} = state ) do {:noreply, Map.put(state, :shared, nil)} end def handle_info({{:down, :register}, _ref, :process, pid, _reason}, state) do state = Map.update!(state, :stack_pids, &Map.delete(&1, pid)) {:noreply, state} end defp get_config(state, stack_id, key) do with {:ok, config} <- Map.fetch(state.stacks, stack_id) do Map.fetch(config, key) end end end end