Skip to content

Commit

Permalink
Adding unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagoesteves committed Dec 2, 2024
1 parent a20a7c0 commit c1049e0
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 13 deletions.
13 changes: 11 additions & 2 deletions lib/deployex/tracer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ defmodule Deployex.Tracer do
def get_modules(node \\ Node.self()) do
:rpc.call(node, :code, :all_loaded, [], :infinity)
|> Enum.map(fn
{module, _} -> module
module -> module
{module, _} ->
module

# coveralls-ignore-start
module ->
module

# coveralls-ignore-stop
end)
end

Expand Down Expand Up @@ -117,6 +123,7 @@ defmodule Deployex.Tracer do
### ==========================================================================
### Private functions
### ==========================================================================
# coveralls-ignore-start
defp regular_functions?(function) do
String.contains?(function, "-anonymous-") or
String.contains?(function, "-fun-") or
Expand All @@ -125,4 +132,6 @@ defmodule Deployex.Tracer do
String.contains?(function, "-lc") or
String.contains?(function, "-lbc")
end

# coveralls-ignore-stop
end
29 changes: 18 additions & 11 deletions lib/deployex/tracer/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ defmodule Deployex.Tracer.Server do
end

@impl true
def handle_call(
{:start_trace, _functions, _session_timeout_ms},
_from,
%DeployexT{status: :running} = state
) do
{:reply, {:error, :already_started}, state}
end

def handle_call({:stop_tracing, rcv_session_id}, _from, %DeployexT{
session_id: session_id
})
Expand All @@ -56,6 +48,14 @@ defmodule Deployex.Tracer.Server do
{:reply, state, state}
end

def handle_call(
{:start_trace, _new_state},
_from,
%DeployexT{status: :running} = state
) do
{:reply, {:error, :already_started}, state}
end

