Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions lib/live_debugger_refactor/api/system/process.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
defmodule LiveDebuggerRefactor.API.System.Process do
@moduledoc """
This module provides wrappers for system functions that queries processes in the current application.
"""
@callback initial_call(pid :: pid()) :: {:ok, mfa()} | {:error, term()}
@callback state(pid :: pid()) :: {:ok, term()} | {:error, term()}
@callback list() :: [pid()]

@doc """
Wrapper for `Process.info/2` with some additional logic that returns the initial call of the process.
"""
@spec initial_call(pid :: pid()) :: {:ok, mfa()} | {:error, term()}
def initial_call(pid), do: impl().initial_call(pid)

@doc """
Wrapper for `:sys.get_state/1` with additional error handling that returns the state of the process.
"""
@spec state(pid :: pid()) :: {:ok, term()} | {:error, term()}
def state(pid), do: impl().state(pid)

@doc """
Wrapper for `Process.list/0` that returns a list of pids.
"""
@spec list() :: [pid()]
def list(), do: impl().list()

defp impl() do
Application.get_env(
:live_debugger,
:api_process,
__MODULE__.Impl
)
end

defmodule Impl do
@moduledoc false
@behaviour LiveDebuggerRefactor.API.System.Process

@impl true
def initial_call(pid) do
pid
|> Process.info([:dictionary])
|> case do
nil ->
{:error, :not_alive}

result ->
case get_in(result, [:dictionary, :"$initial_call"]) do
nil -> {:error, :no_initial_call}
initial_call -> {:ok, initial_call}
end
end
end

@impl true
def state(pid) do
try do
if Process.alive?(pid) do
{:ok, :sys.get_state(pid)}
else
{:error, :not_alive}
end
catch
:exit, reason ->
{:error, reason}
end
end

@impl true
def list(), do: Process.list()
end
end
57 changes: 57 additions & 0 deletions test/live_debugger_refactor/api/system/process_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule LiveDebuggerRefactor.API.System.ProcessImplTest do
use ExUnit.Case, async: true

alias LiveDebuggerRefactor.API.System.Process.Impl, as: ProcessImpl

defmodule TestServer do
use GenServer

def start_link(opts) do
GenServer.start_link(__MODULE__, opts)
end

def init(_opts) do
{:ok, %{number: 14}}
end
end

setup_all do
alive_pid = start_supervised!(TestServer, id: TestServerAlive)
dead_pid = start_supervised!(TestServer, id: TestServerDead)

:ok = stop_supervised!(TestServerDead)

%{alive_pid: alive_pid, dead_pid: dead_pid}
end

def task_func do
IO.puts("task running ...")
Process.sleep(:inifnity)
end

describe "initial_call/1" do
test "returns $initial_call for a live process", %{alive_pid: alive_pid} do
assert {:ok, {TestServer, :init, 1}} = ProcessImpl.initial_call(alive_pid)
end

test "returns :error for a dead process", %{dead_pid: dead_pid} do
assert Process.alive?(dead_pid) == false
assert {:error, :not_alive} = ProcessImpl.initial_call(dead_pid)
end

test "returns :error for a process with no initial call" do
pid = spawn(fn -> Process.sleep(:infinity) end)
assert {:error, :no_initial_call} = ProcessImpl.initial_call(pid)
end
end

describe "state/1" do
test "returns state of for a live process", %{alive_pid: alive_pid} do
assert {:ok, %{number: 14}} = ProcessImpl.state(alive_pid)
end

test "returns :error for a dead process", %{dead_pid: dead_pid} do
assert {:error, :not_alive} = ProcessImpl.state(dead_pid)
end
end
end
3 changes: 3 additions & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ else

Mox.defmock(LiveDebuggerRefactor.MockAPIModule, for: LiveDebuggerRefactor.API.System.Module)
Application.put_env(:live_debugger, :api_module, LiveDebuggerRefactor.MockAPIModule)

Mox.defmock(LiveDebuggerRefactor.MockAPIProcess, for: LiveDebuggerRefactor.API.System.Process)
Application.put_env(:live_debugger, :api_process, LiveDebuggerRefactor.MockAPIProcess)
end

ExUnit.start()