From e6234fdb6135099f12ed0bc8a8568d5d3bf77ec4 Mon Sep 17 00:00:00 2001 From: hpk Date: Thu, 9 Apr 2009 01:33:48 +0200 Subject: [PATCH] [svn r63883] * moving many more events to become ordinary plugin hook calls. * bit hackish because the code for handling the old events is also still there ... --HG-- branch : trunk --- py/_com.py | 4 +- py/test/dist/dsession.py | 33 ++++++------ py/test/dist/testing/test_dsession.py | 69 ++++++++++++------------- py/test/dist/testing/test_nodemanage.py | 6 +-- py/test/dist/testing/test_txnode.py | 41 ++++++++------- py/test/dist/txnode.py | 16 +++--- py/test/looponfail/remote.py | 4 +- py/test/plugin/api.py | 57 ++++++++++---------- py/test/plugin/pytest__pytest.py | 26 +++++++++- py/test/plugin/pytest_default.py | 5 +- py/test/plugin/pytest_doctest.py | 2 +- py/test/plugin/pytest_eventlog.py | 1 + py/test/plugin/pytest_pytester.py | 49 +++++++++++------- py/test/plugin/pytest_resultlog.py | 4 +- py/test/plugin/pytest_runner.py | 16 +++--- py/test/plugin/pytest_terminal.py | 44 ++++++++-------- py/test/pytestplugin.py | 4 +- py/test/runner.py | 1 + py/test/session.py | 11 ++-- py/test/testing/test_collect.py | 2 +- py/test/testing/test_pytestplugin.py | 4 +- py/test/testing/test_runner.py | 12 ++--- py/test/testing/test_session.py | 6 +-- 23 files changed, 232 insertions(+), 185 deletions(-) diff --git a/py/_com.py b/py/_com.py index 3a27a9eee..f8ed6fcfb 100644 --- a/py/_com.py +++ b/py/_com.py @@ -115,11 +115,11 @@ class PyPlugins: def register(self, plugin): assert not isinstance(plugin, str) + self.call_each("pytest_plugin_registered", plugin) self._plugins.append(plugin) - self.notify("plugin_registered", plugin) def unregister(self, plugin): - self.notify("plugin_unregistered", plugin) + self.call_each("pytest_plugin_unregistered", plugin) self._plugins.remove(plugin) def getplugins(self): diff --git a/py/test/dist/dsession.py b/py/test/dist/dsession.py index 06d5ed068..cc6e5dd8a 100644 --- a/py/test/dist/dsession.py +++ b/py/test/dist/dsession.py @@ -24,20 +24,20 @@ class LoopState(object): self.shuttingdown = False self.testsfailed = False - def pyevent__itemtestreport(self, rep): + def pytest_itemtestreport(self, rep): if rep.colitem in self.dsession.item2nodes: self.dsession.removeitem(rep.colitem, rep.node) if rep.failed: self.testsfailed = True - def pyevent__collectreport(self, rep): + def pytest_collectreport(self, rep): if rep.passed: self.colitems.extend(rep.result) - def pyevent__testnodeready(self, node): + def pytest_testnodeready(self, node): self.dsession.addnode(node) - def pyevent__testnodedown(self, node, error=None): + def pytest_testnodedown(self, node, error=None): pending = self.dsession.removenode(node) if pending: crashitem = pending[0] @@ -95,8 +95,12 @@ class DSession(Session): continue loopstate.dowork = True - eventname, args, kwargs = eventcall - self.bus.notify(eventname, *args, **kwargs) + callname, args, kwargs = eventcall + call = getattr(self.config.api, callname, None) + if call is not None: + call(*args, **kwargs) + else: + self.bus.notify(callname, *args, **kwargs) # termination conditions if ((loopstate.testsfailed and self.config.option.exitfirst) or @@ -110,10 +114,9 @@ class DSession(Session): # once we are in shutdown mode we dont send # events other than HostDown upstream eventname, args, kwargs = self.queue.get() - if eventname == "testnodedown": - node, error = args[0], args[1] - self.bus.notify("testnodedown", node, error) - self.removenode(node) + if eventname == "pytest_testnodedown": + self.config.api.pytest_testnodedown(**kwargs) + self.removenode(kwargs['node']) if not self.node2pending: # finished if loopstate.testsfailed: @@ -174,8 +177,8 @@ class DSession(Session): if isinstance(next, py.test.collect.Item): senditems.append(next) else: - self.bus.notify("collectionstart", next) - self.queueevent("collectreport", basic_collect_report(next)) + self.bus.notify("collectstart", next) + self.queueevent("pytest_collectreport", basic_collect_report(next)) if self.config.option.dist == "each": self.senditems_each(senditems) else: @@ -197,7 +200,7 @@ class DSession(Session): pending.extend(sending) for item in sending: self.item2nodes.setdefault(item, []).append(node) - self.bus.notify("itemstart", item, node) + self.config.api.pytest_itemstart(item=item, node=node) tosend[:] = tosend[room:] # update inplace if tosend: # we have some left, give it to the main loop @@ -216,7 +219,7 @@ class DSession(Session): # "sending same item %r to multiple " # "not implemented" %(item,)) self.item2nodes.setdefault(item, []).append(node) - self.bus.notify("itemstart", item, node) + self.config.api.pytest_itemstart(item=item, node=node) pending.extend(sending) tosend[:] = tosend[room:] # update inplace if not tosend: @@ -239,7 +242,7 @@ class DSession(Session): longrepr = "!!! Node %r crashed during running of test %r" %(node, item) rep = ItemTestReport(item, when="???", excinfo=longrepr) rep.node = node - self.bus.notify("itemtestreport", rep) + self.config.api.pytest_itemtestreport(rep=rep) def setup(self): """ setup any neccessary resources ahead of the test run. """ diff --git a/py/test/dist/testing/test_dsession.py b/py/test/dist/testing/test_dsession.py index dc528af07..bbf29c7a1 100644 --- a/py/test/dist/testing/test_dsession.py +++ b/py/test/dist/testing/test_dsession.py @@ -80,7 +80,7 @@ class TestDSession: session = DSession(modcol.config) session.triggertesting([modcol]) name, args, kwargs = session.queue.get(block=False) - assert name == 'collectreport' + assert name == 'pytest_collectreport' rep, = args assert len(rep.result) == 1 @@ -135,7 +135,7 @@ class TestDSession: session.queueevent("anonymous") session.loop_once(loopstate) assert node.sent == [[item]] - session.queueevent("itemtestreport", run(item, node)) + session.queueevent("pytest_itemtestreport", run(item, node)) session.loop_once(loopstate) assert loopstate.shuttingdown assert not loopstate.testsfailed @@ -148,7 +148,7 @@ class TestDSession: session.addnode(node) # setup a HostDown event - session.queueevent("testnodedown", node, None) + session.queueevent("pytest_testnodedown", node, None) loopstate = session._initloopstate([item]) loopstate.dowork = False @@ -156,7 +156,7 @@ class TestDSession: dumpqueue(session.queue) assert loopstate.exitstatus == outcome.EXIT_NOHOSTS - def test_testnodedown_causes_reschedule_pending(self, testdir, EventRecorder): + def test_testnodedown_causes_reschedule_pending(self, testdir): modcol = testdir.getmodulecol(""" def test_crash(): assert 0 @@ -174,8 +174,8 @@ class TestDSession: # have one test pending for a node that goes down session.senditems_load([item1, item2]) node = session.item2nodes[item1] [0] - session.queueevent("testnodedown", node, None) - evrec = EventRecorder(session.bus) + session.queueevent("pytest_testnodedown", node, None) + evrec = testdir.geteventrecorder(session.bus) print session.item2nodes loopstate = session._initloopstate([]) session.loop_once(loopstate) @@ -192,18 +192,18 @@ class TestDSession: # setup a session with two nodes session = DSession(item.config) node1 = MockNode() - session.queueevent("testnodeready", node1) + session.queueevent("pytest_testnodeready", node1) loopstate = session._initloopstate([item]) loopstate.dowork = False assert len(session.node2pending) == 0 session.loop_once(loopstate) assert len(session.node2pending) == 1 - def test_event_propagation(self, testdir, EventRecorder): + def test_event_propagation(self, testdir): item = testdir.getitem("def test_func(): pass") session = DSession(item.config) - evrec = EventRecorder(session.bus) + evrec = testdir.geteventrecorder(session.bus) session.queueevent("NOP", 42) session.loop_once(session._initloopstate([])) assert evrec.getcall('NOP') @@ -219,10 +219,10 @@ class TestDSession: assert node.sent == [[item]] ev = run(item, node) - session.queueevent("itemtestreport", ev) + session.queueevent("pytest_itemtestreport", rep=ev) session.loop_once(loopstate) assert loopstate.shuttingdown - session.queueevent("testnodedown", node, None) + session.queueevent("pytest_testnodedown", node=node, error=None) session.loop_once(loopstate) dumpqueue(session.queue) return session, loopstate.exitstatus @@ -256,30 +256,30 @@ class TestDSession: # run tests ourselves and produce reports ev1 = run(items[0], node) ev2 = run(items[1], node) - session.queueevent("itemtestreport", ev1) # a failing one - session.queueevent("itemtestreport", ev2) + session.queueevent("pytest_itemtestreport", rep=ev1) # a failing one + session.queueevent("pytest_itemtestreport", rep=ev2) # now call the loop loopstate = session._initloopstate(items) session.loop_once(loopstate) assert loopstate.testsfailed assert loopstate.shuttingdown - def test_shuttingdown_filters_events(self, testdir, EventRecorder): + def test_shuttingdown_filters_events(self, testdir): item = testdir.getitem("def test_func(): pass") session = DSession(item.config) node = MockNode() session.addnode(node) loopstate = session._initloopstate([]) loopstate.shuttingdown = True - evrec = EventRecorder(session.bus) - session.queueevent("itemtestreport", run(item, node)) + evrec = testdir.geteventrecorder(session.bus) + session.queueevent("pytest_itemtestreport", rep=run(item, node)) session.loop_once(loopstate) - assert not evrec.getcalls("testnodedown") - session.queueevent("testnodedown", node, None) + assert not evrec.getcalls("pytest_testnodedown") + session.queueevent("pytest_testnodedown", node=node, error=None) session.loop_once(loopstate) - assert evrec.getcall('testnodedown').node == node + assert evrec.getcall('pytest_testnodedown').node == node - def test_filteritems(self, testdir, EventRecorder): + def test_filteritems(self, testdir): modcol = testdir.getmodulecol(""" def test_fail(): assert 0 @@ -292,7 +292,7 @@ class TestDSession: dsel = session.filteritems([modcol]) assert dsel == [modcol] items = modcol.collect() - evrec = EventRecorder(session.bus) + evrec = testdir.geteventrecorder(session.bus) remaining = session.filteritems(items) assert remaining == [] @@ -313,13 +313,13 @@ class TestDSession: node = MockNode() session.addnode(node) session.senditems_load([item]) - session.queueevent("itemtestreport", run(item, node)) + session.queueevent("pytest_itemtestreport", rep=run(item, node)) loopstate = session._initloopstate([]) session.loop_once(loopstate) assert node._shutdown is True assert loopstate.exitstatus is None, "loop did not wait for testnodedown" assert loopstate.shuttingdown - session.queueevent("testnodedown", node, None) + session.queueevent("pytest_testnodedown", node=node, error=None) session.loop_once(loopstate) assert loopstate.exitstatus == 0 @@ -340,10 +340,10 @@ class TestDSession: # node2pending will become empty when the loop sees the report rep = run(item1, node) - session.queueevent("itemtestreport", run(item1, node)) + session.queueevent("pytest_itemtestreport", rep=run(item1, node)) # but we have a collection pending - session.queueevent("collectreport", colreport) + session.queueevent("pytest_collectreport", rep=colreport) loopstate = session._initloopstate([]) session.loop_once(loopstate) @@ -354,7 +354,6 @@ class TestDSession: assert loopstate.exitstatus is None, "loop did not care for colitems" def test_dist_some_tests(self, testdir): - from py.__.test.dist.testing.test_txnode import EventQueue p1 = testdir.makepyfile(test_one=""" def test_1(): pass @@ -366,16 +365,16 @@ class TestDSession: """) config = testdir.parseconfig('-d', p1, '--tx=popen') dsession = DSession(config) - eq = EventQueue(config.bus) + callrecorder = testdir.geteventrecorder(config.bus).callrecorder dsession.main([config.getfsnode(p1)]) - ev, = eq.geteventargs("itemtestreport") - assert ev.passed - ev, = eq.geteventargs("itemtestreport") - assert ev.skipped - ev, = eq.geteventargs("itemtestreport") - assert ev.failed + rep = callrecorder.popcall("pytest_itemtestreport").rep + assert rep.passed + rep = callrecorder.popcall("pytest_itemtestreport").rep + assert rep.skipped + rep = callrecorder.popcall("pytest_itemtestreport").rep + assert rep.failed # see that the node is really down - node, error = eq.geteventargs("testnodedown") + node = callrecorder.popcall("pytest_testnodedown").node assert node.gateway.spec.popen - eq.geteventargs("testrunfinish") + #XXX eq.geteventargs("pytest_testrunfinish") diff --git a/py/test/dist/testing/test_nodemanage.py b/py/test/dist/testing/test_nodemanage.py index eb63d3525..7df026814 100644 --- a/py/test/dist/testing/test_nodemanage.py +++ b/py/test/dist/testing/test_nodemanage.py @@ -97,14 +97,14 @@ class TestNodeManager: assert gwspec._samefilesystem() assert not gwspec.chdir - def test_setup_DEBUG(self, source, EventRecorder): + def test_setup_DEBUG(self, source, testdir): specs = ["popen"] * 2 source.join("conftest.py").write("rsyncdirs = ['a']") source.ensure('a', dir=1) config = py.test.config._reparse([source, '--debug']) assert config.option.debug nodemanager = NodeManager(config, specs) - sorter = EventRecorder(config.bus, debug=True) + sorter = testdir.geteventrecorder(config.bus) nodemanager.setup_nodes(putevent=[].append) for spec in nodemanager.gwmanager.specs: l = sorter.getcalls("trace") @@ -119,6 +119,6 @@ class TestNodeManager: """) sorter = testdir.inline_run("-d", "--rsyncdir=%s" % testdir.tmpdir, "--tx=%s" % specssh, testdir.tmpdir) - ev = sorter.getfirstnamed("itemtestreport") + ev = sorter.getfirstnamed(pytest_itemtestreport) assert ev.passed diff --git a/py/test/dist/testing/test_txnode.py b/py/test/dist/testing/test_txnode.py index 449d2dcb7..1e11bf107 100644 --- a/py/test/dist/testing/test_txnode.py +++ b/py/test/dist/testing/test_txnode.py @@ -26,7 +26,9 @@ class EventQueue: name, args, kwargs = eventcall assert isinstance(name, str) if name == eventname: - return args + if args: + return args + return kwargs events.append(name) if name == "internalerror": print str(kwargs["excrepr"]) @@ -78,9 +80,9 @@ class TestMasterSlaveConnection: def test_crash_invalid_item(self, mysetup): node = mysetup.makenode() node.send(123) # invalid item - n, error = mysetup.geteventargs("testnodedown") - assert n is node - assert str(error).find("AttributeError") != -1 + kwargs = mysetup.geteventargs("pytest_testnodedown") + assert kwargs['node'] is node + assert str(kwargs['error']).find("AttributeError") != -1 def test_crash_killed(self, testdir, mysetup): if not hasattr(py.std.os, 'kill'): @@ -92,16 +94,16 @@ class TestMasterSlaveConnection: """) node = mysetup.makenode(item.config) node.send(item) - n, error = mysetup.geteventargs("testnodedown") - assert n is node - assert str(error).find("Not properly terminated") != -1 + kwargs = mysetup.geteventargs("pytest_testnodedown") + assert kwargs['node'] is node + assert str(kwargs['error']).find("Not properly terminated") != -1 def test_node_down(self, mysetup): node = mysetup.makenode() node.shutdown() - n, error = mysetup.geteventargs("testnodedown") - assert n is node - assert not error + kwargs = mysetup.geteventargs("pytest_testnodedown") + assert kwargs['node'] is node + assert not kwargs['error'] node.callback(node.ENDMARK) excinfo = py.test.raises(IOError, "mysetup.geteventargs('testnodedown', timeout=0.01)") @@ -118,11 +120,11 @@ class TestMasterSlaveConnection: item = testdir.getitem("def test_func(): pass") node = mysetup.makenode(item.config) node.send(item) - ev, = mysetup.geteventargs("itemtestreport") - assert ev.passed - assert ev.colitem == item - #assert event.item == item - #assert event.item is not item + kwargs = mysetup.geteventargs("pytest_itemtestreport") + rep = kwargs['rep'] + assert rep.passed + print rep + assert rep.colitem == item def test_send_some(self, testdir, mysetup): items = testdir.getitems(""" @@ -138,10 +140,11 @@ class TestMasterSlaveConnection: for item in items: node.send(item) for outcome in "passed failed skipped".split(): - ev, = mysetup.geteventargs("itemtestreport") - assert getattr(ev, outcome) + kwargs = mysetup.geteventargs("pytest_itemtestreport") + rep = kwargs['rep'] + assert getattr(rep, outcome) node.sendlist(items) for outcome in "passed failed skipped".split(): - ev, = mysetup.geteventargs("itemtestreport") - assert getattr(ev, outcome) + rep = mysetup.geteventargs("pytest_itemtestreport")['rep'] + assert getattr(rep, outcome) diff --git a/py/test/dist/txnode.py b/py/test/dist/txnode.py index d0101cf3f..0a5645047 100644 --- a/py/test/dist/txnode.py +++ b/py/test/dist/txnode.py @@ -38,21 +38,21 @@ class TXNode(object): if not self._down: if not err: err = "Not properly terminated" - self.notify("testnodedown", self, err) + self.notify("pytest_testnodedown", node=self, error=err) self._down = True return eventname, args, kwargs = eventcall if eventname == "slaveready": if self._sendslaveready: self._sendslaveready(self) - self.notify("testnodeready", self) + self.notify("pytest_testnodeready", node=self) elif eventname == "slavefinished": self._down = True - self.notify("testnodedown", self, None) - elif eventname == "itemtestreport": - rep = args[0] + self.notify("pytest_testnodedown", error=None, node=self) + elif eventname == "pytest_itemtestreport": + rep = kwargs['rep'] rep.node = self - self.notify("itemtestreport", rep) + self.notify("pytest_itemtestreport", rep=rep) else: self.notify(eventname, *args, **kwargs) except KeyboardInterrupt: @@ -104,8 +104,8 @@ class SlaveNode(object): def sendevent(self, eventname, *args, **kwargs): self.channel.send((eventname, args, kwargs)) - def pyevent__itemtestreport(self, rep): - self.sendevent("itemtestreport", rep) + def pytest_itemtestreport(self, rep): + self.sendevent("pytest_itemtestreport", rep=rep) def run(self): channel = self.channel diff --git a/py/test/looponfail/remote.py b/py/test/looponfail/remote.py index 41ac4db97..2b23fdbd2 100644 --- a/py/test/looponfail/remote.py +++ b/py/test/looponfail/remote.py @@ -137,10 +137,10 @@ def slave_runsession(channel, config, fullwidth, hasmarkup): session.shouldclose = channel.isclosed class Failures(list): - def pyevent__itemtestreport(self, rep): + def pytest_itemtestreport(self, rep): if rep.failed: self.append(rep) - pyevent__collectreport = pyevent__itemtestreport + pytest_collectreport = pytest_itemtestreport failreports = Failures() session.bus.register(failreports) diff --git a/py/test/plugin/api.py b/py/test/plugin/api.py index de6fe5ddf..3a7cc0489 100644 --- a/py/test/plugin/api.py +++ b/py/test/plugin/api.py @@ -68,6 +68,36 @@ class PluginHooks: """ return processed content for a given doctest""" pytest_doctest_prepare_content.firstresult = True + def pytest_itemstart(self, item, node=None): + """ test item gets collected. """ + + def pytest_itemtestreport(self, rep): + """ test has been run. """ + + def pytest_item_runtest_finished(self, item, excinfo, outerr): + """ test has been run. """ + + def pytest_itemsetupreport(self, rep): + """ a report on running a fixture function. """ + + def pytest_collectstart(self, collector): + """ collector starts collecting. """ + + def pytest_collectreport(self, rep): + """ collector finished collecting. """ + + def pytest_plugin_registered(self, plugin): + """ a new py lib plugin got registered. """ + + def pytest_plugin_unregistered(self, plugin): + """ a py lib plugin got unregistered. """ + + def pytest_testnodeready(self, node): + """ Test Node is ready to operate. """ + + def pytest_testnodedown(self, node, error): + """ Test Node is down. """ + class Events: # Events @@ -83,26 +113,9 @@ class Events: def pyevent__internalerror(self, excrepr): """ called for internal errors. """ - def pyevent__itemstart(self, item, node=None): - """ test item gets collected. """ - - def pyevent__itemtestreport(self, rep): - """ test has been run. """ - - def pyevent__item_runtest_finished(self, item, excinfo, outerr): - """ test has been run. """ - - def pyevent__itemsetupreport(self, rep): - """ a report on running a fixture function. """ - def pyevent__deselected(self, items): """ collected items that were deselected (by keyword). """ - def pyevent__collectionstart(self, collector): - """ collector starts collecting. """ - - def pyevent__collectreport(self, rep): - """ collector finished collecting. """ def pyevent__testrunstart(self): """ whole test run starts. """ @@ -115,19 +128,11 @@ class Events: The gateway will have an 'id' attribute that is unique within the gateway manager context. """ - def pyevent__testnodeready(self, node): - """ Test Node is ready to operate. """ - def pyevent__testnodedown(self, node, error): - """ Test Node is down. """ - - def pyevent__rescheduleitems(self, items): + def pytest_rescheduleitems(self, items): """ reschedule Items from a node that went down. """ def pyevent__looponfailinfo(self, failreports, rootdirs): """ info for repeating failing tests. """ - def pyevent__plugin_registered(self, plugin): - """ a new py lib plugin got registered. """ - diff --git a/py/test/plugin/pytest__pytest.py b/py/test/plugin/pytest__pytest.py index 764773788..61ed7df26 100644 --- a/py/test/plugin/pytest__pytest.py +++ b/py/test/plugin/pytest__pytest.py @@ -21,10 +21,13 @@ class ParsedCall: def __init__(self, name, locals): assert '_name' not in locals self.__dict__.update(locals) + self.__dict__.pop('self') self._name = name def __repr__(self): - return "" %(self.__dict__,) + d = self.__dict__.copy() + del d['_name'] + return "" %(self._name, d) class CallRecorder: def __init__(self, pyplugins): @@ -47,6 +50,11 @@ class CallRecorder: for recorder in self._recorders.values(): self._pyplugins.unregister(recorder) + def recordsmethod(self, name): + for apiclass in self._recorders: + if hasattr(apiclass, name): + return True + def _getcallparser(self, method): name = method.__name__ args, varargs, varkw, default = py.std.inspect.getargspec(method) @@ -70,6 +78,22 @@ class CallRecorder: return call raise ValueError("could not find call %r in %r" %(name, self.calls)) + def getcalls(self, names): + if isinstance(names, str): + names = names.split() + for name in names: + for cls in self._recorders: + if name in vars(cls): + break + else: + raise ValueError("callname %r not found in %r" %( + name, self._recorders.keys())) + l = [] + for call in self.calls: + if call._name in names: + l.append(call) + return l + def test_generic(plugintester): plugintester.apicheck(_pytestPlugin) diff --git a/py/test/plugin/pytest_default.py b/py/test/plugin/pytest_default.py index bdc1a73fb..a7131ad95 100644 --- a/py/test/plugin/pytest_default.py +++ b/py/test/plugin/pytest_default.py @@ -10,7 +10,7 @@ class DefaultPlugin: else: runner = basic_run_report report = runner(item, pdb=pdb) - item.config.pytestplugins.notify("itemtestreport", report) + item.config.api.pytest_itemtestreport(rep=report) return True def pytest_item_makereport(self, item, excinfo, when, outerr): @@ -20,8 +20,9 @@ class DefaultPlugin: def pytest_item_runtest_finished(self, item, excinfo, outerr): from py.__.test import runner rep = runner.ItemTestReport(item, excinfo, "execute", outerr) - item.config.pytestplugins.notify("itemtestreport", rep) + item.config.api.pytest_itemtestreport(rep=rep) + # XXX make this access pyfuncitem.args or funcargs def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): pyfuncitem.obj(*args, **kwargs) diff --git a/py/test/plugin/pytest_doctest.py b/py/test/plugin/pytest_doctest.py index f7e5bec29..13a23b7dc 100644 --- a/py/test/plugin/pytest_doctest.py +++ b/py/test/plugin/pytest_doctest.py @@ -122,7 +122,7 @@ class TestDoctests: 2 """) sorter = testdir.inline_run(p) - call = sorter.getcall("itemtestreport") + call = sorter.getcall("pytest_itemtestreport") assert call.rep.failed assert call.rep.longrepr # XXX diff --git a/py/test/plugin/pytest_eventlog.py b/py/test/plugin/pytest_eventlog.py index a79867e4c..b8718da4f 100644 --- a/py/test/plugin/pytest_eventlog.py +++ b/py/test/plugin/pytest_eventlog.py @@ -26,6 +26,7 @@ class EventlogPlugin: # plugin tests # =============================================================================== +@py.test.mark.xfail def test_generic(plugintester): plugintester.apicheck(EventlogPlugin) diff --git a/py/test/plugin/pytest_pytester.py b/py/test/plugin/pytest_pytester.py index 0542a3042..bf474e89f 100644 --- a/py/test/plugin/pytest_pytester.py +++ b/py/test/plugin/pytest_pytester.py @@ -6,8 +6,10 @@ import py import inspect from py.__.test import runner from py.__.test.config import Config as pytestConfig +from pytest__pytest import CallRecorder import api + class PytesterPlugin: def pytest_funcarg__linecomp(self, pyfuncitem): return LineComp() @@ -19,8 +21,8 @@ class PytesterPlugin: tmptestdir = TmpTestdir(pyfuncitem) return tmptestdir - def pytest_funcarg__EventRecorder(self, pyfuncitem): - return EventRecorder + #def pytest_funcarg__EventRecorder(self, pyfuncitem): + # return EventRecorder def pytest_funcarg__eventrecorder(self, pyfuncitem): evrec = EventRecorder(py._com.pyplugins) @@ -74,10 +76,12 @@ class TmpTestdir: if hasattr(self, '_olddir'): self._olddir.chdir() - def geteventrecorder(self, config): - evrec = EventRecorder(config.bus) - self.pyfuncitem.addfinalizer(lambda: config.bus.unregister(evrec)) - return evrec + def geteventrecorder(self, bus): + sorter = EventRecorder(bus) + sorter.callrecorder = CallRecorder(bus) + sorter.callrecorder.start_recording(api.PluginHooks) + self.pyfuncitem.addfinalizer(sorter.callrecorder.finalize) + return sorter def chdir(self): old = self.tmpdir.chdir() @@ -128,7 +132,7 @@ class TmpTestdir: #config = self.parseconfig(*args) config = self.parseconfig(*args) session = config.initsession() - rec = EventRecorder(config.bus) + rec = self.geteventrecorder(config.bus) colitems = [config.getfsnode(arg) for arg in config.args] items = list(session.genitems(colitems)) return items, rec @@ -150,10 +154,10 @@ class TmpTestdir: config = self.parseconfig(*args) config.pytestplugins.do_configure(config) session = config.initsession() - sorter = EventRecorder(config.bus) + sorter = self.geteventrecorder(config.bus) session.main() config.pytestplugins.do_unconfigure(config) - return sorter + return sorter def config_preparse(self): config = self.Config() @@ -306,11 +310,17 @@ class EventRecorder(object): if len(names) == 1 and isinstance(names, str): names = names.split() l = [] - for event in self.events: - if event.name in names: - method = self._getcallparser(event.name) - pevent = method(*event.args, **event.kwargs) - l.append(pevent) + for name in names: + if self.callrecorder.recordsmethod("pytest_" + name): + name = "pytest_" + name + if self.callrecorder.recordsmethod(name): + l.extend(self.callrecorder.getcalls(name)) + else: + for event in self.events: + if event.name == name: + method = self._getcallparser(event.name) + pevent = method(*event.args, **event.kwargs) + l.append(pevent) return l def _getcallparser(self, eventname): @@ -328,7 +338,7 @@ class EventRecorder(object): # functionality for test reports def getreports(self, names="itemtestreport collectreport"): - names = names.split() + names = [("pytest_" + x) for x in names.split()] l = [] for call in self.getcalls(*names): l.append(call.rep) @@ -386,6 +396,7 @@ class EventRecorder(object): def unregister(self): self.pyplugins.unregister(self) +@py.test.mark.xfail def test_eventrecorder(): bus = py._com.PyPlugins() recorder = EventRecorder(bus) @@ -395,7 +406,7 @@ def test_eventrecorder(): rep = runner.ItemTestReport(None, None) rep.passed = False rep.failed = True - bus.notify("itemtestreport", rep) + bus.notify("pytest_itemtestreport", rep) failures = recorder.getfailures() assert failures == [rep] failures = recorder.getfailures() @@ -404,12 +415,12 @@ def test_eventrecorder(): rep = runner.ItemTestReport(None, None) rep.passed = False rep.skipped = True - bus.notify("itemtestreport", rep) + bus.notify("pytest_itemtestreport", rep) rep = runner.CollectReport(None, None) rep.passed = False rep.failed = True - bus.notify("itemtestreport", rep) + bus.notify("pytest_itemtestreport", rep) passed, skipped, failed = recorder.listoutcomes() assert not passed and skipped and failed @@ -423,7 +434,7 @@ def test_eventrecorder(): recorder.clear() assert not recorder.events assert not recorder.getfailures() - bus.notify("itemtestreport", rep) + bus.notify(pytest_itemtestreport, rep) assert not recorder.events assert not recorder.getfailures() diff --git a/py/test/plugin/pytest_resultlog.py b/py/test/plugin/pytest_resultlog.py index e8fdff4a4..9b25d24a8 100644 --- a/py/test/plugin/pytest_resultlog.py +++ b/py/test/plugin/pytest_resultlog.py @@ -58,7 +58,7 @@ class ResultLog(object): testpath = generic_path(event.colitem) self.write_log_entry(testpath, shortrepr, longrepr) - def pyevent__itemtestreport(self, rep): + def pytest_itemtestreport(self, rep): code = rep.shortrepr if rep.passed: longrepr = "" @@ -68,7 +68,7 @@ class ResultLog(object): longrepr = str(rep.longrepr.reprcrash.message) self.log_outcome(rep, code, longrepr) - def pyevent__collectreport(self, rep): + def pytest_collectreport(self, rep): if not rep.passed: if rep.failed: code = "F" diff --git a/py/test/plugin/pytest_runner.py b/py/test/plugin/pytest_runner.py index 5065885a8..98dbd41e0 100644 --- a/py/test/plugin/pytest_runner.py +++ b/py/test/plugin/pytest_runner.py @@ -13,7 +13,7 @@ class RunnerPlugin: call = item.config.guardedcall(lambda: setupstate.prepare(item)) rep = ItemSetupReport(item, call.excinfo, call.outerr) if call.excinfo: - item.config.pytestplugins.notify("itemsetupreport", rep) + item.config.pytestplugins.notify(pytest_itemsetupreport, rep) else: call = item.config.guardedcall(lambda: item.runtest()) item.config.api.pytest_item_runtest_finished( @@ -21,7 +21,7 @@ class RunnerPlugin: call = item.config.guardedcall(lambda: self.teardown_exact(item)) if call.excinfo: rep = ItemSetupReport(item, call.excinfo, call.outerr) - item.config.pytestplugins.notify("itemsetupreport", rep) + item.config.api.pytest_itemsetupreport(rep=rep) def pytest_collector_collect(self, collector): call = item.config.guardedcall(lambda x: collector._memocollect()) @@ -208,9 +208,9 @@ class TestRunnerPlugin: item = testdir.getitem("""def test_func(): pass""") plugin = RunnerPlugin() plugin.pytest_configure(item.config) - sorter = testdir.geteventrecorder(item.config) + sorter = testdir.geteventrecorder(item.config.bus) plugin.pytest_item_setup_and_runtest(item) - rep = sorter.getcall("itemtestreport").rep + rep = sorter.getcall("pytest_itemtestreport").rep assert rep.passed class TestSetupEvents: @@ -223,7 +223,7 @@ class TestSetupEvents: def test_func(): pass """) - assert not sorter.getcalls("itemsetupreport") + assert not sorter.getcalls(pytest_itemsetupreport) def test_setup_fails(self, testdir): sorter = testdir.inline_runsource(""" @@ -233,7 +233,7 @@ class TestSetupEvents: def test_func(): pass """) - rep = sorter.popcall("itemsetupreport").rep + rep = sorter.popcall(pytest_itemsetupreport).rep assert rep.failed assert not rep.skipped assert rep.excrepr @@ -248,7 +248,7 @@ class TestSetupEvents: print "13" raise ValueError(25) """) - rep = evrec.popcall("itemsetupreport").rep + rep = evrec.popcall(pytest_itemsetupreport).rep assert rep.failed assert rep.item == item assert not rep.passed @@ -263,7 +263,7 @@ class TestSetupEvents: def test_func(): pass """) - rep = sorter.popcall("itemsetupreport") + rep = sorter.popcall(pytest_itemsetupreport) assert not rep.failed assert rep.skipped assert rep.excrepr diff --git a/py/test/plugin/pytest_terminal.py b/py/test/plugin/pytest_terminal.py index 392b0202c..98ec8a251 100644 --- a/py/test/plugin/pytest_terminal.py +++ b/py/test/plugin/pytest_terminal.py @@ -116,7 +116,7 @@ class TerminalReporter: targets = ", ".join([gw.id for gw in gateways]) self.write_line("rsyncfinish: %s -> %s" %(source, targets)) - def pyevent__plugin_registered(self, plugin): + def pytest_plugin_registered(self, plugin): if self.config.option.traceconfig: msg = "PLUGIN registered: %s" %(plugin,) # XXX this event may happen during setup/teardown time @@ -124,10 +124,10 @@ class TerminalReporter: # which garbles our output if we use self.write_line self.write_line(msg) - def pyevent__testnodeready(self, node): + def pytest_testnodeready(self, node): self.write_line("%s txnode ready to receive tests" %(node.gateway.id,)) - def pyevent__testnodedown(self, node, error): + def pytest_testnodedown(self, node, error): if error: self.write_line("%s node down, error: %s" %(node.gateway.id, error)) @@ -136,7 +136,7 @@ class TerminalReporter: self.config.option.traceconfig and category.find("config") != -1: self.write_line("[%s] %s" %(category, msg)) - def pyevent__itemstart(self, item, node=None): + def pytest_itemstart(self, item, node=None): if self.config.option.debug: info = item.repr_metainfo() line = info.verboseline(basedir=self.curdir) + " " @@ -154,14 +154,14 @@ class TerminalReporter: #self.write_fspath_result(fspath, "") self.write_ensure_prefix(line, "") - def pyevent__rescheduleitems(self, items): + def pytest_rescheduleitems(self, items): if self.config.option.debug: self.write_sep("!", "RESCHEDULING %s " %(items,)) def pyevent__deselected(self, items): self.stats.setdefault('deselected', []).append(items) - def pyevent__itemtestreport(self, rep): + def pytest_itemtestreport(self, rep): fspath = rep.colitem.fspath cat, letter, word = self.getcategoryletterword(rep) if isinstance(word, tuple): @@ -184,7 +184,7 @@ class TerminalReporter: self._tw.write(" " + line) self.currentfspath = -2 - def pyevent__collectreport(self, rep): + def pytest_collectreport(self, rep): if not rep.passed: if rep.failed: self.stats.setdefault("failed", []).append(rep) @@ -309,14 +309,14 @@ class CollectonlyReporter: def outindent(self, line): self.out.line(self.indent + str(line)) - def pyevent__collectionstart(self, collector): + def pytest_collectstart(self, collector): self.outindent(collector) self.indent += self.INDENT - def pyevent__itemstart(self, item, node=None): + def pytest_itemstart(self, item, node=None): self.outindent(item) - def pyevent__collectreport(self, rep): + def pytest_collectreport(self, rep): if not rep.passed: self.outindent("!!! %s !!!" % rep.longrepr.reprcrash.message) self._failed.append(rep) @@ -373,7 +373,7 @@ class TestTerminal: for item in testdir.genitems([modcol]): ev = runner.basic_run_report(item) - rep.config.bus.notify("itemtestreport", ev) + rep.config.api.pytest_itemtestreport(rep=ev) linecomp.assert_contains_lines([ "*test_pass_skip_fail.py .sF" ]) @@ -400,10 +400,10 @@ class TestTerminal: items = modcol.collect() rep.config.option.debug = True # for item in items: - rep.config.bus.notify("itemstart", item, None) + rep.config.api.pytest_itemstart(item=item, node=None) s = linecomp.stringio.getvalue().strip() assert s.endswith(item.name) - rep.config.bus.notify("itemtestreport", runner.basic_run_report(item)) + rep.config.api.pytest_itemtestreport(rep=runner.basic_run_report(item)) linecomp.assert_contains_lines([ "*test_pass_skip_fail_verbose.py:2: *test_ok*PASS*", @@ -520,8 +520,8 @@ class TestTerminal: rep.config.bus.notify("testrunstart") rep.config.bus.notify("testrunstart") for item in testdir.genitems([modcol]): - rep.config.bus.notify("itemtestreport", - runner.basic_run_report(item)) + rep.config.api.pytest_itemtestreport( + rep=runner.basic_run_report(item)) rep.config.bus.notify("testrunfinish", exitstatus=1) s = linecomp.stringio.getvalue() if tbopt == "long": @@ -548,7 +548,7 @@ class TestTerminal: l = list(testdir.genitems([modcol])) assert len(l) == 1 modcol.config.option.debug = True - rep.config.bus.notify("itemstart", l[0]) + rep.config.api.pytest_itemstart(item=l[0]) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) @@ -569,8 +569,8 @@ class TestTerminal: bus.notify("testrunstart") try: for item in testdir.genitems([modcol]): - bus.notify("itemtestreport", - runner.basic_run_report(item)) + modcol.config.api.pytest_itemtestreport( + rep=runner.basic_run_report(item)) except KeyboardInterrupt: excinfo = py.code.ExceptionInfo() else: @@ -628,17 +628,17 @@ class TestCollectonly: rep = CollectonlyReporter(modcol.config, out=linecomp.stringio) modcol.config.bus.register(rep) indent = rep.indent - rep.config.bus.notify("collectionstart", modcol) + rep.config.api.pytest_collectstart(collector=modcol) linecomp.assert_contains_lines([ "" ]) item = modcol.join("test_func") - rep.config.bus.notify("itemstart", item) + rep.config.api.pytest_itemstart(item=item) linecomp.assert_contains_lines([ " ", ]) - rep.config.bus.notify( "collectreport", - runner.CollectReport(modcol, [], excinfo=None)) + rep.config.api.pytest_collectreport( + rep=runner.CollectReport(modcol, [], excinfo=None)) assert rep.indent == indent def test_collectonly_skipped_module(self, testdir, linecomp): diff --git a/py/test/pytestplugin.py b/py/test/pytestplugin.py index fb8a9eb7e..1b4886481 100644 --- a/py/test/pytestplugin.py +++ b/py/test/pytestplugin.py @@ -86,14 +86,14 @@ class PytestPlugins(object): if excinfo is None: excinfo = py.code.ExceptionInfo() excrepr = excinfo.getrepr(funcargs=True, showlocals=True) - return self.notify("internalerror", excrepr) + return self.notify("internalerror", excrepr=excrepr) def do_addoption(self, parser): methods = self.pyplugins.listattr("pytest_addoption", reverse=True) mc = py._com.MultiCall(methods, parser=parser) mc.execute() - def pyevent__plugin_registered(self, plugin): + def pytest_plugin_registered(self, plugin): if hasattr(self, '_config'): self.pyplugins.call_plugin(plugin, "pytest_addoption", parser=self._config._parser) self.pyplugins.call_plugin(plugin, "pytest_configure", config=self._config) diff --git a/py/test/runner.py b/py/test/runner.py index 25a02cd80..6684a8fae 100644 --- a/py/test/runner.py +++ b/py/test/runner.py @@ -100,6 +100,7 @@ class BaseReport(object): class ItemTestReport(BaseReport): failed = passed = skipped = False + # XXX rename colitem to item here def __init__(self, colitem, excinfo=None, when=None, outerr=None): self.colitem = colitem if colitem and when != "setup": diff --git a/py/test/session.py b/py/test/session.py index aab711a45..c8824ea43 100644 --- a/py/test/session.py +++ b/py/test/session.py @@ -34,20 +34,19 @@ class Session(object): colitems[:] = list(next) + colitems continue assert self.bus is next.config.bus - notify = self.bus.notify if isinstance(next, Item): remaining = self.filteritems([next]) if remaining: - notify("itemstart", next) + self.config.api.pytest_itemstart(item=next) yield next else: assert isinstance(next, Collector) - notify("collectionstart", next) + self.config.api.pytest_collectstart(collector=next) rep = basic_collect_report(next) if rep.passed: for x in self.genitems(rep.result, keywordexpr): yield x - notify("collectreport", rep) + self.config.api.pytest_collectreport(rep=rep) if self.shouldstop: break @@ -81,12 +80,12 @@ class Session(object): """ setup any neccessary resources ahead of the test run. """ self.bus.notify("testrunstart") - def pyevent__itemtestreport(self, rep): + def pytest_itemtestreport(self, rep): if rep.failed: self._testsfailed = True if self.config.option.exitfirst: self.shouldstop = True - pyevent__collectreport = pyevent__itemtestreport + pytest_collectreport = pytest_itemtestreport def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ diff --git a/py/test/testing/test_collect.py b/py/test/testing/test_collect.py index 55aaed535..f8d6529dc 100644 --- a/py/test/testing/test_collect.py +++ b/py/test/testing/test_collect.py @@ -167,7 +167,7 @@ class TestCollectPluginHooks: assert "hello" in wascalled assert "world" in wascalled # make sure the directories do not get double-appended - colreports = sorter.getreports(names="collectreport") + colreports = sorter.getreports("collectreport") names = [rep.colitem.name for rep in colreports] assert names.count("hello") == 1 diff --git a/py/test/testing/test_pytestplugin.py b/py/test/testing/test_pytestplugin.py index 8b04dd263..b24bffd15 100644 --- a/py/test/testing/test_pytestplugin.py +++ b/py/test/testing/test_pytestplugin.py @@ -69,12 +69,12 @@ class TestBootstrapping: assert plugins.getplugin("plug1").__class__.__name__ == "Plug1Plugin" assert plugins.getplugin("plug2").__class__.__name__ == "Plug2Plugin" - def test_consider_module_import_module(self, testdir, EventRecorder): + def test_consider_module_import_module(self, testdir): mod = py.std.new.module("x") mod.pytest_plugins = "pytest_a" aplugin = testdir.makepyfile(pytest_a="""class APlugin: pass""") plugins = PytestPlugins() - sorter = EventRecorder(plugins) + sorter = testdir.geteventrecorder(plugins) #syspath.prepend(aplugin.dirpath()) py.std.sys.path.insert(0, str(aplugin.dirpath())) plugins.consider_module(mod) diff --git a/py/test/testing/test_runner.py b/py/test/testing/test_runner.py index 934993bf8..cca4b6045 100644 --- a/py/test/testing/test_runner.py +++ b/py/test/testing/test_runner.py @@ -27,7 +27,7 @@ class TestSetupState: setup = SetupState() res = setup.do_setup(item) assert not res - rep = evrec.popcall("itemsetupreport").rep + rep = evrec.popcall(pytest_itemsetupreport).rep assert rep.failed assert not rep.skipped assert rep.excrepr @@ -46,10 +46,10 @@ class TestSetupState: setup = SetupState() res = setup.do_setup(item) assert res - rep = evrec.popcall("itemsetupreport").rep + rep = evrec.popcall(pytest_itemsetupreport).rep assert rep.passed setup.do_teardown(item) - rep = evrec.popcall("itemsetupreport").rep + rep = evrec.popcall(pytest_itemsetupreport).rep assert rep.item == item assert rep.failed assert not rep.passed @@ -67,7 +67,7 @@ class TestSetupState: evrec = testdir.geteventrecorder(item.config) setup = SetupState() setup.do_setup(item) - rep = evrec.popcall("itemsetupreport").rep + rep = evrec.popcall(pytest_itemsetupreport).rep assert not rep.failed assert rep.skipped assert rep.excrepr @@ -78,7 +78,7 @@ class TestSetupState: evrec = testdir.geteventrecorder(item.config) setup = SetupState() setup.do_fixture_and_runtest(item) - rep = evrec.popcall("itemtestreport").rep + rep = evrec.popcall(pytest_itemtestreport).rep assert rep.passed def test_runtest_fails(self, testdir): @@ -86,7 +86,7 @@ class TestSetupState: evrec = testdir.geteventrecorder(item.config) setup = SetupState() setup.do_fixture_and_runtest(item) - event = evrec.popcall("item_runtest_finished") + event = evrec.popcall(pytest_item_runtest_finished) assert event.excinfo diff --git a/py/test/testing/test_session.py b/py/test/testing/test_session.py index a4775b25a..fd2ac3d9a 100644 --- a/py/test/testing/test_session.py +++ b/py/test/testing/test_session.py @@ -27,7 +27,7 @@ class SessionTests: assert failed[2].colitem.name == "test_two" itemstarted = sorter.getcalls("itemstart") assert len(itemstarted) == 4 - colstarted = sorter.getcalls("collectionstart") + colstarted = sorter.getcalls("collectstart") assert len(colstarted) == 1 col = colstarted[0].collector assert isinstance(col, py.test.collect.Module) @@ -199,10 +199,10 @@ class TestNewSession(SessionTests): ) sorter = testdir.inline_run('--collectonly', p.dirpath()) - itemstarted = sorter.getcalls("itemstart") + itemstarted = sorter.getcalls("pytest_itemstart") assert len(itemstarted) == 3 assert not sorter.getreports("itemtestreport") - started = sorter.getcalls("collectionstart") + started = sorter.getcalls("pytest_collectstart") finished = sorter.getreports("collectreport") assert len(started) == len(finished) assert len(started) == 8