Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Copy fails with umbrella mix test stale (make Mimic.copy idempotent) #47

Merged
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
38 changes: 15 additions & 23 deletions lib/mimic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -344,42 +344,32 @@ defmodule Mimic do
@doc """
Prepare `module` for mocking.

Ideally, don't call this function twice for the same module, but in case you do, this function
is idempotent. It will not delete any `stub`s or `expect`s that you've set up.

## Arguments:

* `module` - the name of the module to copy.

"""
@spec copy(module()) :: :ok | no_return
def copy(module) do
with {:module, module} <- Code.ensure_compiled(module),
with :ok <- ensure_module_not_copied(module),
{:module, module} <- Code.ensure_compiled(module),
:ok <- Mimic.Server.mark_to_copy(module) do
ExUnit.after_suite(fn _ -> Mimic.Server.reset(module) end)
:ok
else
{:error, :module_already_copied} ->
:ok

{:error, reason}
when reason in [:embedded, :badfile, :nofile, :on_load_failure, :unavailable] ->
raise ArgumentError, "Module #{inspect(module)} is not available"

error ->
validate_server_response(error, :copy)
end

# case Code.ensure_compiled(module) do
# {:error, _} ->
# raise ArgumentError,
# "Module #{inspect(module)} is not available"

# {:module, module} ->
# case Mimic.Server.mark_to_copy(module) do
# :ok ->
# ExUnit.after_suite(fn _ -> Mimic.Server.reset(module) end)

# error ->
# validate_server_response(error, :copy)
# end

# :ok
# end
end

@doc """
Expand Down Expand Up @@ -465,6 +455,13 @@ defmodule Mimic do
Server.get_mode()
end

defp ensure_module_not_copied(module) do
case Server.marked_to_copy?(module) do
false -> :ok
true -> {:error, :module_already_copied}
end
end

defp raise_if_not_exported_function!(module, fn_name, arity) do
unless function_exported?(module, fn_name, arity) do
raise ArgumentError, "Function #{fn_name}/#{arity} not defined for #{inspect(module)}"
Expand Down Expand Up @@ -502,11 +499,6 @@ defmodule Mimic do
"Module #{inspect(module)} has not been copied. See docs for Mimic.copy/1"
end

defp validate_server_response({:error, {:module_already_copied, module}}, :copy) do
raise ArgumentError,
"Module #{inspect(module)} has already been copied. See docs for Mimic.copy/1"
end

defp validate_server_response(_, :copy) do
raise ArgumentError,
"Failed to copy module. See docs for Mimic.copy/1"
Expand Down
15 changes: 14 additions & 1 deletion lib/mimic/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ defmodule Mimic.Server do
GenServer.call(__MODULE__, {:mark_to_copy, module}, @long_timeout)
end

@spec marked_to_copy?(module) :: boolean
def marked_to_copy?(module) do
GenServer.call(__MODULE__, {:marked_to_copy?, module}, @long_timeout)
end

def apply(module, fn_name, args) do
arity = Enum.count(args)
original_module = Mimic.Module.original(module)
Expand Down Expand Up @@ -453,8 +458,12 @@ defmodule Mimic.Server do
end
end

def handle_call({:marked_to_copy?, module}, _from, state) do
{:reply, marked_to_copy?(module, state), state}
end

def handle_call({:mark_to_copy, module}, _from, state) do
if MapSet.member?(state.modules_to_be_copied, module) do
if marked_to_copy?(module, state) do
{:reply, {:error, {:module_already_copied, module}}, state}
else
# If cover is enabled call ensure_module_copied now
Expand All @@ -475,6 +484,10 @@ defmodule Mimic.Server do
end
end

defp marked_to_copy?(module, state) do
MapSet.member?(state.modules_to_be_copied, module)
end

defp do_reset(module, state) do
case state.modules_beam[module] do
{beam, coverdata} -> Cover.clear_module_and_import_coverdata!(module, beam, coverdata)
Expand Down
24 changes: 19 additions & 5 deletions test/mimic_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -908,11 +908,25 @@ defmodule Mimic.Test do
end
end

describe "copy/1" do
test "copying the same module raises" do
assert_raise ArgumentError,
"Module Calculator has already been copied. See docs for Mimic.copy/1",
fn -> Mimic.copy(Calculator) end
describe "copy/1 with duplicates does nothing" do
setup :set_mimic_private

test "stubs still stub" do
parent_pid = self()

Mimic.copy(Calculator)
Mimic.copy(Calculator)

Calculator
|> stub(:add, fn x, y ->
send(parent_pid, {:add, x, y})
:stubbed
end)

Mimic.copy(Calculator)

assert Calculator.add(1, 2) == :stubbed
assert_receive {:add, 1, 2}
end
end
end