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

Only resolve lazy allowances if absolutely necessary #8

Merged
merged 1 commit into from
Dec 6, 2024

Conversation

martosaur
Copy link
Contributor

I've recently ran into weird Mox failures while running a lot of asynchronous tests that leverage lazy allowances resolution. After some investigation, I figured that NimbleOwnership resolves all lazy allowances every time get_and_update or fetch_owner is called. Given that Mox runs a single global server, this results in a situation when a lazy allowance can be resolved at a random time simply because another tests was making a call to a different mock.

Here's a failing Mox test for example
    test "allowances support lazy calls for asynchronous tests" do
      defmodule CalculatorServer_Lazy do
        use GenServer

        def init(args) do
          {:ok, args}
        end

        def handle_call(:call_mock, _from, []) do
          add_result = CalcMock.add(1, 1)
          {:reply, add_result, []}
        end
      end

      {:ok, _} = Registry.start_link(keys: :unique, name: Registry.Test)
      real_name = {:via, Registry, {Registry.Test, :test_process_lazy}}
      fake_name = {:via, Registry, {FakeRegistry, :test_process_lazy}}

      CalcMock
      |> expect(:add, fn _, _ -> :expected end)
      |> allow(self(), fn -> GenServer.whereis(real_name) end)
      
      FakeMock
      |> allow(self(), fn -> GenServer.whereis(fake_name) end)

      {:ok, _} = GenServer.start_link(CalculatorServer_Lazy, [], name: real_name)
      add_result = GenServer.call(real_name, :call_mock)
      assert add_result == :expected
    end
1) test allow/3 allowances support lazy calls for asynchronous tests (MoxTest)
     test/mox_test.exs:1035
     ** (EXIT from #PID<0.295.0>) exited in: GenServer.call({:global, Mox.Server}, {:fetch_owner, [#PID<0.301.0>], CalcMock}, 30000)
         ** (EXIT) an exception was raised:
             ** (ArgumentError) unknown registry: FakeRegistry
                 (elixir 1.17.3) lib/registry.ex:1400: Registry.key_info!/1
                 (elixir 1.17.3) lib/registry.ex:237: Registry.whereis_name/2
                 (elixir 1.17.3) lib/gen_server.ex:1300: GenServer.whereis/1
                 (nimble_ownership 1.0.0) lib/nimble_ownership.ex:623: anonymous fn/2 in NimbleOwnership.revalidate_lazy_calls/1
                 (stdlib 6.1) maps.erl:860: :maps.fold_1/4
                 (nimble_ownership 1.0.0) lib/nimble_ownership.ex:621: NimbleOwnership.revalidate_lazy_calls/1
                 (nimble_ownership 1.0.0) lib/nimble_ownership.ex:529: NimbleOwnership.handle_call/3
                 (stdlib 6.1) gen_server.erl:2381: :gen_server.try_handle_call/4
                 (stdlib 6.1) gen_server.erl:2410: :gen_server.handle_msg/6
                 (stdlib 6.1) proc_lib.erl:329: :proc_lib.init_p_do_apply/3

This PR aims to address this:

Changes:

  1. I removed lazy_calls from the state. I don't believe it was actually read anywhere.
  2. Replace revalidate_lazy_calls/2 with resolve_lazy_calls_for_key/2. It will only resolve allowances for the given key.
  3. Reshuffle fetch_owner code a little so that we would first attempt all owners and resolved allowances and only if nothing worked resolve lazy ones.

Notes:

  • I did my best to maintain resolve_lazy_calls_for_key's behaviour in a sense that it allows callbacks to "keep on giving". That means that resolution is never final and this check is not bullet-proof and a process can become an owner of a key it has allowance for. I'm not sure what the implications of this are though 😅
  • This doesn't fully address my Mox issue, as operations on the same mock can still trigger other test's allowance resolution too early, but it should be less likely.

@martosaur martosaur force-pushed the am/extra_lazy_resolution branch from 86ef9f2 to be2e2f2 Compare December 5, 2024 21:02
@martosaur
Copy link
Contributor Author

Just rebased the branch against main and fixed conflicts

@whatyouhide whatyouhide merged commit ac4d4b1 into dashbitco:main Dec 6, 2024
3 checks passed
@whatyouhide
Copy link
Collaborator

Great catch, thanks @martosaur 💟

@martosaur martosaur deleted the am/extra_lazy_resolution branch December 6, 2024 07:15
@whatyouhide
Copy link
Collaborator

v1.0.1 is out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants