diff --git a/lib/membrane/testing/assertions.ex b/lib/membrane/testing/assertions.ex index 8a51853c3..e37a95f3d 100644 --- a/lib/membrane/testing/assertions.ex +++ b/lib/membrane/testing/assertions.ex @@ -9,13 +9,13 @@ defmodule Membrane.Testing.Assertions do @default_timeout 2000 - defp do_assert_receive_from_pipeline(assertion, pid, pattern, timeout, failure_message) do + defp assert_receive_from_entity(assertion, entity, pid, pattern, timeout, failure_message) do quote do import ExUnit.Assertions pid_value = unquote(pid) unquote(assertion)( - {Membrane.Testing.Pipeline, ^pid_value, unquote(pattern)}, + {unquote(entity), ^pid_value, unquote(pattern)}, unquote(timeout), unquote(failure_message) ) @@ -23,11 +23,36 @@ defmodule Membrane.Testing.Assertions do end defp assert_receive_from_pipeline(pid, pattern, timeout, failure_message \\ nil) do - do_assert_receive_from_pipeline(:assert_receive, pid, pattern, timeout, failure_message) + assert_receive_from_entity( + :assert_receive, + Membrane.Testing.Pipeline, + pid, + pattern, + timeout, + failure_message + ) end defp refute_receive_from_pipeline(pid, pattern, timeout, failure_message \\ nil) do - do_assert_receive_from_pipeline(:refute_receive, pid, pattern, timeout, failure_message) + assert_receive_from_entity( + :refute_receive, + Membrane.Testing.Pipeline, + pid, + pattern, + timeout, + failure_message + ) + end + + defp assert_receive_from_resource_guard(pid, pattern, timeout, failure_message \\ nil) do + assert_receive_from_entity( + :assert_receive, + Membrane.Testing.MockResourceGuard, + pid, + pattern, + timeout, + failure_message + ) end @doc """ @@ -423,4 +448,50 @@ defmodule Membrane.Testing.Assertions do timeout ) end + + @doc """ + Asserts that a cleanup function was registered in `Membrane.Testing.MockResourceGuard`. + """ + defmacro assert_resource_guard_register( + mock_guard, + function, + tag, + timeout \\ @default_timeout + ) do + assert_receive_from_resource_guard( + mock_guard, + {:register, {function, tag}}, + timeout + ) + end + + @doc """ + Asserts that a tag was unregistered in `Membrane.Testing.MockResourceGuard`. + """ + defmacro assert_resource_guard_unregister( + mock_guard, + tag, + timeout \\ @default_timeout + ) do + assert_receive_from_resource_guard( + mock_guard, + {:cleanup, tag}, + timeout + ) + end + + @doc """ + Asserts that `Membrane.Testing.MockResourceGuard` was requested to cleanup a given tag. + """ + defmacro assert_resource_guard_cleanup( + mock_guard, + tag, + timeout \\ @default_timeout + ) do + assert_receive_from_resource_guard( + mock_guard, + {:cleanup, tag}, + timeout + ) + end end diff --git a/lib/membrane/testing/mock_resource_guard.ex b/lib/membrane/testing/mock_resource_guard.ex new file mode 100644 index 000000000..f7330d97e --- /dev/null +++ b/lib/membrane/testing/mock_resource_guard.ex @@ -0,0 +1,73 @@ +defmodule Membrane.Testing.MockResourceGuard do + @moduledoc """ + Mock for `Membrane.ResourceGuard`. + + Informs the test process about registered cleanup functions and tags. + Works with `Membrane.Testing.Assertions`, for example + `Membrane.Testing.Assertions.assert_resource_guard_register/4`: + + iex> guard = #{inspect(__MODULE__)}.start_link_supervised!() + ...> Membrane.ResourceGuard.register(guard, fn -> :abc end, tag: :some_tag) + ...> import Membrane.Testing.Assertions + ...> assert_resource_guard_register(guard, function, :some_tag) + ...> function.() + :abc + + """ + use GenServer + + require Membrane.Core.Message, as: Message + + @type options :: [test_process: pid] + + @spec child_spec(test_process: pid()) :: Supervisor.child_spec() + def child_spec(options) do + super(options) |> Map.merge(%{restart: :transient, id: {__MODULE__, make_ref()}}) + end + + @spec start_link(options) :: {:ok, pid} + def start_link(options \\ []) do + options = Keyword.put_new(options, :test_process, self()) + GenServer.start_link(__MODULE__, options) + end + + @spec start_link_supervised!(options) :: pid + def start_link_supervised!(options \\ []) do + options = Keyword.put_new(options, :test_process, self()) + {:ok, pid} = ex_unit_start_supervised({__MODULE__, options}) + pid + end + + @impl true + def init(options) do + {:ok, Map.new(options)} + end + + @impl true + def handle_info(Message.new(:register, [function, options]), state) do + tag = Keyword.fetch!(options, :tag) + send_to_test_process(state, :register, {function, tag}) + {:noreply, state} + end + + @impl true + def handle_info(Message.new(:unregister, tag), state) do + send_to_test_process(state, :unregister, tag) + {:noreply, state} + end + + @impl true + def handle_info(Message.new(:cleanup, tag), state) do + send_to_test_process(state, :cleanup, tag) + {:noreply, state} + end + + defp ex_unit_start_supervised(child_spec) do + # It's not a 'normal' call to keep dialyzer quiet + apply(ExUnit.Callbacks, :start_supervised, [child_spec]) + end + + defp send_to_test_process(%{test_process: test_process}, type, args) do + send(test_process, {__MODULE__, self(), {type, args}}) + end +end diff --git a/mix.exs b/mix.exs index 6cb4290ee..3fee91580 100644 --- a/mix.exs +++ b/mix.exs @@ -138,7 +138,7 @@ defmodule Membrane.Mixfile do [ {:qex, "~> 0.3"}, {:telemetry, "~> 1.0"}, - {:bunch, github: "membraneframework/bunch", branch: "config-require"}, + {:bunch, github: "membraneframework/bunch"}, {:ratio, "~> 2.0"}, # Development {:ex_doc, "~> 0.28", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index 0ff2b8fda..091833d77 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "bunch": {:git, "https://github.com/membraneframework/bunch.git", "d86e7ca7d26e434d9c5d28b6a1cb662eaa48ac5f", [branch: "config-require"]}, + "bunch": {:git, "https://github.com/membraneframework/bunch.git", "179707e46972afee21c8e9b57805df4a9b66d708", []}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, diff --git a/test/membrane/testing/mock_resource_guard_test.exs b/test/membrane/testing/mock_resource_guard_test.exs new file mode 100644 index 000000000..bce3800e1 --- /dev/null +++ b/test/membrane/testing/mock_resource_guard_test.exs @@ -0,0 +1,5 @@ +defmodule Membrane.Testing.MockResourceGuardTest do + use ExUnit.Case + + doctest Membrane.Testing.MockResourceGuard +end