text: """
  ``PluginManager`` unit and public API testing.
  """
  import pytest
  
  from pluggy import (
      PluginValidationError,
      HookCallError,
      HookimplMarker,
      HookspecMarker,
  )
  from pluggy.manager import importlib_metadata
  
  
  hookspec = HookspecMarker("example")
  hookimpl = HookimplMarker("example")
  
  
  def test_plugin_double_register(pm):
      """Registering the same plugin more then once isn't allowed"""
      pm.register(42, name="abc")
      with pytest.raises(ValueError):
          pm.register(42, name="abc")
      with pytest.raises(ValueError):
          pm.register(42, name="def")
  
  
  def test_pm(pm):
      """Basic registration with objects"""
  
      class A:
          pass
  
      a1, a2 = A(), A()
      pm.register(a1)
      assert pm.is_registered(a1)
      pm.register(a2, "hello")
      assert pm.is_registered(a2)
      out = pm.get_plugins()
      assert a1 in out
      assert a2 in out
      assert pm.get_plugin("hello") == a2
      assert pm.unregister(a1) == a1
      assert not pm.is_registered(a1)
  
      out = pm.list_name_plugin()
      assert len(out) == 1
      assert out == [("hello", a2)]
  
  
  def test_has_plugin(pm):
      class A:
          pass
  
      a1 = A()
      pm.register(a1, "hello")
      assert pm.is_registered(a1)
      assert pm.has_plugin("hello")
  
  
  def test_register_dynamic_attr(he_pm):
      class A:
          def __getattr__(self, name):
              if name[0] != "_":
                  return 42
              raise AttributeError()
  
      a = A()
      he_pm.register(a)
      assert not he_pm.get_hookcallers(a)
  
  
  def test_pm_name(pm):
      class A:
          pass
  
      a1 = A()
      name = pm.register(a1, name="hello")
      assert name == "hello"
      pm.unregister(a1)
      assert pm.get_plugin(a1) is None
      assert not pm.is_registered(a1)
      assert not pm.get_plugins()
      name2 = pm.register(a1, name="hello")
      assert name2 == name
      pm.unregister(name="hello")
      assert pm.get_plugin(a1) is None
      assert not pm.is_registered(a1)
      assert not pm.get_plugins()
  
  
  def test_set_blocked(pm):
      class A:
          pass
  
      a1 = A()
      name = pm.register(a1)
      assert pm.is_registered(a1)
      assert not pm.is_blocked(name)
      pm.set_blocked(name)
      assert pm.is_blocked(name)
      assert not pm.is_registered(a1)
  
      pm.set_blocked("somename")
      assert pm.is_blocked("somename")
      assert not pm.register(A(), "somename")
      pm.unregister(name="somename")
      assert pm.is_blocked("somename")
  
  
  def test_register_mismatch_method(he_pm):
      class hello:
          @hookimpl
          def he_method_notexists(self):
              pass
  
      plugin = hello()
  
      he_pm.register(plugin)
      with pytest.raises(PluginValidationError) as excinfo:
          he_pm.check_pending()
      assert excinfo.value.plugin is plugin
  
  
  def test_register_mismatch_arg(he_pm):
      class hello:
          @hookimpl
          def he_method1(self, qlwkje):
              pass
  
      plugin = hello()
  
      with pytest.raises(PluginValidationError) as excinfo:
          he_pm.register(plugin)
      assert excinfo.value.plugin is plugin
  
  
  def test_register(pm):
      class MyPlugin:
          pass
  
      my = MyPlugin()
      pm.register(my)
      assert my in pm.get_plugins()
      my2 = MyPlugin()
      pm.register(my2)
      assert {my, my2}.issubset(pm.get_plugins())
  
      assert pm.is_registered(my)
      assert pm.is_registered(my2)
      pm.unregister(my)
      assert not pm.is_registered(my)
      assert my not in pm.get_plugins()
  
  
  def test_register_unknown_hooks(pm):
      class Plugin1:
          @hookimpl
          def he_method1(self, arg):
              return arg + 1
  
      pname = pm.register(Plugin1())
  
      class Hooks:
          @hookspec
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
      # assert not pm._unverified_hooks
      assert pm.hook.he_method1(arg=1) == [2]
      assert len(pm.get_hookcallers(pm.get_plugin(pname))) == 1
  
  
  def test_register_historic(pm):
      class Hooks:
          @hookspec(historic=True)
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
      out = []
  
      class Plugin:
          @hookimpl
          def he_method1(self, arg):
              out.append(arg)
  
      pm.register(Plugin())
      assert out == [1]
  
      class Plugin2:
          @hookimpl
          def he_method1(self, arg):
              out.append(arg * 10)
  
      pm.register(Plugin2())
      assert out == [1, 10]
      pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
      assert out == [1, 10, 120, 12]
  
  
  @pytest.mark.parametrize("result_callback", [True, False])
  def test_with_result_memorized(pm, result_callback):
      """Verify that ``_HookCaller._maybe_apply_history()`
      correctly applies the ``result_callback`` function, when provided,
      to the result from calling each newly registered hook.
      """
      out = []
      if result_callback:
  
          def callback(res):
              out.append(res)
  
      else:
          callback = None
  
      class Hooks:
          @hookspec(historic=True)
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      class Plugin1:
          @hookimpl
          def he_method1(self, arg):
              return arg * 10
  
      pm.register(Plugin1())
  
      he_method1 = pm.hook.he_method1
      he_method1.call_historic(result_callback=callback, kwargs=dict(arg=1))
  
      class Plugin2:
          @hookimpl
          def he_method1(self, arg):
              return arg * 10
  
      pm.register(Plugin2())
      if result_callback:
          assert out == [10, 10]
      else:
          assert out == []
  
  
  def test_with_callbacks_immediately_executed(pm):
      class Hooks:
          @hookspec(historic=True)
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      class Plugin1:
          @hookimpl
          def he_method1(self, arg):
              return arg * 10
  
      class Plugin2:
          @hookimpl
          def he_method1(self, arg):
              return arg * 20
  
      class Plugin3:
          @hookimpl
          def he_method1(self, arg):
              return arg * 30
  
      out = []
      pm.register(Plugin1())
      pm.register(Plugin2())
  
      he_method1 = pm.hook.he_method1
      he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
      assert out == [20, 10]
      pm.register(Plugin3())
      assert out == [20, 10, 30]
  
  
  def test_register_historic_incompat_hookwrapper(pm):
      class Hooks:
          @hookspec(historic=True)
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      out = []
  
      class Plugin:
          @hookimpl(hookwrapper=True)
          def he_method1(self, arg):
              out.append(arg)
  
      with pytest.raises(PluginValidationError):
          pm.register(Plugin())
  
  
  def test_call_extra(pm):
      class Hooks:
          @hookspec
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      def he_method1(arg):
          return arg * 10
  
      out = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
      assert out == [10]
  
  
  def test_call_with_too_few_args(pm):
      class Hooks:
          @hookspec
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      class Plugin1:
          @hookimpl
          def he_method1(self, arg):
              0 / 0
  
      pm.register(Plugin1())
      with pytest.raises(HookCallError):
          with pytest.warns(UserWarning):
              pm.hook.he_method1()
  
  
  def test_subset_hook_caller(pm):
      class Hooks:
          @hookspec
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
  
      out = []
  
      class Plugin1:
          @hookimpl
          def he_method1(self, arg):
              out.append(arg)
  
      class Plugin2:
          @hookimpl
          def he_method1(self, arg):
              out.append(arg * 10)
  
      class PluginNo:
          pass
  
      plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
      pm.register(plugin1)
      pm.register(plugin2)
      pm.register(plugin3)
      pm.hook.he_method1(arg=1)
      assert out == [10, 1]
      out[:] = []
  
      hc = pm.subset_hook_caller("he_method1", [plugin1])
      hc(arg=2)
      assert out == [20]
      out[:] = []
  
      hc = pm.subset_hook_caller("he_method1", [plugin2])
      hc(arg=2)
      assert out == [2]
      out[:] = []
  
      pm.unregister(plugin1)
      hc(arg=2)
      assert out == []
      out[:] = []
  
      pm.hook.he_method1(arg=1)
      assert out == [10]
  
  
  def test_get_hookimpls(pm):
      class Hooks:
          @hookspec
          def he_method1(self, arg):
              pass
  
      pm.add_hookspecs(Hooks)
      assert pm.hook.he_method1.get_hookimpls() == []
  
      class Plugin1:
          @hookimpl
          def he_method1(self, arg):
              pass
  
      class Plugin2:
          @hookimpl
          def he_method1(self, arg):
              pass
  
      class PluginNo:
          pass
  
      plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
      pm.register(plugin1)
      pm.register(plugin2)
      pm.register(plugin3)
  
      hookimpls = pm.hook.he_method1.get_hookimpls()
      hook_plugins = [item.plugin for item in hookimpls]
      assert hook_plugins == [plugin1, plugin2]
  
  
  def test_add_hookspecs_nohooks(pm):
      with pytest.raises(ValueError):
          pm.add_hookspecs(10)
  
  
  def test_load_setuptools_instantiation(monkeypatch, pm):
      class EntryPoint:
          name = "myname"
          group = "hello"
          value = "myname:foo"
  
          def load(self):
              class PseudoPlugin:
                  x = 42
  
              return PseudoPlugin()
  
      class Distribution:
          entry_points = (EntryPoint(),)
  
      dist = Distribution()
  
      def my_distributions():
          return (dist,)
  
      monkeypatch.setattr(importlib_metadata, "distributions", my_distributions)
      num = pm.load_setuptools_entrypoints("hello")
      assert num == 1
      plugin = pm.get_plugin("myname")
      assert plugin.x == 42
      ret = pm.list_plugin_distinfo()
      # poor man's `assert ret == [(plugin, mock.ANY)]`
      assert len(ret) == 1
      assert len(ret[0]) == 2
      assert ret[0][0] == plugin
      assert ret[0][1]._dist == dist
      num = pm.load_setuptools_entrypoints("hello")
      assert num == 0  # no plugin loaded by this call
  
  
  def test_add_tracefuncs(he_pm):
      out = []
  
      class api1:
          @hookimpl
          def he_method1(self):
              out.append("he_method1-api1")
  
      class api2:
          @hookimpl
          def he_method1(self):
              out.append("he_method1-api2")
  
      he_pm.register(api1())
      he_pm.register(api2())
  
      def before(hook_name, hook_impls, kwargs):
          out.append((hook_name, list(hook_impls), kwargs))
  
      def after(outcome, hook_name, hook_impls, kwargs):
          out.append((outcome, hook_name, list(hook_impls), kwargs))
  
      undo = he_pm.add_hookcall_monitoring(before, after)
  
      he_pm.hook.he_method1(arg=1)
      assert len(out) == 4
      assert out[0][0] == "he_method1"
      assert len(out[0][1]) == 2
      assert isinstance(out[0][2], dict)
      assert out[1] == "he_method1-api2"
      assert out[2] == "he_method1-api1"
      assert len(out[3]) == 4
      assert out[3][1] == out[0][0]
  
      undo()
      he_pm.hook.he_method1(arg=1)
      assert len(out) == 4 + 2
  
  
  def test_hook_tracing(he_pm):
      saveindent = []
  
      class api1:
          @hookimpl
          def he_method1(self):
              saveindent.append(he_pm.trace.root.indent)
  
      class api2:
          @hookimpl
          def he_method1(self):
              saveindent.append(he_pm.trace.root.indent)
              raise ValueError()
  
      he_pm.register(api1())
      out = []
      he_pm.trace.root.setwriter(out.append)
      undo = he_pm.enable_tracing()
      try:
          indent = he_pm.trace.root.indent
          he_pm.hook.he_method1(arg=1)
          assert indent == he_pm.trace.root.indent
          assert len(out) == 2
          assert "he_method1" in out[0]
          assert "finish" in out[1]
  
          out[:] = []
          he_pm.register(api2())
  
          with pytest.raises(ValueError):
              he_pm.hook.he_method1(arg=1)
          assert he_pm.trace.root.indent == indent
          assert saveindent[0] > indent
      finally:
          undo()
  