diff --git a/dev/live_components/crash.ex b/dev/live_components/crash.ex
new file mode 100644
index 000000000..b7e608c47
--- /dev/null
+++ b/dev/live_components/crash.ex
@@ -0,0 +1,13 @@
+defmodule LiveDebuggerDev.LiveComponents.Crash do
+ use DevWeb, :live_component
+
+ def render(assigns) do
+ ~H"""
+
+ <.box title="Crash [LiveComponent]" color="red">
+ <.button phx-click="crash" color="red" phx-target={@myself}>Crash
+
+
+ """
+ end
+end
diff --git a/dev/live_views/main.ex b/dev/live_views/main.ex
index b74bc16b9..b9757d9a2 100644
--- a/dev/live_views/main.ex
+++ b/dev/live_views/main.ex
@@ -58,6 +58,7 @@ defmodule LiveDebuggerDev.LiveViews.Main do
<.live_component id="many_assigns" module={LiveComponents.ManyAssigns} />
<.live_component id="name_outer" name={@name} module={LiveComponents.Name} />
+ <.live_component id="crash" module={LiveComponents.Crash} />
<.live_component id="send_outer" module={LiveComponents.Send}>
<.live_component id="name_inner" name={@name} module={LiveComponents.Name} />
<.live_component id="long_name" module={LiveComponents.LiveComponentWithVeryVeryLongName} />
diff --git a/lib/live_debugger.ex b/lib/live_debugger.ex
index 646d4ee6f..b3ccb6316 100644
--- a/lib/live_debugger.ex
+++ b/lib/live_debugger.ex
@@ -39,6 +39,7 @@ defmodule LiveDebugger do
else
children ++
[
+ {LiveDebugger.GenServers.StateServer, []},
{LiveDebugger.GenServers.CallbackTracingServer, []},
{LiveDebugger.GenServers.EtsTableServer, []}
]
diff --git a/lib/live_debugger/gen_servers/callback_tracing_server.ex b/lib/live_debugger/gen_servers/callback_tracing_server.ex
index 21450a223..a3532d13f 100644
--- a/lib/live_debugger/gen_servers/callback_tracing_server.ex
+++ b/lib/live_debugger/gen_servers/callback_tracing_server.ex
@@ -177,23 +177,18 @@ defmodule LiveDebugger.GenServers.CallbackTracingServer do
@spec do_publish(Trace.t()) :: :ok
defp do_publish(%{module: Phoenix.LiveView.Diff} = trace) do
- trace
- |> PubSubUtils.component_deleted_topic()
- |> PubSubUtils.broadcast({:new_trace, trace})
+ PubSubUtils.component_deleted_topic()
+ |> PubSubUtils.broadcast({:component_deleted, trace})
end
- defp do_publish(trace) do
+ defp do_publish(%Trace{} = trace) do
socket_id = trace.socket_id
node_id = Trace.node_id(trace)
transport_pid = trace.transport_pid
fun = trace.function
socket_id
- |> PubSubUtils.tsnf_topic(transport_pid, node_id, fun, :call)
- |> PubSubUtils.broadcast({:new_trace, trace})
-
- socket_id
- |> PubSubUtils.ts_f_topic(transport_pid, fun)
+ |> PubSubUtils.trace_topic(transport_pid, node_id, fun, :call)
|> PubSubUtils.broadcast({:new_trace, trace})
end
@@ -204,8 +199,13 @@ defmodule LiveDebugger.GenServers.CallbackTracingServer do
transport_pid = trace.transport_pid
fun = trace.function
+ if fun == :render do
+ PubSubUtils.node_rendered_topic()
+ |> PubSubUtils.broadcast({:render_trace, trace})
+ end
+
socket_id
- |> PubSubUtils.tsnf_topic(transport_pid, node_id, fun, :return)
+ |> PubSubUtils.trace_topic(transport_pid, node_id, fun, :return)
|> PubSubUtils.broadcast({:updated_trace, trace})
end
end
diff --git a/lib/live_debugger/gen_servers/ets_table_server.ex b/lib/live_debugger/gen_servers/ets_table_server.ex
index 440093073..aae06e893 100644
--- a/lib/live_debugger/gen_servers/ets_table_server.ex
+++ b/lib/live_debugger/gen_servers/ets_table_server.ex
@@ -21,37 +21,13 @@ defmodule LiveDebugger.GenServers.EtsTableServer do
It creates table if none is associated with given pid
"""
@spec table!(pid :: pid()) :: :ets.table()
- def table!(pid) when is_pid(pid) do
- impl().table!(pid)
- end
+ def table!(pid) when is_pid(pid), do: impl().table!(pid)
@doc """
If table for given `pid` exists it deletes it from ETS.
"""
@spec delete_table!(pid :: pid()) :: :ok
- def delete_table!(pid) when is_pid(pid) do
- impl().delete_table!(pid)
- end
-
- def impl() do
- Application.get_env(:live_debugger, :ets_table_server, __MODULE__.Impl)
- end
-
- defmodule Impl do
- @moduledoc false
- @behaviour LiveDebugger.GenServers.EtsTableServer
- @server_module LiveDebugger.GenServers.EtsTableServer
-
- @impl true
- def table!(pid) do
- GenServer.call(@server_module, {:get_or_create_table, pid}, 1000)
- end
-
- @impl true
- def delete_table!(pid) do
- GenServer.call(@server_module, {:delete_table, pid}, 1000)
- end
- end
+ def delete_table!(pid) when is_pid(pid), do: impl().delete_table!(pid)
## GenServer
@@ -69,9 +45,8 @@ defmodule LiveDebugger.GenServers.EtsTableServer do
def handle_info({:DOWN, _, :process, closed_pid, _}, table_refs) do
{_, table_refs} = delete_ets_table(closed_pid, table_refs)
- closed_pid
- |> PubSubUtils.process_status_topic()
- |> PubSubUtils.broadcast({:process_status, :dead})
+ PubSubUtils.process_status_topic()
+ |> PubSubUtils.broadcast({:process_status, {:dead, closed_pid}})
{:noreply, table_refs}
end
@@ -95,6 +70,26 @@ defmodule LiveDebugger.GenServers.EtsTableServer do
{:reply, :ok, table_refs}
end
+ defp impl() do
+ Application.get_env(:live_debugger, :ets_table_server, __MODULE__.Impl)
+ end
+
+ defmodule Impl do
+ @moduledoc false
+ @behaviour LiveDebugger.GenServers.EtsTableServer
+ @server_module LiveDebugger.GenServers.EtsTableServer
+
+ @impl true
+ def table!(pid) do
+ GenServer.call(@server_module, {:get_or_create_table, pid}, 1000)
+ end
+
+ @impl true
+ def delete_table!(pid) do
+ GenServer.call(@server_module, {:delete_table, pid}, 1000)
+ end
+ end
+
@spec create_ets_table() :: :ets.table()
defp create_ets_table() do
:ets.new(@ets_table_name, [:ordered_set, :public])
diff --git a/lib/live_debugger/gen_servers/state_server.ex b/lib/live_debugger/gen_servers/state_server.ex
new file mode 100644
index 000000000..c2d2c3a3d
--- /dev/null
+++ b/lib/live_debugger/gen_servers/state_server.ex
@@ -0,0 +1,114 @@
+defmodule LiveDebugger.GenServers.StateServer do
+ @moduledoc """
+ This gen_server is responsible for storing the state of the application.
+ It collects state when `render` or `delete_component` callbacks are traced.
+ It uses named ETS table to store the state of the LiveView channel process.
+ When process dies, it removes the state from the table.
+ """
+
+ use GenServer
+
+ alias LiveDebugger.Services.System.ProcessService
+ alias LiveDebugger.Utils.PubSub, as: PubSubUtils
+ alias LiveDebugger.CommonTypes
+ alias LiveDebugger.Structs.Trace
+
+ @ets_table_name :lvdbg_states
+
+ @callback get(pid :: pid()) :: {:ok, CommonTypes.channel_state()} | {:error, term()}
+
+ @doc """
+ Returns previously stored state of the LiveView channel process identified by `pid`.
+ If the state is not found, it returns `{:error, :not_found}`.
+ """
+ @spec get(pid :: pid()) :: {:ok, CommonTypes.channel_state()} | {:error, term()}
+ def get(pid) when is_pid(pid) do
+ impl().get(pid)
+ end
+
+ @doc false
+ @spec ets_table_name() :: atom()
+ def ets_table_name(), do: @ets_table_name
+
+ @doc false
+ def record_id(pid), do: "#{inspect(pid)}"
+
+ @doc false
+ def start_link(args \\ []) do
+ GenServer.start_link(__MODULE__, args, name: __MODULE__)
+ end
+
+ @impl true
+ def init(_args) do
+ :ets.new(@ets_table_name, [:named_table, :public, :ordered_set])
+
+ PubSubUtils.node_rendered_topic()
+ |> PubSubUtils.subscribe!()
+
+ PubSubUtils.component_deleted_topic()
+ |> PubSubUtils.subscribe!()
+
+ PubSubUtils.process_status_topic()
+ |> PubSubUtils.subscribe!()
+
+ {:ok, []}
+ end
+
+ @impl true
+ def handle_info({:component_deleted, trace}, state) do
+ save_state(trace)
+
+ {:noreply, state}
+ end
+
+ def handle_info({:render_trace, trace}, state) do
+ save_state(trace)
+
+ {:noreply, state}
+ end
+
+ def handle_info({:process_status, {:dead, pid}}, state) do
+ :ets.delete(@ets_table_name, record_id(pid))
+
+ {:noreply, state}
+ end
+
+ defp save_state(%Trace{pid: pid} = trace) do
+ with {:ok, channel_state} <- ProcessService.state(pid) do
+ record_id = record_id(pid)
+ :ets.insert(@ets_table_name, {record_id, channel_state})
+
+ publish_state_changed(trace, channel_state)
+ end
+ end
+
+ defp publish_state_changed(%Trace{} = trace, channel_state) do
+ socket_id = trace.socket_id
+ transport_pid = trace.transport_pid
+ node_id = trace.cid || trace.pid
+
+ PubSubUtils.state_changed_topic(socket_id, transport_pid, node_id)
+ |> PubSubUtils.broadcast({:state_changed, channel_state, trace})
+
+ PubSubUtils.state_changed_topic(socket_id, transport_pid)
+ |> PubSubUtils.broadcast({:state_changed, channel_state, trace})
+ end
+
+ defp impl() do
+ Application.get_env(:live_debugger, :state_server, __MODULE__.Impl)
+ end
+
+ defmodule Impl do
+ @moduledoc false
+
+ @behaviour LiveDebugger.GenServers.StateServer
+ @server_module LiveDebugger.GenServers.StateServer
+
+ def get(pid) do
+ case :ets.lookup(@server_module.ets_table_name(), @server_module.record_id(pid)) do
+ [{_, channel_state}] -> {:ok, channel_state}
+ [] -> {:error, :not_found}
+ end
+ end
+ end
+end
diff --git a/lib/live_debugger/services/channel_service.ex b/lib/live_debugger/services/channel_service.ex
index 209d10a65..c9770e8e7 100644
--- a/lib/live_debugger/services/channel_service.ex
+++ b/lib/live_debugger/services/channel_service.ex
@@ -4,20 +4,15 @@ defmodule LiveDebugger.Services.ChannelService do
"""
alias LiveDebugger.Structs.TreeNode
- alias LiveDebugger.Services.System.ProcessService
alias LiveDebugger.CommonTypes
+ alias LiveDebugger.GenServers.StateServer
@doc """
Retrieves the state of the LiveView channel process identified by `pid`.
"""
@spec state(pid :: pid()) :: {:ok, CommonTypes.channel_state()} | {:error, term()}
def state(pid) do
- case ProcessService.state(pid) do
- {:ok, %{socket: %Phoenix.LiveView.Socket{}, components: _} = state} -> {:ok, state}
- {:ok, _} -> {:error, "PID: #{inspect(pid)} is not a LiveView process"}
- {:error, :not_alive} -> {:error, :not_alive}
- {:error, _} -> {:error, "Could not get state from pid: #{inspect(pid)}"}
- end
+ StateServer.get(pid)
end
@doc """
diff --git a/lib/live_debugger/services/system/dbg_service.ex b/lib/live_debugger/services/system/dbg_service.ex
index b0c9f041f..93f82ba2a 100644
--- a/lib/live_debugger/services/system/dbg_service.ex
+++ b/lib/live_debugger/services/system/dbg_service.ex
@@ -29,9 +29,7 @@ defmodule LiveDebugger.Services.System.DbgService do
@spec tracer(:process, handler_spec()) :: {:ok, pid()} | {:error, term()}
@spec tracer(:module, module_spec()) :: {:ok, pid()} | {:error, term()}
@spec tracer(:file, filename :: :file.name_all()) :: {:ok, pid()} | {:error, term()}
- def tracer(type, handler_spec) do
- impl().tracer(type, handler_spec)
- end
+ def tracer(type, handler_spec), do: impl().tracer(type, handler_spec)
@doc """
Wrapper for `:dbg.p/2`.
@@ -39,18 +37,14 @@ defmodule LiveDebugger.Services.System.DbgService do
`p` stands for **p**rocess.
"""
@spec p(item :: term(), flags :: term()) :: {:ok, match_desc()} | {:error, term()}
- def p(item, flags) do
- impl().p(item, flags)
- end
+ def p(item, flags), do: impl().p(item, flags)
@doc """
Wrapper for `:dbg.tp/2` that sets up a trace pattern.
Enables call trace for one or more exported functions specified by `ModuleOrMFA`.
"""
@spec tp(module() | mfa(), match_spec :: term()) :: {:ok, match_desc()} | {:error, term()}
- def tp(module, match_spec) do
- impl().tp(module, match_spec)
- end
+ def tp(module, match_spec), do: impl().tp(module, match_spec)
defp impl() do
Application.get_env(
diff --git a/lib/live_debugger/structs/trace.ex b/lib/live_debugger/structs/trace.ex
index 7ae7e456e..cbea26ce8 100644
--- a/lib/live_debugger/structs/trace.ex
+++ b/lib/live_debugger/structs/trace.ex
@@ -31,7 +31,7 @@ defmodule LiveDebugger.Structs.Trace do
@type t() :: %__MODULE__{
id: integer(),
- module: atom(),
+ module: module(),
function: atom(),
arity: non_neg_integer(),
args: list(),
@@ -39,14 +39,14 @@ defmodule LiveDebugger.Structs.Trace do
transport_pid: pid() | nil,
pid: pid(),
cid: struct() | nil,
- timestamp: timestamp(),
+ timestamp: integer(),
execution_time: non_neg_integer() | nil
}
@doc """
Creates a new trace struct.
"""
- @spec new(integer(), atom(), atom(), list(), pid(), timestamp(), Keyword.t()) :: t()
+ @spec new(integer(), module(), atom(), list(), pid(), timestamp(), Keyword.t()) :: t()
def new(id, module, function, args, pid, timestamp, opts \\ []) do
socket_id = Keyword.get(opts, :socket_id, get_socket_id_from_args(args))
transport_pid = Keyword.get(opts, :transport_pid, get_transport_pid_from_args(args))
diff --git a/lib/live_debugger/utils/pubsub.ex b/lib/live_debugger/utils/pubsub.ex
index d4a169426..ea37420e2 100644
--- a/lib/live_debugger/utils/pubsub.ex
+++ b/lib/live_debugger/utils/pubsub.ex
@@ -3,7 +3,6 @@ defmodule LiveDebugger.Utils.PubSub do
This module provides helpers for LiveDebugger's PubSub.
"""
- alias LiveDebugger.Structs.Trace
alias LiveDebugger.Structs.TreeNode
@callback broadcast(topic :: String.t(), payload :: term()) :: :ok
@@ -13,85 +12,84 @@ defmodule LiveDebugger.Utils.PubSub do
@callback unsubscribe(topic :: String.t()) :: :ok
@spec broadcast(topic :: String.t(), payload :: term()) :: :ok
- def broadcast(topic, payload) do
- impl().broadcast(topic, payload)
- end
+ def broadcast(topic, payload), do: impl().broadcast(topic, payload)
@spec subscribe!(topics :: [String.t()]) :: :ok
- def subscribe!(topics) when is_list(topics) do
- impl().subscribe!(topics)
- end
+ def subscribe!(topics) when is_list(topics), do: impl().subscribe!(topics)
@spec subscribe!(topic :: String.t()) :: :ok
- def subscribe!(topic) do
- impl().subscribe!(topic)
- end
+ def subscribe!(topic), do: impl().subscribe!(topic)
@spec unsubscribe(topics :: [String.t()]) :: :ok
- def unsubscribe(topics) when is_list(topics) do
- impl().unsubscribe(topics)
- end
+ def unsubscribe(topics) when is_list(topics), do: impl().unsubscribe(topics)
@spec unsubscribe(topic :: String.t()) :: :ok
- def unsubscribe(topic) do
- impl().unsubscribe(topic)
- end
-
- @spec component_deleted_topic(trace :: Trace.t()) :: String.t()
- def component_deleted_topic(trace) do
- socket_id = trace.socket_id
- transport_pid = trace.transport_pid
+ def unsubscribe(topic), do: impl().unsubscribe(topic)
- component_deleted_topic(socket_id, transport_pid)
+ @doc "Use `{:component_deleted, delete_trace}` for broadcasting"
+ @spec component_deleted_topic() :: String.t()
+ def component_deleted_topic() do
+ "lvdbg/component_deleted"
end
+ @doc "Use `{:node_changed, node_id}` for broadcasting"
@spec node_changed_topic(socket_id :: String.t()) :: String.t()
- def node_changed_topic(socket_id) do
+ def node_changed_topic(socket_id) when is_binary(socket_id) do
"lvdbg/#{socket_id}/node_changed"
end
- @spec component_deleted_topic(socket_id :: String.t(), transport_pid :: pid()) :: String.t()
- def component_deleted_topic(socket_id, transport_pid) do
- "lvdbg/#{inspect(transport_pid)}/#{socket_id}/component_deleted"
+ @doc "Use `{:process_status, {status, pid}}` for broadcasting"
+ @spec process_status_topic() :: String.t()
+ def process_status_topic() do
+ "lvdbg/process_status"
+ end
+
+ @doc "Use `{:state_changed, new_state, triggered_trace}` for broadcasting"
+ @spec state_changed_topic(
+ socket_id :: String.t(),
+ transport_pid :: pid()
+ ) :: String.t()
+ def state_changed_topic(socket_id, transport_pid)
+ when is_pid(transport_pid) and is_binary(socket_id) do
+ "lvdbg/#{inspect(transport_pid)}/#{socket_id}/*/state_changed"
+ end
+
+ @doc "Use `{:state_changed, new_state, triggered_trace}` for broadcasting"
+ @spec state_changed_topic(
+ socket_id :: String.t(),
+ transport_pid :: pid(),
+ node_id :: TreeNode.id()
+ ) :: String.t()
+ def state_changed_topic(socket_id, transport_pid, node_id)
+ when is_pid(transport_pid) and is_binary(socket_id) do
+ "lvdbg/#{inspect(transport_pid)}/#{socket_id}/#{inspect(node_id)}/state_changed"
end
- @spec process_status_topic(pid :: pid()) :: String.t()
- def process_status_topic(pid) when is_pid(pid) do
- "lvdbg/#{inspect(pid)}/status"
+ @doc "Use `{:render_trace, trace}` for broadcasting."
+ @spec node_rendered_topic() :: String.t()
+ def node_rendered_topic() do
+ "lvdbg/node_rendered"
end
@doc """
- It stands for transport_pid/socket_id/node_id/function.
+ It stands for `transport_pid/socket_id/node_id/function/type`.
It gives you traces of given callback in given node in given LiveView
Used to update assigns based on render callback and for filtering traces
+
+ Use `{:new_trace, trace}` or `{:updated_trace, trace}` for broadcasting.
"""
- @spec tsnf_topic(
+ @spec trace_topic(
socket_id :: String.t(),
transport_pid :: pid(),
node_id :: TreeNode.id(),
fun :: atom(),
type :: :call | :return
) :: String.t()
- def tsnf_topic(socket_id, transport_pid, node_id, fun, type \\ :call) do
+ def trace_topic(socket_id, transport_pid, node_id, fun, type \\ :call) do
"#{inspect(transport_pid)}/#{socket_id}/#{inspect(node_id)}/#{inspect(fun)}/#{inspect(type)}"
end
- @doc """
- It stands for transport_pid/socket_id/*/function.
-
- It gives you traces of given callback in all nodes of given LiveView
- Used for detecting new nodes in sidebar
- """
- @spec ts_f_topic(
- socket_id :: String.t(),
- transport_pid :: pid(),
- fun :: atom()
- ) :: String.t()
- def ts_f_topic(socket_id, transport_pid, fun) do
- "#{inspect(transport_pid)}/#{socket_id}/*/#{inspect(fun)}"
- end
-
@spec impl() :: module()
defp impl() do
Application.get_env(
diff --git a/lib/live_debugger_web/helpers/state_helper.ex b/lib/live_debugger_web/helpers/state_helper.ex
new file mode 100644
index 000000000..97f373ecb
--- /dev/null
+++ b/lib/live_debugger_web/helpers/state_helper.ex
@@ -0,0 +1,20 @@
+defmodule LiveDebuggerWeb.Helpers.StateHelper do
+ @moduledoc """
+ This module has helper functions for managing state
+ """
+ alias LiveDebugger.CommonTypes
+ alias LiveDebugger.Services.ChannelService
+
+ @doc """
+ Fetches state using ChannelService when no state is passed
+ """
+ @spec maybe_get_state(pid :: pid(), channel_state :: CommonTypes.channel_state() | nil) ::
+ {:ok, CommonTypes.channel_state()} | {:error, term()}
+ def maybe_get_state(pid, channel_state \\ nil) when is_pid(pid) do
+ if is_nil(channel_state) do
+ ChannelService.state(pid)
+ else
+ {:ok, channel_state}
+ end
+ end
+end
diff --git a/lib/live_debugger_web/helpers/tracing_helper.ex b/lib/live_debugger_web/helpers/tracing_helper.ex
index b4aa7b9f9..f6b349a20 100644
--- a/lib/live_debugger_web/helpers/tracing_helper.ex
+++ b/lib/live_debugger_web/helpers/tracing_helper.ex
@@ -142,14 +142,14 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do
|> Enum.filter(fn {_, active?} -> active? end)
|> Enum.flat_map(fn {function, _} ->
[
- PubSubUtils.tsnf_topic(
+ PubSubUtils.trace_topic(
lv_process.socket_id,
lv_process.transport_pid,
node_id,
function,
:call
),
- PubSubUtils.tsnf_topic(
+ PubSubUtils.trace_topic(
lv_process.socket_id,
lv_process.transport_pid,
node_id,
@@ -166,7 +166,7 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do
socket.assigns.current_filters
|> Enum.map(fn {function, _} ->
- PubSubUtils.tsnf_topic(
+ PubSubUtils.trace_topic(
lv_process.socket_id,
lv_process.transport_pid,
node_id,
diff --git a/lib/live_debugger_web/hooks/linked_view.ex b/lib/live_debugger_web/hooks/linked_view.ex
index 93c18bf03..c9a8fb27f 100644
--- a/lib/live_debugger_web/hooks/linked_view.ex
+++ b/lib/live_debugger_web/hooks/linked_view.ex
@@ -60,7 +60,8 @@ defmodule LiveDebuggerWeb.Hooks.LinkedView do
# When fetching LvProcess succeeds, we subscribe to its process state
def handle_async(:fetch_lv_process, {:ok, fetched_lv_process}, socket) do
- subscribe_process_state(fetched_lv_process.pid)
+ PubSubUtils.process_status_topic()
+ |> PubSubUtils.subscribe!()
socket
|> assign(:lv_process, AsyncResult.ok(fetched_lv_process))
@@ -80,12 +81,18 @@ defmodule LiveDebuggerWeb.Hooks.LinkedView do
def handle_async(_, _, socket), do: {:cont, socket}
- def handle_info({:process_status, :dead}, socket) do
+ def handle_info(
+ {:process_status, {:dead, pid}},
+ %{assigns: %{lv_process: %{result: %LvProcess{pid: pid}}}} = socket
+ )
+ when is_pid(pid) do
socket
|> find_successor_lv_process()
|> halt()
end
+ def handle_info({:process_status, _}, socket), do: halt(socket)
+
def handle_info(_, socket), do: {:cont, socket}
defp find_successor_lv_process(socket) do
@@ -125,12 +132,6 @@ defmodule LiveDebuggerWeb.Hooks.LinkedView do
end
end
- defp subscribe_process_state(pid) do
- pid
- |> PubSubUtils.process_status_topic()
- |> PubSubUtils.subscribe!()
- end
-
defp fetch_after(function, milliseconds) do
Process.sleep(milliseconds)
function.()
diff --git a/lib/live_debugger_web/live/sidebar_live.ex b/lib/live_debugger_web/live/sidebar_live.ex
index d24356bad..1511a79b2 100644
--- a/lib/live_debugger_web/live/sidebar_live.ex
+++ b/lib/live_debugger_web/live/sidebar_live.ex
@@ -18,6 +18,7 @@ defmodule LiveDebuggerWeb.SidebarLive do
alias LiveDebugger.Utils.URL
alias LiveDebuggerWeb.LiveComponents.NestedLiveViewsLinks
alias LiveDebugger.Utils.PubSub, as: PubSubUtils
+ alias LiveDebuggerWeb.Helpers.StateHelper
attr(:socket, :map, required: true)
attr(:id, :string, required: true)
@@ -56,11 +57,7 @@ defmodule LiveDebuggerWeb.SidebarLive do
|> PubSubUtils.subscribe!()
lv_process.socket_id
- |> PubSubUtils.component_deleted_topic(lv_process.transport_pid)
- |> PubSubUtils.subscribe!()
-
- lv_process.socket_id
- |> PubSubUtils.ts_f_topic(lv_process.transport_pid, :render)
+ |> PubSubUtils.state_changed_topic(lv_process.transport_pid)
|> PubSubUtils.subscribe!()
end
@@ -111,7 +108,7 @@ defmodule LiveDebuggerWeb.SidebarLive do
end
@impl true
- def handle_info({:new_trace, trace}, socket) do
+ def handle_info({:state_changed, new_state, trace}, socket) do
existing_node_ids = socket.assigns.existing_node_ids
trace_node_id = Trace.node_id(trace)
@@ -120,7 +117,7 @@ defmodule LiveDebuggerWeb.SidebarLive do
updated_map_set = MapSet.put(existing_node_ids.result, trace_node_id)
socket
- |> assign_async_tree()
+ |> assign_async_tree(new_state)
|> update_nested_live_views_links()
|> assign(:existing_node_ids, Map.put(existing_node_ids, :result, updated_map_set))
@@ -128,7 +125,7 @@ defmodule LiveDebuggerWeb.SidebarLive do
updated_map_set = MapSet.delete(existing_node_ids.result, trace_node_id)
socket
- |> assign_async_tree()
+ |> assign_async_tree(new_state)
|> update_nested_live_views_links()
|> assign(:existing_node_ids, Map.put(existing_node_ids, :result, updated_map_set))
@@ -348,11 +345,11 @@ defmodule LiveDebuggerWeb.SidebarLive do
end)
end
- defp assign_async_tree(socket) do
+ defp assign_async_tree(socket, state \\ nil) do
pid = socket.assigns.lv_process.pid
assign_async(socket, [:tree, :max_opened_node_level], fn ->
- with {:ok, channel_state} <- ChannelService.state(pid),
+ with {:ok, channel_state} <- StateHelper.maybe_get_state(pid, state),
{:ok, tree} <- ChannelService.build_tree(channel_state) do
{:ok, %{tree: tree, max_opened_node_level: Tree.max_opened_node_level(tree)}}
else
diff --git a/lib/live_debugger_web/live/state_live.ex b/lib/live_debugger_web/live/state_live.ex
index 38634a34c..eca5647cd 100644
--- a/lib/live_debugger_web/live/state_live.ex
+++ b/lib/live_debugger_web/live/state_live.ex
@@ -13,6 +13,7 @@ defmodule LiveDebuggerWeb.StateLive do
alias LiveDebugger.Services.ChannelService
alias LiveDebugger.Utils.TermParser
alias LiveDebugger.Utils.PubSub, as: PubSubUtils
+ alias LiveDebuggerWeb.Helpers.StateHelper
attr(:socket, :map, required: true)
attr(:id, :string, required: true)
@@ -50,7 +51,7 @@ defmodule LiveDebuggerWeb.StateLive do
|> PubSubUtils.subscribe!()
lv_process.socket_id
- |> PubSubUtils.tsnf_topic(lv_process.transport_pid, node_id, :render)
+ |> PubSubUtils.state_changed_topic(lv_process.transport_pid, node_id)
|> PubSubUtils.subscribe!()
end
@@ -92,15 +93,9 @@ defmodule LiveDebuggerWeb.StateLive do
end
@impl true
- def handle_info({:new_trace, _trace}, socket) do
- socket
- |> assign_async_node_with_type()
- |> noreply()
- end
-
- @impl true
- def handle_info({:updated_trace, _trace}, socket) do
+ def handle_info({:state_changed, channel_state, _trace}, socket) do
socket
+ |> assign_async_node_with_type(channel_state)
|> noreply()
end
@@ -165,12 +160,15 @@ defmodule LiveDebuggerWeb.StateLive do
"""
end
+ defp assign_async_node_with_type(socket, channel_state \\ nil)
+
defp assign_async_node_with_type(
- %{assigns: %{node_id: node_id, lv_process: %{pid: pid}}} = socket
+ %{assigns: %{node_id: node_id, lv_process: %{pid: pid}}} = socket,
+ channel_state
)
when not is_nil(node_id) do
assign_async(socket, [:node, :node_type], fn ->
- with {:ok, channel_state} <- ChannelService.state(pid),
+ with {:ok, channel_state} <- StateHelper.maybe_get_state(pid, channel_state),
{:ok, node} <- ChannelService.get_node(channel_state, node_id),
true <- not is_nil(node) do
{:ok, %{node: node, node_type: TreeNode.type(node)}}
@@ -181,7 +179,7 @@ defmodule LiveDebuggerWeb.StateLive do
end)
end
- defp assign_async_node_with_type(socket) do
+ defp assign_async_node_with_type(socket, _channel_state) do
socket
|> assign(:node, AsyncResult.failed(%AsyncResult{}, :no_node_id))
|> assign(:node_type, AsyncResult.failed(%AsyncResult{}, :no_node_id))
diff --git a/test/gen_servers/callback_tracing_server_test.exs b/test/gen_servers/callback_tracing_server_test.exs
index b11f2c459..ad3ad54ca 100644
--- a/test/gen_servers/callback_tracing_server_test.exs
+++ b/test/gen_servers/callback_tracing_server_test.exs
@@ -10,7 +10,7 @@ defmodule LiveDebugger.GenServers.CallbackTracingServerTest do
alias LiveDebugger.MockDbg
alias LiveDebugger.MockEtsTableServer
alias LiveDebugger.MockPubSubUtils
- alias LiveDebugger.MockProcessService
+ alias LiveDebugger.MockStateServer
@modules [
CoolApp.LiveViews.UserDashboard,
@@ -80,16 +80,16 @@ defmodule LiveDebugger.GenServers.CallbackTracingServerTest do
fun = :delete_component
args = [cid, %{}]
- expected_topic =
- PubSubUtils.component_deleted_topic(%{socket_id: socket_id, transport_pid: transport_pid})
+ component_deleted_topic =
+ PubSubUtils.component_deleted_topic()
- MockProcessService
- |> expect(:state, fn ^pid ->
+ MockStateServer
+ |> expect(:get, fn ^pid ->
{:ok, LiveDebugger.Fakes.state(transport_pid: transport_pid, socket_id: socket_id)}
end)
MockPubSubUtils
- |> expect(:broadcast, fn ^expected_topic, {:new_trace, trace} ->
+ |> expect(:broadcast, fn ^component_deleted_topic, {:component_deleted, trace} ->
send(parent, {:trace, trace})
end)
@@ -125,15 +125,13 @@ defmodule LiveDebugger.GenServers.CallbackTracingServerTest do
table = :ets.new(:test_table, [:ordered_set, :public])
- expected_tsnf_topic = PubSubUtils.tsnf_topic(socket_id, transport_pid, pid, fun)
- expected_ts_f_topic = PubSubUtils.ts_f_topic(socket_id, transport_pid, fun)
+ expected_trace_topic = PubSubUtils.trace_topic(socket_id, transport_pid, pid, fun)
MockEtsTableServer
|> expect(:table!, fn ^pid -> table end)
MockPubSubUtils
- |> expect(:broadcast, fn ^expected_tsnf_topic, {:new_trace, _trace} -> :ok end)
- |> expect(:broadcast, fn ^expected_ts_f_topic, {:new_trace, _trace} -> :ok end)
+ |> expect(:broadcast, fn ^expected_trace_topic, {:new_trace, _trace} -> :ok end)
assert {:noreply, %{}} = CallbackTracingServer.handle_info(:setup_tracing, %{})
assert_receive handle_trace
diff --git a/test/gen_servers/ets_table_server_test.exs b/test/gen_servers/ets_table_server_test.exs
index a0c91525a..0368d02cb 100644
--- a/test/gen_servers/ets_table_server_test.exs
+++ b/test/gen_servers/ets_table_server_test.exs
@@ -47,10 +47,10 @@ defmodule LiveDebugger.GenServers.EtsTableServerTest do
table_refs = %{pid => ref, other_pid => other_ref}
- topic = PubSubUtils.process_status_topic(pid)
+ topic = PubSubUtils.process_status_topic()
LiveDebugger.MockPubSubUtils
- |> expect(:broadcast, fn ^topic, {:process_status, :dead} -> :ok end)
+ |> expect(:broadcast, fn ^topic, {:process_status, {:dead, _}} -> :ok end)
assert {:noreply, new_table_refs} =
EtsTableServer.handle_info({:DOWN, :_, :process, pid, :_}, table_refs)
diff --git a/test/gen_servers/state_server_test.exs b/test/gen_servers/state_server_test.exs
new file mode 100644
index 000000000..3c2d6877b
--- /dev/null
+++ b/test/gen_servers/state_server_test.exs
@@ -0,0 +1,113 @@
+defmodule LiveDebugger.GenServers.StateServerTest do
+ use ExUnit.Case, async: true
+
+ import Mox
+
+ alias LiveDebugger.Fakes
+ alias LiveDebugger.Utils.PubSub, as: PubSubUtils
+ alias LiveDebugger.GenServers.StateServer
+ alias LiveDebugger.MockPubSubUtils
+ alias LiveDebugger.MockProcessService
+
+ setup :verify_on_exit!
+
+ test "init/1" do
+ node_rendered_topic = PubSubUtils.node_rendered_topic()
+ process_status_topic = PubSubUtils.process_status_topic()
+ component_deleted_topic = PubSubUtils.component_deleted_topic()
+
+ MockPubSubUtils
+ |> expect(:subscribe!, fn ^node_rendered_topic -> :ok end)
+ |> expect(:subscribe!, fn ^component_deleted_topic -> :ok end)
+ |> expect(:subscribe!, fn ^process_status_topic -> :ok end)
+
+ assert {:ok, []} = StateServer.init([])
+
+ assert Enum.find(:ets.all(), false, &(&1 == StateServer.ets_table_name()))
+ end
+
+ test "record_id/1" do
+ pid = self()
+ assert StateServer.record_id(pid) == "#{inspect(pid)}"
+ end
+
+ describe "handle_info/2" do
+ test "handles component deleted trace and updates state" do
+ pid = :c.pid(0, 1, 0)
+ transport_pid = :c.pid(0, 7, 0)
+ socket_id = "socket_id"
+ :ets.new(StateServer.ets_table_name(), [:named_table, :public, :ordered_set])
+ :ets.insert(StateServer.ets_table_name(), {inspect(pid), :old_state})
+
+ trace =
+ Fakes.trace(
+ function: :render,
+ pid: pid,
+ transport_pid: transport_pid,
+ socket_id: socket_id
+ )
+
+ state_changed_node_topic = PubSubUtils.state_changed_topic(socket_id, transport_pid, pid)
+ state_changed_topic = PubSubUtils.state_changed_topic(socket_id, transport_pid)
+
+ state = Fakes.state()
+
+ MockProcessService
+ |> expect(:state, fn ^pid -> {:ok, state} end)
+
+ MockPubSubUtils
+ |> expect(:broadcast, fn ^state_changed_node_topic, {:state_changed, ^state, ^trace} ->
+ :ok
+ end)
+ |> expect(:broadcast, fn ^state_changed_topic, {:state_changed, ^state, ^trace} -> :ok end)
+
+ StateServer.handle_info({:component_deleted, trace}, [])
+
+ assert [{_, ^state}] = :ets.lookup(StateServer.ets_table_name(), inspect(pid))
+ end
+
+ test "handles render trace and updates state" do
+ pid = :c.pid(0, 1, 0)
+ transport_pid = :c.pid(0, 7, 0)
+ socket_id = "socket_id"
+ :ets.new(StateServer.ets_table_name(), [:named_table, :public, :ordered_set])
+ :ets.insert(StateServer.ets_table_name(), {inspect(pid), :old_state})
+
+ trace =
+ Fakes.trace(
+ function: :render,
+ pid: pid,
+ transport_pid: transport_pid,
+ socket_id: socket_id
+ )
+
+ state_changed_node_topic = PubSubUtils.state_changed_topic(socket_id, transport_pid, pid)
+ state_changed_topic = PubSubUtils.state_changed_topic(socket_id, transport_pid)
+
+ state = Fakes.state()
+
+ MockProcessService
+ |> expect(:state, fn ^pid -> {:ok, state} end)
+
+ MockPubSubUtils
+ |> expect(:broadcast, fn ^state_changed_node_topic, {:state_changed, ^state, ^trace} ->
+ :ok
+ end)
+ |> expect(:broadcast, fn ^state_changed_topic, {:state_changed, ^state, ^trace} -> :ok end)
+
+ StateServer.handle_info({:render_trace, trace}, [])
+
+ assert [{_, ^state}] = :ets.lookup(StateServer.ets_table_name(), inspect(pid))
+ end
+
+ test "handles dead process status and deletes table record" do
+ pid = :c.pid(0, 1, 0)
+ :ets.new(StateServer.ets_table_name(), [:named_table, :public, :ordered_set])
+ :ets.insert(StateServer.ets_table_name(), {inspect(pid), :old_state})
+
+ StateServer.handle_info({:process_status, {:dead, pid}}, [])
+
+ assert [] = :ets.lookup(StateServer.ets_table_name(), inspect(pid))
+ end
+ end
+end
diff --git a/test/services/channel_service_test.exs b/test/services/channel_service_test.exs
index 4484d0b35..9be394a02 100644
--- a/test/services/channel_service_test.exs
+++ b/test/services/channel_service_test.exs
@@ -38,27 +38,6 @@ defmodule LiveDebugger.Services.ChannelServiceTest do
}
end
- describe "state/1" do
- test "returns the state of the LiveView channel process identified by pid", %{
- live_view_pid: pid
- } do
- {:ok, state} = ProcessService.state(pid)
- assert {:ok, ^state} = ChannelService.state(pid)
- end
-
- test "returns an error when the process is not a LiveView", %{non_live_view_pid: pid} do
- assert {:error, "PID:" <> _} = ChannelService.state(pid)
- end
-
- test "returns an error when there is no process with the given pid", %{not_alive_pid: pid} do
- assert {:error, :not_alive} = ChannelService.state(pid)
- end
-
- test "returns and error when something went wrong", %{exited_pid: pid} do
- assert {:error, "Could not get state from pid:" <> _} = ChannelService.state(pid)
- end
- end
-
describe "get_node/2" do
test "returns LiveView node with the given id from the channel state when pid is passed", %{
live_view_pid: pid
diff --git a/test/services/live_view_discovery_service_test.exs b/test/services/live_view_discovery_service_test.exs
index 888f994c2..f0314ffb4 100644
--- a/test/services/live_view_discovery_service_test.exs
+++ b/test/services/live_view_discovery_service_test.exs
@@ -5,6 +5,7 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
alias LiveDebugger.Services.LiveViewDiscoveryService
alias LiveDebugger.MockProcessService
+ alias LiveDebugger.MockStateServer
alias LiveDebugger.Structs.LvProcess
alias LiveDebugger.Fakes
@@ -18,10 +19,12 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> expect(:list, fn -> [live_view_pid_1, live_view_pid_2] end)
|> expect(:initial_call, 2, fn _ -> {module, :mount} end)
- |> expect(:state, fn ^live_view_pid_1 ->
+
+ MockStateServer
+ |> expect(:get, fn ^live_view_pid_1 ->
{:ok, Fakes.state(root_pid: live_view_pid_1, module: module)}
end)
- |> expect(:state, fn ^live_view_pid_2 ->
+ |> expect(:get, fn ^live_view_pid_2 ->
{:ok, Fakes.state(root_pid: live_view_pid_2, module: module)}
end)
@@ -42,10 +45,12 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
|> expect(:list, fn -> [live_view_pid, debugger_pid] end)
|> expect(:initial_call, fn _ -> {module, :mount} end)
|> expect(:initial_call, fn _ -> {live_debugger_module, :mount} end)
- |> expect(:state, fn ^live_view_pid ->
+
+ MockStateServer
+ |> expect(:get, fn ^live_view_pid ->
{:ok, Fakes.state(root_pid: live_view_pid, module: module)}
end)
- |> expect(:state, fn ^debugger_pid ->
+ |> expect(:get, fn ^debugger_pid ->
{:ok, Fakes.state(root_pid: debugger_pid, module: live_debugger_module)}
end)
@@ -66,10 +71,12 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
|> expect(:list, fn -> [live_debugger_pid, live_view_pid] end)
|> expect(:initial_call, fn _ -> {live_debugger_module, :mount} end)
|> expect(:initial_call, fn _ -> {live_view_module, :mount} end)
- |> expect(:state, fn ^live_debugger_pid ->
+
+ MockStateServer
+ |> expect(:get, fn ^live_debugger_pid ->
{:ok, Fakes.state(root_pid: live_debugger_pid, module: live_debugger_module)}
end)
- |> expect(:state, fn ^live_view_pid ->
+ |> expect(:get, fn ^live_view_pid ->
{:ok, Fakes.state(root_pid: live_view_pid, module: live_view_module)}
end)
@@ -93,7 +100,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
|> expect(:list, fn -> [searched_live_view_pid, live_view_pid_1, live_view_pid_2] end)
|> expect(:initial_call, fn _ -> {searched_module, :mount} end)
|> expect(:initial_call, 2, fn _ -> {other_module, :mount} end)
- |> expect(:state, fn ^searched_live_view_pid ->
+
+ MockStateServer
+ |> expect(:get, fn ^searched_live_view_pid ->
{:ok,
Fakes.state(
root_pid: searched_live_view_pid,
@@ -101,7 +110,7 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
socket_id: socket_id
)}
end)
- |> expect(:state, 2, fn live_view_pid ->
+ |> expect(:get, 2, fn live_view_pid ->
{:ok,
Fakes.state(root_pid: live_view_pid, module: other_module, socket_id: other_socket_id)}
end)
@@ -117,7 +126,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> expect(:list, fn -> [:c.pid(0, 0, 0)] end)
|> expect(:initial_call, fn _ -> {module, :mount} end)
- |> expect(:state, fn _ -> {:ok, Fakes.state()} end)
+
+ MockStateServer
+ |> expect(:get, fn _ -> {:ok, Fakes.state()} end)
assert LiveViewDiscoveryService.lv_process(bad_socket_id) == nil
end
@@ -129,7 +140,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> expect(:list, fn -> [:c.pid(0, 0, 1), :c.pid(0, 0, 2)] end)
|> expect(:initial_call, 2, fn _ -> {module, :mount} end)
- |> expect(:state, 2, fn _ -> {:ok, Fakes.state()} end)
+
+ MockStateServer
+ |> expect(:get, 2, fn _ -> {:ok, Fakes.state()} end)
assert LiveViewDiscoveryService.lv_process(socket_id) == nil
end
@@ -148,13 +161,15 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
|> expect(:list, fn -> [searched_live_view_pid, live_view_pid_1, live_view_pid_2] end)
|> expect(:initial_call, fn _ -> {searched_module, :mount} end)
|> expect(:initial_call, 2, fn _ -> {other_module, :mount} end)
- |> expect(:state, fn ^searched_live_view_pid ->
+
+ MockStateServer
+ |> expect(:get, fn ^searched_live_view_pid ->
{:ok, Fakes.state(module: searched_module)}
end)
- |> expect(:state, fn _pid ->
+ |> expect(:get, fn _pid ->
{:ok, Fakes.state(module: other_module)}
end)
- |> expect(:state, fn _pid ->
+ |> expect(:get, fn _pid ->
{:ok, Fakes.state(module: other_module)}
end)
@@ -176,21 +191,23 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
|> expect(:list, fn -> [searched_live_view_pid, live_view_pid_1, live_view_pid_2] end)
|> expect(:initial_call, fn _ -> {searched_module, :mount} end)
|> expect(:initial_call, 2, fn _ -> {other_module, :mount} end)
- |> expect(:state, fn ^searched_live_view_pid ->
+
+ MockStateServer
+ |> expect(:get, fn ^searched_live_view_pid ->
{:ok,
Fakes.state(
module: searched_module,
socket_id: searched_socket_id
)}
end)
- |> expect(:state, fn _pid ->
+ |> expect(:get, fn _pid ->
{:ok,
Fakes.state(
module: other_module,
socket_id: other_socket_id
)}
end)
- |> expect(:state, fn _pid ->
+ |> expect(:get, fn _pid ->
{:ok,
Fakes.state(
module: other_module,
@@ -211,7 +228,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> expect(:list, fn -> [live_view_pid_1, live_view_pid_2] end)
|> expect(:initial_call, 2, fn _ -> {:"Elixir.SomeLiveView", :mount} end)
- |> expect(:state, 2, fn _ -> {:ok, Fakes.state()} end)
+
+ MockStateServer
+ |> expect(:get, 2, fn _ -> {:ok, Fakes.state()} end)
assert nil ==
LiveViewDiscoveryService.lv_process(searched_live_view_pid)
@@ -227,7 +246,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> expect(:list, fn -> [live_view_pid_1, live_view_pid_2] end)
|> expect(:initial_call, 2, fn _ -> {:"Elixir.SomeLiveView", :mount} end)
- |> expect(:state, 2, fn _ -> {:ok, Fakes.state(socket_id: other_socket_id)} end)
+
+ MockStateServer
+ |> expect(:get, 2, fn _ -> {:ok, Fakes.state(socket_id: other_socket_id)} end)
assert nil ==
LiveViewDiscoveryService.lv_process(searched_socket_id)
@@ -253,7 +274,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [successor_pid, other_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^successor_pid ->
{:ok,
@@ -299,7 +322,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [successor_pid, other_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^successor_pid ->
{:ok,
@@ -347,7 +372,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [successor_pid, other_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^successor_pid ->
{:ok,
@@ -394,7 +421,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [successor_pid, other_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^successor_pid ->
{:ok,
@@ -441,7 +470,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [successor_pid, other_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^successor_pid ->
{:ok,
@@ -487,7 +518,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [successor_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn ^successor_pid ->
+
+ MockStateServer
+ |> stub(:get, fn ^successor_pid ->
{:ok,
Fakes.state(
pid: successor_pid,
@@ -522,7 +555,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [other_pid_1, other_pid_2] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^other_pid_1 ->
{:ok,
@@ -621,10 +656,12 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
|> expect(:list, fn -> [live_view_pid_1, live_view_pid_2, non_live_view_pid] end)
|> expect(:initial_call, 2, fn _ -> {module, :mount} end)
|> expect(:initial_call, fn _ -> {non_live_view_module, :some_initial_call} end)
- |> expect(:state, fn ^live_view_pid_1 ->
+
+ MockStateServer
+ |> expect(:get, fn ^live_view_pid_1 ->
{:ok, Fakes.state(root_pid: live_view_pid_1, module: module)}
end)
- |> expect(:state, fn ^live_view_pid_2 ->
+ |> expect(:get, fn ^live_view_pid_2 ->
{:ok, Fakes.state(root_pid: live_view_pid_2, module: module)}
end)
@@ -645,7 +682,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [parent_pid, child_pid_1, child_pid_2] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
if pid == parent_pid do
{:ok, Fakes.state(root_pid: parent_pid, module: module, parent_pid: nil)}
else
@@ -670,7 +709,9 @@ defmodule LiveDebugger.Services.LiveViewDiscoveryServiceTest do
MockProcessService
|> stub(:list, fn -> [parent_pid, child_pid_1, child_pid_2, grandchild_pid] end)
|> stub(:initial_call, fn _ -> {module, :mount} end)
- |> stub(:state, fn pid ->
+
+ MockStateServer
+ |> stub(:get, fn pid ->
case pid do
^parent_pid ->
{:ok, Fakes.state(root_pid: parent_pid, module: module, parent_pid: nil)}
diff --git a/test/services/trace_service_test.exs b/test/services/trace_service_test.exs
index 2003ac0ad..e1da1db61 100644
--- a/test/services/trace_service_test.exs
+++ b/test/services/trace_service_test.exs
@@ -3,7 +3,7 @@ defmodule Services.TraceServiceTest do
import Mox
- alias LiveDebugger.Structs.Trace
+ alias LiveDebugger.Fakes
alias LiveDebugger.Services.TraceService
alias LiveDebugger.MockEtsTableServer
@@ -23,7 +23,7 @@ defmodule Services.TraceServiceTest do
end
test "insert/1", %{module: module, pid: pid, table: table} do
- trace = new_trace(1, module, :render, [], pid)
+ trace = Fakes.trace(id: 1, module: module, function: :render, args: [], pid: pid)
MockEtsTableServer
|> expect(:table!, fn ^pid -> table end)
@@ -33,8 +33,8 @@ defmodule Services.TraceServiceTest do
end
test "get/2", %{module: module, pid: pid, table: table} do
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+ trace2 = Fakes.trace(id: 2, module: module, function: :render, args: [], pid: pid)
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
@@ -48,8 +48,8 @@ defmodule Services.TraceServiceTest do
describe "existing_traces/2" do
test "returns traces with default limit", %{module: module, pid: pid, table: table} do
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+ trace2 = Fakes.trace(id: 2, module: module, function: :render, args: [], pid: pid)
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
@@ -60,9 +60,10 @@ defmodule Services.TraceServiceTest do
end
test "returns traces with limit and continuation", %{module: module, pid: pid, table: table} do
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid)
- trace3 = new_trace(3, module, :handle_event, [], pid)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+ trace2 = Fakes.trace(id: 2, module: module, function: :render, args: [], pid: pid)
+ trace3 = Fakes.trace(id: 3, module: module, function: :handle_event, args: [], pid: pid)
+
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
:ets.insert(table, {trace3.id, trace3})
@@ -85,9 +86,9 @@ defmodule Services.TraceServiceTest do
end
test "returns traces with functions filter", %{module: module, pid: pid, table: table} do
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid)
- trace3 = new_trace(3, module, :handle_event, [], pid)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+ trace2 = Fakes.trace(id: 2, module: module, function: :render, args: [], pid: pid)
+ trace3 = Fakes.trace(id: 3, module: module, function: :handle_event, args: [], pid: pid)
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
:ets.insert(table, {trace3.id, trace3})
@@ -103,9 +104,12 @@ defmodule Services.TraceServiceTest do
test "returns traces with node_id filter", %{module: module, pid: pid, table: table} do
cid = %Phoenix.LiveComponent.CID{cid: 3}
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid, cid: cid)
- trace3 = new_trace(3, module, :handle_event, [], pid, cid: cid)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+ trace2 = Fakes.trace(id: 2, module: module, function: :render, args: [], pid: pid, cid: cid)
+
+ trace3 =
+ Fakes.trace(id: 3, module: module, function: :handle_event, args: [], pid: pid, cid: cid)
+
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
:ets.insert(table, {trace3.id, trace3})
@@ -118,8 +122,8 @@ defmodule Services.TraceServiceTest do
end
test "returns :end_of_table when no traces match", %{module: module, pid: pid, table: table} do
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+ trace2 = Fakes.trace(id: 2, module: module, function: :render, args: [], pid: pid)
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
@@ -130,8 +134,18 @@ defmodule Services.TraceServiceTest do
end
test "returns only finished traces", %{module: module, pid: pid, table: table} do
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid, execution_time: nil)
+ trace1 = Fakes.trace(id: 1, module: module, function: :handle_info, args: [], pid: pid)
+
+ trace2 =
+ Fakes.trace(
+ id: 2,
+ module: module,
+ function: :render,
+ args: [],
+ pid: pid,
+ execution_time: nil
+ )
+
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
@@ -145,8 +159,26 @@ defmodule Services.TraceServiceTest do
describe "clear_traces/2" do
test "clears traces for LiveView or LiveComponent", %{module: module, pid: pid, table: table} do
cid = %Phoenix.LiveComponent.CID{cid: 3}
- trace1 = new_trace(1, module, :handle_info, [], pid)
- trace2 = new_trace(2, module, :render, [], pid, cid: cid)
+
+ trace1 =
+ Fakes.trace(
+ id: 1,
+ module: module,
+ function: :handle_info,
+ args: [],
+ pid: pid
+ )
+
+ trace2 =
+ Fakes.trace(
+ id: 2,
+ module: module,
+ function: :render,
+ args: [],
+ pid: pid,
+ cid: cid
+ )
+
:ets.insert(table, {trace1.id, trace1})
:ets.insert(table, {trace2.id, trace2})
@@ -164,18 +196,4 @@ defmodule Services.TraceServiceTest do
assert :end_of_table = TraceService.existing_traces(pid)
end
end
-
- defp new_trace(id, module, function, args, pid, opts \\ []) do
- %Trace{
- id: id,
- module: module,
- function: function,
- arity: length(args),
- args: args,
- pid: pid,
- cid: Keyword.get(opts, :cid, nil),
- timestamp: 1,
- execution_time: Keyword.get(opts, :execution_time, 1)
- }
- end
end
diff --git a/test/structs/lv_process_test.exs b/test/structs/lv_process_test.exs
index 410f5b4e2..8a07f7787 100644
--- a/test/structs/lv_process_test.exs
+++ b/test/structs/lv_process_test.exs
@@ -36,9 +36,9 @@ defmodule LiveDebugger.Structs.LvProcessTest do
describe "new/1" do
test "returns nil if the process is not found" do
- LiveDebugger.MockProcessService
- |> expect(:state, fn _pid ->
- {:error, :not_alive}
+ LiveDebugger.MockStateServer
+ |> expect(:get, fn _pid ->
+ {:error, :not_found}
end)
assert LvProcess.new(self()) == nil
@@ -52,8 +52,8 @@ defmodule LiveDebugger.Structs.LvProcessTest do
transport_pid = nil
module = LiveDebuggerTest.TestView
- LiveDebugger.MockProcessService
- |> expect(:state, fn _pid ->
+ LiveDebugger.MockStateServer
+ |> expect(:get, fn _pid ->
{:ok,
LiveDebugger.Fakes.state(
socket_id: socket_id,
@@ -85,8 +85,8 @@ defmodule LiveDebugger.Structs.LvProcessTest do
transport_pid = :c.pid(0, 7, 0)
module = LiveDebuggerTest.TestView
- LiveDebugger.MockProcessService
- |> expect(:state, fn _pid ->
+ LiveDebugger.MockStateServer
+ |> expect(:get, fn _pid ->
{:ok,
LiveDebugger.Fakes.state(
socket_id: socket_id,
@@ -119,8 +119,8 @@ defmodule LiveDebugger.Structs.LvProcessTest do
transport_pid = nil
module = LiveDebugger.TestView
- LiveDebugger.MockProcessService
- |> expect(:state, fn _pid ->
+ LiveDebugger.MockStateServer
+ |> expect(:get, fn _pid ->
{:ok,
LiveDebugger.Fakes.state(
socket_id: socket_id,
@@ -152,8 +152,8 @@ defmodule LiveDebugger.Structs.LvProcessTest do
transport_pid = nil
module = LiveDebuggerTest.TestView
- LiveDebugger.MockProcessService
- |> expect(:state, fn _pid ->
+ LiveDebugger.MockStateServer
+ |> expect(:get, fn _pid ->
{:ok,
LiveDebugger.Fakes.state(
socket_id: socket_id,
@@ -187,8 +187,8 @@ defmodule LiveDebugger.Structs.LvProcessTest do
transport_pid = nil
module = LiveDebuggerTest.TestView
- LiveDebugger.MockProcessService
- |> expect(:state, fn ^parent_pid ->
+ LiveDebugger.MockStateServer
+ |> expect(:get, fn ^parent_pid ->
{:ok,
LiveDebugger.Fakes.state(
socket_id: socket_id,
diff --git a/test/structs/trace_display_test.exs b/test/structs/trace_display_test.exs
index ab95602b6..2b0b2bc9c 100644
--- a/test/structs/trace_display_test.exs
+++ b/test/structs/trace_display_test.exs
@@ -2,23 +2,10 @@ defmodule LiveDebugger.Structs.TraceDisplayTest do
use ExUnit.Case, async: true
alias LiveDebugger.Structs.TraceDisplay
- alias LiveDebugger.Structs.Trace
-
- @trace %Trace{
- id: 1,
- module: LiveDebuggerTest.TestView,
- function: :handle_event,
- arity: 3,
- args: ["event", %{"key" => "value"}, %{}],
- socket_id: "socket_id",
- transport_pid: self(),
- pid: self(),
- cid: nil,
- timestamp: System.system_time(:millisecond)
- }
+ alias LiveDebugger.Fakes
test "from_trace/1 creates a TraceDisplay struct" do
- trace = @trace
+ trace = Fakes.trace()
trace_display = TraceDisplay.from_trace(trace)
assert %TraceDisplay{id: 1, trace: ^trace, render_body?: false} = trace_display
@@ -27,7 +14,7 @@ defmodule LiveDebugger.Structs.TraceDisplayTest do
test "render_body/1 sets render_body? to true" do
trace_display = %TraceDisplay{
id: 1,
- trace: @trace,
+ trace: Fakes.trace(),
render_body?: false,
counter: 0
}
diff --git a/test/support/fakes.ex b/test/support/fakes.ex
index 95a65e5fd..9afbd0355 100644
--- a/test/support/fakes.ex
+++ b/test/support/fakes.ex
@@ -1,8 +1,26 @@
defmodule LiveDebugger.Fakes do
@moduledoc """
- Fake responses from internal services
+ Fake complex structures
"""
+ def trace(opts \\ []) do
+ default = [
+ id: 1,
+ module: LiveDebuggerTest.LiveView,
+ function: :render,
+ arity: 1,
+ args: [%{socket_id: "socket_id"}],
+ socket_id: "socket_id",
+ pid: :c.pid(0, 1, 0),
+ timestamp: :erlang.timestamp(),
+ execution_time: 1
+ ]
+
+ fields = Keyword.merge(default, opts)
+
+ Kernel.struct!(LiveDebugger.Structs.Trace, fields)
+ end
+
def state(opts \\ []) do
socket_id = Keyword.get(opts, :socket_id, "phx-GBsi_6M7paYhySQj")
parent_pid = Keyword.get(opts, :parent_pid, nil)
diff --git a/test/test_helper.exs b/test/test_helper.exs
index d713bffd6..5767f760b 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -20,6 +20,9 @@ else
Mox.defmock(LiveDebugger.MockDbg, for: LiveDebugger.Services.System.DbgService)
Application.put_env(:live_debugger, :dbg_service, LiveDebugger.MockDbg)
+
+ Mox.defmock(LiveDebugger.MockStateServer, for: LiveDebugger.GenServers.StateServer)
+ Application.put_env(:live_debugger, :state_server, LiveDebugger.MockStateServer)
end
ExUnit.start()
diff --git a/test/utils/pubsub_test.exs b/test/utils/pubsub_test.exs
index 4a01c61b5..4a661dc8b 100644
--- a/test/utils/pubsub_test.exs
+++ b/test/utils/pubsub_test.exs
@@ -3,7 +3,6 @@ defmodule LiveDebugger.Utils.PubSubTest do
import Mox
- alias LiveDebugger.Structs.Trace
alias LiveDebugger.Utils.PubSub, as: PubSubUtils
alias LiveDebugger.MockPubSubUtils
@@ -12,49 +11,29 @@ defmodule LiveDebugger.Utils.PubSubTest do
PubSubUtils.node_changed_topic("phx-GBsi_6M7paYhySQj")
end
- test "component_deleted_topic/1" do
- trace = %Trace{socket_id: "phx-GBsi_6M7paYhySQj", transport_pid: :c.pid(0, 1, 0)}
-
- assert "lvdbg/#PID<0.1.0>/phx-GBsi_6M7paYhySQj/component_deleted" =
- PubSubUtils.component_deleted_topic(trace)
- end
-
- test "component_deleted_topic/2" do
- socket_id = "phx-GBsi_6M7paYhySQj"
- transport_pid = :c.pid(0, 1, 0)
-
- assert "lvdbg/#PID<0.1.0>/phx-GBsi_6M7paYhySQj/component_deleted" =
- PubSubUtils.component_deleted_topic(socket_id, transport_pid)
+ test "component_deleted_topic/0" do
+ assert "lvdbg/component_deleted" =
+ PubSubUtils.component_deleted_topic()
end
- test "process_status_topic/1" do
- pid = :c.pid(0, 1, 0)
- assert "lvdbg/#PID<0.1.0>/status" = PubSubUtils.process_status_topic(pid)
+ test "process_status_topic/0" do
+ assert "lvdbg/process_status" = PubSubUtils.process_status_topic()
end
- test "tsnf_topic/4" do
+ test "trace_topic/4" do
socket_id = "phx-GBsi_6M7paYhySQj"
transport_pid = :c.pid(0, 1, 0)
node_id = :c.pid(0, 2, 0)
fun = :handle_info
assert "#PID<0.1.0>/phx-GBsi_6M7paYhySQj/#PID<0.2.0>/:handle_info/:call" =
- PubSubUtils.tsnf_topic(socket_id, transport_pid, node_id, fun)
+ PubSubUtils.trace_topic(socket_id, transport_pid, node_id, fun)
assert "#PID<0.1.0>/phx-GBsi_6M7paYhySQj/#PID<0.2.0>/:handle_info/:call" =
- PubSubUtils.tsnf_topic(socket_id, transport_pid, node_id, fun, :call)
+ PubSubUtils.trace_topic(socket_id, transport_pid, node_id, fun, :call)
assert "#PID<0.1.0>/phx-GBsi_6M7paYhySQj/#PID<0.2.0>/:handle_info/:return" =
- PubSubUtils.tsnf_topic(socket_id, transport_pid, node_id, fun, :return)
- end
-
- test "ts_f_topic/3" do
- socket_id = "phx-GBsi_6M7paYhySQj"
- transport_pid = :c.pid(0, 1, 0)
- fun = :handle_info
-
- assert "#PID<0.1.0>/phx-GBsi_6M7paYhySQj/*/:handle_info" =
- PubSubUtils.ts_f_topic(socket_id, transport_pid, fun)
+ PubSubUtils.trace_topic(socket_id, transport_pid, node_id, fun, :return)
end
describe "mock" do