From 941d06e50951a1a5b0cd02e1eb7845d968f81019 Mon Sep 17 00:00:00 2001 From: hpk Date: Wed, 18 Mar 2009 16:51:55 +0100 Subject: [PATCH] [svn r63040] try harder to record and auto-exit gateways after test runs --HG-- branch : trunk --- py/execnet/gateway.py | 1 + py/execnet/register.py | 1 + py/execnet/testing/test_event.py | 11 +++++ py/execnet/testing/test_gateway.py | 7 +++- py/test/defaultconftest.py | 2 +- py/test/plugin/pytest_execnetcleanup.py | 53 +++++++++++++++++++++++++ py/test/plugin/pytest_plugintester.py | 8 +++- py/test/plugin/pytest_pytester.py | 14 +++++++ py/test/pytestplugin.py | 1 + 9 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 py/execnet/testing/test_event.py create mode 100644 py/test/plugin/pytest_execnetcleanup.py diff --git a/py/execnet/gateway.py b/py/execnet/gateway.py index dd5a676ba..69aa8df3f 100644 --- a/py/execnet/gateway.py +++ b/py/execnet/gateway.py @@ -330,6 +330,7 @@ class Gateway(object): self._cleanup.unregister(self) self._stopexec() self._stopsend() + py._com.pyplugins.notify("gateway_exit", self) def _stopsend(self): self._send(None) diff --git a/py/execnet/register.py b/py/execnet/register.py index 848b8b897..cb082dc8c 100644 --- a/py/execnet/register.py +++ b/py/execnet/register.py @@ -41,6 +41,7 @@ class InstallableGateway(gateway.Gateway): super(InstallableGateway, self).__init__(io=io, _startcount=1) # XXX we dissallow execution form the other side self._initreceive(requestqueue=False) + py._com.pyplugins.notify("gateway_init", self) def _remote_bootstrap_gateway(self, io, extra=''): """ return Gateway with a asynchronously remotely diff --git a/py/execnet/testing/test_event.py b/py/execnet/testing/test_event.py new file mode 100644 index 000000000..631efce2f --- /dev/null +++ b/py/execnet/testing/test_event.py @@ -0,0 +1,11 @@ +import py +pytest_plugins = "pytester" + +class TestExecnetEvents: + def test_popengateway(self, eventrecorder): + gw = py.execnet.PopenGateway() + event = eventrecorder.popevent("gateway_init") + assert event.args[0] == gw + gw.exit() + event = eventrecorder.popevent("gateway_exit") + assert event.args[0] == gw diff --git a/py/execnet/testing/test_gateway.py b/py/execnet/testing/test_gateway.py index 70325bc96..a3f243715 100644 --- a/py/execnet/testing/test_gateway.py +++ b/py/execnet/testing/test_gateway.py @@ -571,6 +571,9 @@ class TestSocketGateway(SocketGatewaySetup, BasicRemoteExecution): class TestSshGateway(BasicRemoteExecution): def setup_class(cls): sshhost = py.test.config.getvalueorskip("sshhost") + if sshhost.find(":") != -1: + sshhost = sshhost.split(":")[0] + cls.sshhost = sshhost cls.gw = py.execnet.SshGateway(sshhost) def test_sshconfig_functional(self): @@ -578,7 +581,7 @@ class TestSshGateway(BasicRemoteExecution): ssh_config = tmpdir.join("ssh_config") ssh_config.write( "Host alias123\n" - " HostName %s\n" % (py.test.config.option.sshhost,)) + " HostName %s\n" % self.sshhost) gw = py.execnet.SshGateway("alias123", ssh_config=ssh_config) assert gw._cmd.find("-F") != -1 assert gw._cmd.find(str(ssh_config)) != -1 @@ -586,7 +589,7 @@ class TestSshGateway(BasicRemoteExecution): gw.exit() def test_sshaddress(self): - assert self.gw.remoteaddress == py.test.config.option.sshhost + assert self.gw.remoteaddress == self.sshhost @py.test.mark.xfail("XXX ssh-gateway error handling") def test_connexion_failes_on_non_existing_hosts(self): diff --git a/py/test/defaultconftest.py b/py/test/defaultconftest.py index dca474e47..b9c8c4ba5 100644 --- a/py/test/defaultconftest.py +++ b/py/test/defaultconftest.py @@ -11,7 +11,7 @@ Instance = py.test.collect.Instance conf_iocapture = "fd" # overridable from conftest.py # XXX resultlog should go, pypy's nightrun depends on it -pytest_plugins = "default terminal xfail tmpdir resultlog monkeypatch".split() +pytest_plugins = "default terminal xfail tmpdir execnetcleanup resultlog monkeypatch".split() # =================================================== # Distributed testing specific options diff --git a/py/test/plugin/pytest_execnetcleanup.py b/py/test/plugin/pytest_execnetcleanup.py new file mode 100644 index 000000000..807e16e24 --- /dev/null +++ b/py/test/plugin/pytest_execnetcleanup.py @@ -0,0 +1,53 @@ +import py + +class ExecnetcleanupPlugin: + _gateways = None + _debug = None + + def pytest_configure(self, config): + self._debug = config.option.debug + + def trace(self, msg, *args): + if self._debug: + print "[execnetcleanup %0x] %s %s" %(id(self), msg, args) + + def pyevent_gateway_init(self, gateway): + self.trace("init", gateway) + if self._gateways is not None: + self._gateways.append(gateway) + + def pyevent_gateway_exit(self, gateway): + self.trace("exit", gateway) + if self._gateways is not None: + self._gateways.remove(gateway) + + def pyevent_testrunstart(self, event): + self.trace("testrunstart", event) + self._gateways = [] + + def pyevent_testrunfinish(self, event): + self.trace("testrunfinish", event) + l = [] + for gw in self._gateways: + gw.exit() + l.append(gw) + for gw in l: + gw.join() + +def test_generic(plugintester): + plugintester.apicheck(ExecnetcleanupPlugin) + +@py.test.mark.xfail("clarify plugin registration/unregistration") +def test_execnetplugin(testdir): + p = ExecnetcleanupPlugin() + testdir.plugins.append(p) + testdir.inline_runsource(""" + import py + import sys + def test_hello(): + sys._gw = py.execnet.PopenGateway() + """, "-s", "--debug") + assert not p._gateways + assert py.std.sys._gw + py.test.raises(KeyError, "py.std.sys._gw.exit()") # already closed + diff --git a/py/test/plugin/pytest_plugintester.py b/py/test/plugin/pytest_plugintester.py index 3db476104..1fc6b938a 100644 --- a/py/test/plugin/pytest_plugintester.py +++ b/py/test/plugin/pytest_plugintester.py @@ -155,8 +155,14 @@ class PytestPluginHooks: def pyevent(self, eventname, *args, **kwargs): """ called for each testing event. """ + def pyevent_gateway_init(self, gateway): + """ called a gateway has been initialized. """ + + def pyevent_gateway_exit(self, gateway): + """ called when gateway is being exited. """ + def pyevent_trace(self, category, msg): - """ called for tracing events events. """ + """ called for tracing events. """ def pyevent_internalerror(self, event): """ called for internal errors. """ diff --git a/py/test/plugin/pytest_pytester.py b/py/test/plugin/pytest_pytester.py index 1525c7f7f..c921547bf 100644 --- a/py/test/plugin/pytest_pytester.py +++ b/py/test/plugin/pytest_pytester.py @@ -21,6 +21,11 @@ class PytesterPlugin: def pytest_pyfuncarg_EventRecorder(self, pyfuncitem): return EventRecorder + def pytest_pyfuncarg_eventrecorder(self, pyfuncitem): + evrec = EventRecorder(py._com.pyplugins) + pyfuncitem.addfinalizer(lambda: evrec.pyplugins.unregister(evrec)) + return evrec + def test_generic(plugintester): plugintester.apicheck(PytesterPlugin) @@ -245,6 +250,8 @@ class Event: self.name = name self.args = args self.kwargs = kwargs + def __repr__(self): + return "" %(self.name, self.args) class EventRecorder(object): def __init__(self, pyplugins, debug=False): # True): @@ -260,6 +267,13 @@ class EventRecorder(object): print "[event: %s]: %s **%s" %(name, ", ".join(map(str, args)), kwargs,) self.events.append(Event(name, args, kwargs)) + def popevent(self, name): + for i, event in py.builtin.enumerate(self.events): + if event.name == name: + del self.events[i] + return event + raise KeyError("popevent: %r not found in %r" %(name, self.events)) + def get(self, cls): l = [] for event in self.events: diff --git a/py/test/pytestplugin.py b/py/test/pytestplugin.py index dbc4d4b5e..7da0676da 100644 --- a/py/test/pytestplugin.py +++ b/py/test/pytestplugin.py @@ -95,6 +95,7 @@ class PytestPlugins(object): config = self._config del self._config self.pyplugins.call_each("pytest_unconfigure", config=config) + config.bus.unregister(self) # # XXX old code to automatically load classes