# credo:disable-for-lines:1
def handle_call(
{:start_trace,
Expand All @@ -71,9 +71,7 @@ defmodule Deployex.Tracer.Server do
) do
Process.monitor(request_pid)

Logger.info(
"New trace requested session: #{session_id} functions: #{inspect(functions_by_node)}"
)
Logger.info("New Trace Session: #{session_id} functions: #{inspect(functions_by_node)}")

tracer_pid = self()
# The local node (deployex) is always present in the trace list of nodes.
Expand All @@ -96,10 +94,13 @@ defmodule Deployex.Tracer.Server do

Enum.each(functions_by_node, fn {node, functions} ->
# Add node to tracing process (exclude local node since it is added by default)
# coveralls-ignore-start
if node != Node.self() do
:dbg.n(node)
end

# coveralls-ignore-stop

# Add functions to be traced
# credo:disable-for-lines:12
Enum.each(functions, fn function ->
Expand All @@ -108,7 +109,9 @@ defmodule Deployex.Tracer.Server do
atom_spec = String.to_existing_atom(spec)

case Map.get(default_functions_matchspecs, atom_spec) do
# coveralls-ignore-start
nil -> acc
# coveralls-ignore-stop
%{pattern: pattern} -> acc ++ pattern
end
end)
Expand Down Expand Up @@ -226,7 +229,9 @@ defmodule Deployex.Tracer.Server do

def handle_trace(_trace_message, {%{max_messages: max_messages} = session_info, index})
when index > max_messages do
# coveralls-ignore-start
:dbg.stop()
# coveralls-ignore-stop
{session_info, index}
end

Expand Down Expand Up @@ -268,12 +273,14 @@ defmodule Deployex.Tracer.Server do
{pid, type,
"[#{y}-#{mm}-#{d} #{h}:#{m}:#{s}] (#{inspect(pid)}) #{inspect(module)}.#{fun}/#{arity}}) exception_value: #{inspect(exception_value)}"}

# coveralls-ignore-start
trace_msg ->
Logger.warning(
"Not able to decode trace_mg: #{inspect(trace_msg)} session_index: #{inspect(index)}"
)

{nil, nil, nil}
# coveralls-ignore-stop
end

node = origin_pid && :erlang.node(origin_pid)
Expand Down
235 changes: 235 additions & 0 deletions test/deployex/tracer_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
defmodule Deployex.TracerTest do
use ExUnit.Case, async: true

import Mox

alias Deployex.Tracer, as: DeployexT
alias Deployex.TracerFixtures

setup :verify_on_exit!

test "get_modules/1" do
assert list = DeployexT.get_modules(Node.self())
assert Enum.member?(list, :kernel)
end

test "get_module_functions_info/2" do
assert %{
functions: %{"config_change/3" => %{arity: 3, name: :config_change}},
module: :kernel,
node: :nonode@nohost
} = DeployexT.get_module_functions_info(Node.self(), :kernel)

assert %{
functions: _,
module: :erlang,
node: :nonode@nohost
} = DeployexT.get_module_functions_info(Node.self(), :erlang)
end

test "get_default_functions_matchspecs/0" do
assert %{
return_trace: %{pattern: [{:_, [], [{:return_trace}]}]},
exception_trace: %{pattern: [{:_, [], [{:exception_trace}]}]},
caller: %{pattern: [{:_, [], [{:message, {:caller}}]}]},
process_dump: %{pattern: [{:_, [], [{:message, {:process_dump}}]}]}
} = DeployexT.get_default_functions_matchspecs()
end

describe "start_trace/2" do
test "Only one session is allowed for Tracer" do
functions = [
%{
arity: :_,
function: :_,
match_spec: [],
module: TracerFixtures,
node: Node.self()
}
]

assert {:ok, %{session_id: session_id}} =
DeployexT.start_trace(functions, %{max_messages: 1})

assert {:error, :already_started} =
DeployexT.start_trace(functions, %{max_messages: 1})

assert :ok == DeployexT.stop_trace(session_id)

:timer.sleep(50)
end

test "Success requesting only module" do
node = Node.self()

functions = [
%{
arity: :_,
function: :_,
match_spec: [],
module: TracerFixtures,
node: node
}
]

assert {:ok, %{session_id: session_id} = state} =
DeployexT.start_trace(functions, %{max_messages: 1})

assert state == DeployexT.state()

TracerFixtures.testing_fun([true, true, true])

assert_receive {:new_trace_message, ^session_id, ^node, _index, :call, msg}, 1_000

assert msg =~ "TracerFixtures"

assert :ok == DeployexT.stop_trace(session_id)

:timer.sleep(50)
end

test "Success requesting module/function" do
node = Node.self()

functions = [
%{
arity: 2,
function: :testing_adding_fun,
match_spec: [],
module: TracerFixtures,
node: node
}
]

assert {:ok, %{session_id: session_id}} =
DeployexT.start_trace(functions, %{max_messages: 1})

TracerFixtures.testing_adding_fun(50, 50)

assert_receive {:new_trace_message, ^session_id, ^node, _index, :call, msg}, 1_000

assert msg =~ "TracerFixtures"
assert msg =~ "testing_adding_fun"

assert :ok == DeployexT.stop_trace(session_id)

:timer.sleep(50)
end

test "Success requesting module/function with match_spec [return_trace]" do
node = Node.self()

functions = [
%{
arity: 2,
function: :testing_adding_fun,
match_spec: ["return_trace"],
module: TracerFixtures,
node: node
}
]

assert {:ok, %{session_id: session_id}} =
DeployexT.start_trace(functions, %{max_messages: 2})

TracerFixtures.testing_adding_fun(253, 200)

assert_receive {:new_trace_message, ^session_id, ^node, _index, :return_from, msg}, 1_000

assert msg =~ "TracerFixtures"
assert msg =~ "testing_adding_fun"
assert msg =~ "453"

assert :ok == DeployexT.stop_trace(session_id)

:timer.sleep(50)
end

test "Success requesting module/function with match_spec [caller]" do
node = Node.self()

functions = [
%{
arity: 2,
function: :testing_adding_fun,
match_spec: ["caller"],
module: TracerFixtures,
node: node
}
]

assert {:ok, %{session_id: session_id}} =
DeployexT.start_trace(functions, %{max_messages: 2})

TracerFixtures.testing_adding_fun(255, 200)

assert_receive {:new_trace_message, ^session_id, ^node, _index, :call, msg}, 1_000

assert msg =~ "TracerFixtures"
assert msg =~ "testing_adding_fun"
assert msg =~ "caller: {Deployex.TracerTest"

assert :ok == DeployexT.stop_trace(session_id)

:timer.sleep(50)
end

@tag :capture_log
test "Success requesting module/function with match_spec [exception_trace]" do
node = Node.self()

functions = [
%{
arity: 1,
function: :testing_exception_fun,
match_spec: ["exception_trace"],
module: TracerFixtures,
node: node
}
]

assert {:ok, %{session_id: session_id}} =
DeployexT.start_trace(functions, %{max_messages: 2})

spawn(fn ->
TracerFixtures.testing_exception_fun(0)
end)

assert_receive {:new_trace_message, ^session_id, ^node, _index, :exception_from, msg}, 1_000

assert msg =~ "TracerFixtures"
assert msg =~ "testing_exception_fun"
assert msg =~ "exception_value: {:error, :badarith}"

assert :ok == DeployexT.stop_trace(session_id)

:timer.sleep(50)
end
end

describe "stop_trace/2" do
test "Ignore stop_trace with invalid session_id" do
functions = [
%{
arity: :_,
function: :_,
match_spec: [],
module: TracerFixtures,
node: Node.self()
}
]

assert {:ok, %{session_id: session_id}} =
DeployexT.start_trace(functions, %{max_messages: 1})

assert %DeployexT{status: :running} = DeployexT.state()

assert :ok == DeployexT.stop_trace("123456789")

assert :ok == DeployexT.stop_trace(session_id)

assert %DeployexT{status: :idle} = DeployexT.state()
:timer.sleep(50)
end
end
end
21 changes: 21 additions & 0 deletions test/support/fixtures/tracer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule Deployex.TracerFixtures do
@moduledoc """
This module will handle the tracer fixture
"""

def testing_fun(_arg1) do
:ok
end

def testing_adding_fun(arg1, arg2) do
arg1 + arg2
end

def testing_caller_fun(arg1, arg2) do
testing_adding_fun(arg1, arg2)
end

def testing_exception_fun(arg) do
1 / arg
end
end

0 comments on commit c1049e0

Please sign in to comment.