only consider matching conftest plugins for discovering hooks related to collection nodes.
--HG-- branch : trunk
This commit is contained in:
		
							parent
							
								
									9d01975c78
								
							
						
					
					
						commit
						631dfe9f13
					
				|  | @ -9,6 +9,10 @@ Changes between 1.X and 1.1.1 | ||||||
| 
 | 
 | ||||||
| - new "pytestconfig" funcarg allows access to test config object | - new "pytestconfig" funcarg allows access to test config object | ||||||
| 
 | 
 | ||||||
|  | - collection/item node specific runtest/collect hooks are only called exactly | ||||||
|  |   on matching conftest.py files, i.e. ones which are exactly below | ||||||
|  |   the filesystem path of an item | ||||||
|  | 
 | ||||||
| - robustify capturing to survive if custom pytest_runtest_setup  | - robustify capturing to survive if custom pytest_runtest_setup  | ||||||
|   code failed and prevented the capturing setup code from running.  |   code failed and prevented the capturing setup code from running.  | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,18 @@ def configproperty(name): | ||||||
|         return self.config._getcollectclass(name, self.fspath) |         return self.config._getcollectclass(name, self.fspath) | ||||||
|     return property(fget) |     return property(fget) | ||||||
| 
 | 
 | ||||||
|  | class HookProxy: | ||||||
|  |     def __init__(self, node): | ||||||
|  |         self.node = node | ||||||
|  |     def __getattr__(self, name): | ||||||
|  |         if name[0] == "_": | ||||||
|  |             raise AttributeError(name) | ||||||
|  |         hookmethod = getattr(self.node.config.hook, name) | ||||||
|  |         def call_matching_hooks(**kwargs): | ||||||
|  |             plugins = self.node.config.getmatchingplugins(self.node.fspath) | ||||||
|  |             return hookmethod.pcall(plugins, **kwargs) | ||||||
|  |         return call_matching_hooks | ||||||
|  | 
 | ||||||
| class Node(object):  | class Node(object):  | ||||||
|     """ base class for Nodes in the collection tree.   |     """ base class for Nodes in the collection tree.   | ||||||
|         Collector nodes have children and  |         Collector nodes have children and  | ||||||
|  | @ -29,6 +41,7 @@ class Node(object): | ||||||
|         self.parent = parent |         self.parent = parent | ||||||
|         self.config = getattr(parent, 'config', None) |         self.config = getattr(parent, 'config', None) | ||||||
|         self.fspath = getattr(parent, 'fspath', None)  |         self.fspath = getattr(parent, 'fspath', None)  | ||||||
|  |         self.ihook = HookProxy(self) | ||||||
| 
 | 
 | ||||||
|     def _checkcollectable(self): |     def _checkcollectable(self): | ||||||
|         if not hasattr(self, 'fspath'): |         if not hasattr(self, 'fspath'): | ||||||
|  | @ -426,13 +439,12 @@ class Directory(FSCollector): | ||||||
|         return res |         return res | ||||||
| 
 | 
 | ||||||
|     def consider_file(self, path): |     def consider_file(self, path): | ||||||
|         return self.config.hook.pytest_collect_file(path=path, parent=self) |         return self.ihook.pytest_collect_file(path=path, parent=self) | ||||||
| 
 | 
 | ||||||
|     def consider_dir(self, path, usefilters=None): |     def consider_dir(self, path, usefilters=None): | ||||||
|         if usefilters is not None: |         if usefilters is not None: | ||||||
|             py.log._apiwarn("0.99", "usefilters argument not needed") |             py.log._apiwarn("0.99", "usefilters argument not needed") | ||||||
|         return self.config.hook.pytest_collect_directory( |         return self.ihook.pytest_collect_directory(path=path, parent=self) | ||||||
|             path=path, parent=self) |  | ||||||
| 
 | 
 | ||||||
| class Item(Node):  | class Item(Node):  | ||||||
|     """ a basic test item. """ |     """ a basic test item. """ | ||||||
|  |  | ||||||
|  | @ -45,6 +45,13 @@ class Config(object): | ||||||
|         self.trace("loaded conftestmodule %r" %(conftestmodule,)) |         self.trace("loaded conftestmodule %r" %(conftestmodule,)) | ||||||
|         self.pluginmanager.consider_conftest(conftestmodule) |         self.pluginmanager.consider_conftest(conftestmodule) | ||||||
| 
 | 
 | ||||||
|  |     def getmatchingplugins(self, fspath): | ||||||
|  |         conftests = self._conftest._conftestpath2mod.values() | ||||||
|  |         plugins = [x for x in self.pluginmanager.getplugins()  | ||||||
|  |                         if x not in conftests] | ||||||
|  |         plugins += self._conftest.getconftestmodules(fspath) | ||||||
|  |         return plugins | ||||||
|  | 
 | ||||||
|     def trace(self, msg): |     def trace(self, msg): | ||||||
|         if getattr(self.option, 'traceconfig', None): |         if getattr(self.option, 'traceconfig', None): | ||||||
|             self.hook.pytest_trace(category="config", msg=msg) |             self.hook.pytest_trace(category="config", msg=msg) | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ class Conftest(object): | ||||||
|     def __init__(self, onimport=None): |     def __init__(self, onimport=None): | ||||||
|         self._path2confmods = {} |         self._path2confmods = {} | ||||||
|         self._onimport = onimport |         self._onimport = onimport | ||||||
|  |         self._conftestpath2mod = {} | ||||||
| 
 | 
 | ||||||
|     def setinitial(self, args): |     def setinitial(self, args): | ||||||
|         """ try to find a first anchor path for looking up global values |         """ try to find a first anchor path for looking up global values | ||||||
|  | @ -65,17 +66,20 @@ class Conftest(object): | ||||||
|         raise KeyError(name) |         raise KeyError(name) | ||||||
| 
 | 
 | ||||||
|     def importconftest(self, conftestpath): |     def importconftest(self, conftestpath): | ||||||
|         # Using caching here looks redundant since ultimately |  | ||||||
|         # sys.modules caches already  |  | ||||||
|         assert conftestpath.check(), conftestpath |         assert conftestpath.check(), conftestpath | ||||||
|         if not conftestpath.dirpath('__init__.py').check(file=1):  |         try: | ||||||
|             # HACK: we don't want any "globally" imported conftest.py,  |             return self._conftestpath2mod[conftestpath] | ||||||
|             #       prone to conflicts and subtle problems  |         except KeyError: | ||||||
|             modname = str(conftestpath).replace('.', conftestpath.sep) |             if not conftestpath.dirpath('__init__.py').check(file=1):  | ||||||
|             mod = conftestpath.pyimport(modname=modname) |                 # HACK: we don't want any "globally" imported conftest.py,  | ||||||
|         else: |                 #       prone to conflicts and subtle problems  | ||||||
|             mod = conftestpath.pyimport() |                 modname = str(conftestpath).replace('.', conftestpath.sep) | ||||||
|         return self._postimport(mod) |                 mod = conftestpath.pyimport(modname=modname) | ||||||
|  |             else: | ||||||
|  |                 mod = conftestpath.pyimport() | ||||||
|  |             self._postimport(mod) | ||||||
|  |             self._conftestpath2mod[conftestpath] = mod | ||||||
|  |             return mod | ||||||
| 
 | 
 | ||||||
|     def _postimport(self, mod): |     def _postimport(self, mod): | ||||||
|         if self._onimport: |         if self._onimport: | ||||||
|  |  | ||||||
|  | @ -223,7 +223,7 @@ class DSession(Session): | ||||||
|                     nodes = self.item2nodes.setdefault(item, []) |                     nodes = self.item2nodes.setdefault(item, []) | ||||||
|                     assert node not in nodes |                     assert node not in nodes | ||||||
|                     nodes.append(node) |                     nodes.append(node) | ||||||
|                     self.config.hook.pytest_itemstart(item=item, node=node) |                     item.ihook.pytest_itemstart(item=item, node=node) | ||||||
|             tosend[:] = tosend[room:]  # update inplace |             tosend[:] = tosend[room:]  # update inplace | ||||||
|         if tosend: |         if tosend: | ||||||
|             # we have some left, give it to the main loop |             # we have some left, give it to the main loop | ||||||
|  | @ -242,7 +242,7 @@ class DSession(Session): | ||||||
|                     #    "sending same item %r to multiple " |                     #    "sending same item %r to multiple " | ||||||
|                     #    "not implemented" %(item,)) |                     #    "not implemented" %(item,)) | ||||||
|                     self.item2nodes.setdefault(item, []).append(node) |                     self.item2nodes.setdefault(item, []).append(node) | ||||||
|                     self.config.hook.pytest_itemstart(item=item, node=node) |                     item.ihook.pytest_itemstart(item=item, node=node) | ||||||
|                 pending.extend(sending) |                 pending.extend(sending) | ||||||
|                 tosend[:] = tosend[room:]  # update inplace |                 tosend[:] = tosend[room:]  # update inplace | ||||||
|                 if not tosend: |                 if not tosend: | ||||||
|  | @ -267,7 +267,7 @@ class DSession(Session): | ||||||
|         info = "!!! Node %r crashed during running of test %r" %(node, item) |         info = "!!! Node %r crashed during running of test %r" %(node, item) | ||||||
|         rep = runner.ItemTestReport(item=item, excinfo=info, when="???") |         rep = runner.ItemTestReport(item=item, excinfo=info, when="???") | ||||||
|         rep.node = node |         rep.node = node | ||||||
|         self.config.hook.pytest_runtest_logreport(report=rep) |         item.ihook.pytest_runtest_logreport(report=rep) | ||||||
| 
 | 
 | ||||||
|     def setup(self): |     def setup(self): | ||||||
|         """ setup any neccessary resources ahead of the test run. """ |         """ setup any neccessary resources ahead of the test run. """ | ||||||
|  |  | ||||||
|  | @ -93,7 +93,7 @@ class FuncargRequest: | ||||||
|         self.fspath = pyfuncitem.fspath |         self.fspath = pyfuncitem.fspath | ||||||
|         if hasattr(pyfuncitem, '_requestparam'): |         if hasattr(pyfuncitem, '_requestparam'): | ||||||
|             self.param = pyfuncitem._requestparam  |             self.param = pyfuncitem._requestparam  | ||||||
|         self._plugins = self.config.pluginmanager.getplugins() |         self._plugins = self.config.getmatchingplugins(self.fspath) | ||||||
|         self._plugins.append(self.module) |         self._plugins.append(self.module) | ||||||
|         if self.instance is not None: |         if self.instance is not None: | ||||||
|             self._plugins.append(self.instance) |             self._plugins.append(self.instance) | ||||||
|  |  | ||||||
|  | @ -136,8 +136,8 @@ class PluginManager(object): | ||||||
|     # API for interacting with registered and instantiated plugin objects  |     # API for interacting with registered and instantiated plugin objects  | ||||||
|     # |     # | ||||||
|     #  |     #  | ||||||
|     def listattr(self, attrname, plugins=None, extra=()): |     def listattr(self, attrname, plugins=None): | ||||||
|         return self.registry.listattr(attrname, plugins=plugins, extra=extra) |         return self.registry.listattr(attrname, plugins=plugins) | ||||||
| 
 | 
 | ||||||
|     def notify_exception(self, excinfo=None): |     def notify_exception(self, excinfo=None): | ||||||
|         if excinfo is None: |         if excinfo is None: | ||||||
|  | @ -271,12 +271,11 @@ class Registry: | ||||||
|     def __iter__(self): |     def __iter__(self): | ||||||
|         return iter(self._plugins) |         return iter(self._plugins) | ||||||
| 
 | 
 | ||||||
|     def listattr(self, attrname, plugins=None, extra=(), reverse=False): |     def listattr(self, attrname, plugins=None, reverse=False): | ||||||
|         l = [] |         l = [] | ||||||
|         if plugins is None: |         if plugins is None: | ||||||
|             plugins = self._plugins |             plugins = self._plugins | ||||||
|         candidates = list(plugins) + list(extra) |         for plugin in plugins: | ||||||
|         for plugin in candidates: |  | ||||||
|             try: |             try: | ||||||
|                 l.append(getattr(plugin, attrname)) |                 l.append(getattr(plugin, attrname)) | ||||||
|             except AttributeError: |             except AttributeError: | ||||||
|  | @ -291,32 +290,29 @@ class HookRelay: | ||||||
|         self._registry = registry |         self._registry = registry | ||||||
|         for name, method in vars(hookspecs).items(): |         for name, method in vars(hookspecs).items(): | ||||||
|             if name[:1] != "_": |             if name[:1] != "_": | ||||||
|                 setattr(self, name, self._makecall(name)) |                 firstresult = getattr(method, 'firstresult', False) | ||||||
| 
 |                 hc = HookCaller(self, name, firstresult=firstresult) | ||||||
|     def _makecall(self, name, extralookup=None): |                 setattr(self, name, hc) | ||||||
|         hookspecmethod = getattr(self._hookspecs, name) |  | ||||||
|         firstresult = getattr(hookspecmethod, 'firstresult', False) |  | ||||||
|         return HookCaller(self, name, firstresult=firstresult, |  | ||||||
|             extralookup=extralookup) |  | ||||||
| 
 |  | ||||||
|     def _getmethods(self, name, extralookup=()): |  | ||||||
|         return self._registry.listattr(name, extra=extralookup) |  | ||||||
| 
 | 
 | ||||||
|     def _performcall(self, name, multicall): |     def _performcall(self, name, multicall): | ||||||
|         return multicall.execute() |         return multicall.execute() | ||||||
|          |          | ||||||
| class HookCaller: | class HookCaller: | ||||||
|     def __init__(self, hookrelay, name, firstresult, extralookup=None): |     def __init__(self, hookrelay, name, firstresult): | ||||||
|         self.hookrelay = hookrelay  |         self.hookrelay = hookrelay  | ||||||
|         self.name = name  |         self.name = name  | ||||||
|         self.firstresult = firstresult  |         self.firstresult = firstresult  | ||||||
|         self.extralookup = extralookup and [extralookup] or () |  | ||||||
| 
 | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return "<HookCaller %r>" %(self.name,) |         return "<HookCaller %r>" %(self.name,) | ||||||
| 
 | 
 | ||||||
|     def __call__(self, **kwargs): |     def __call__(self, **kwargs): | ||||||
|         methods = self.hookrelay._getmethods(self.name, self.extralookup) |         methods = self.hookrelay._registry.listattr(self.name) | ||||||
|  |         mc = MultiCall(methods, kwargs, firstresult=self.firstresult) | ||||||
|  |         return self.hookrelay._performcall(self.name, mc) | ||||||
|  | 
 | ||||||
|  |     def pcall(self, plugins, **kwargs): | ||||||
|  |         methods = self.hookrelay._registry.listattr(self.name, plugins=plugins) | ||||||
|         mc = MultiCall(methods, kwargs, firstresult=self.firstresult) |         mc = MultiCall(methods, kwargs, firstresult=self.firstresult) | ||||||
|         return self.hookrelay._performcall(self.name, mc) |         return self.hookrelay._performcall(self.name, mc) | ||||||
|     |     | ||||||
|  |  | ||||||
|  | @ -120,7 +120,7 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector): | ||||||
|             return self.join(name) |             return self.join(name) | ||||||
| 
 | 
 | ||||||
|     def makeitem(self, name, obj): |     def makeitem(self, name, obj): | ||||||
|         return self.config.hook.pytest_pycollect_makeitem( |         return self.ihook.pytest_pycollect_makeitem( | ||||||
|             collector=self, name=name, obj=obj) |             collector=self, name=name, obj=obj) | ||||||
| 
 | 
 | ||||||
|     def _istestclasscandidate(self, name, obj): |     def _istestclasscandidate(self, name, obj): | ||||||
|  | @ -137,9 +137,9 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector): | ||||||
|         cls = clscol and clscol.obj or None |         cls = clscol and clscol.obj or None | ||||||
|         metafunc = funcargs.Metafunc(funcobj, config=self.config,  |         metafunc = funcargs.Metafunc(funcobj, config=self.config,  | ||||||
|             cls=cls, module=module) |             cls=cls, module=module) | ||||||
|         gentesthook = self.config.hook._makecall( |         gentesthook = self.config.hook.pytest_generate_tests | ||||||
|             "pytest_generate_tests", extralookup=module) |         plugins = self.config.getmatchingplugins(self.fspath) + [module] | ||||||
|         gentesthook(metafunc=metafunc) |         gentesthook.pcall(plugins, metafunc=metafunc) | ||||||
|         if not metafunc._calls: |         if not metafunc._calls: | ||||||
|             return self.Function(name, parent=self) |             return self.Function(name, parent=self) | ||||||
|         return funcargs.FunctionCollector(name=name,  |         return funcargs.FunctionCollector(name=name,  | ||||||
|  | @ -338,7 +338,7 @@ class Function(FunctionMixin, py.test.collect.Item): | ||||||
| 
 | 
 | ||||||
|     def runtest(self): |     def runtest(self): | ||||||
|         """ execute the underlying test function. """ |         """ execute the underlying test function. """ | ||||||
|         self.config.hook.pytest_pyfunc_call(pyfuncitem=self) |         self.ihook.pytest_pyfunc_call(pyfuncitem=self) | ||||||
| 
 | 
 | ||||||
|     def setup(self): |     def setup(self): | ||||||
|         super(Function, self).setup() |         super(Function, self).setup() | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ def pytest_runtest_protocol(item): | ||||||
|     if item.config.getvalue("boxed"): |     if item.config.getvalue("boxed"): | ||||||
|         reports = forked_run_report(item)  |         reports = forked_run_report(item)  | ||||||
|         for rep in reports: |         for rep in reports: | ||||||
|             item.config.hook.pytest_runtest_logreport(report=rep) |             item.ihook.pytest_runtest_logreport(report=rep) | ||||||
|     else: |     else: | ||||||
|         runtestprotocol(item) |         runtestprotocol(item) | ||||||
|     return True |     return True | ||||||
|  | @ -85,7 +85,7 @@ def pytest_report_teststatus(report): | ||||||
| 
 | 
 | ||||||
| def call_and_report(item, when, log=True): | def call_and_report(item, when, log=True): | ||||||
|     call = call_runtest_hook(item, when) |     call = call_runtest_hook(item, when) | ||||||
|     hook = item.config.hook |     hook = item.ihook | ||||||
|     report = hook.pytest_runtest_makereport(item=item, call=call) |     report = hook.pytest_runtest_makereport(item=item, call=call) | ||||||
|     if log and (when == "call" or not report.passed): |     if log and (when == "call" or not report.passed): | ||||||
|         hook.pytest_runtest_logreport(report=report)  |         hook.pytest_runtest_logreport(report=report)  | ||||||
|  | @ -93,8 +93,8 @@ def call_and_report(item, when, log=True): | ||||||
| 
 | 
 | ||||||
| def call_runtest_hook(item, when): | def call_runtest_hook(item, when): | ||||||
|     hookname = "pytest_runtest_" + when  |     hookname = "pytest_runtest_" + when  | ||||||
|     hook = getattr(item.config.hook, hookname) |     ihook = getattr(item.ihook, hookname) | ||||||
|     return CallInfo(lambda: hook(item=item), when=when) |     return CallInfo(lambda: ihook(item=item), when=when) | ||||||
| 
 | 
 | ||||||
| class CallInfo: | class CallInfo: | ||||||
|     excinfo = None  |     excinfo = None  | ||||||
|  |  | ||||||
|  | @ -482,3 +482,25 @@ class TestGenfuncFunctional: | ||||||
|             "*test_myfunc*world*FAIL*",  |             "*test_myfunc*world*FAIL*",  | ||||||
|             "*1 failed, 1 passed*" |             "*1 failed, 1 passed*" | ||||||
|         ]) |         ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_conftest_funcargs_only_available_in_subdir(testdir): | ||||||
|  |     sub1 = testdir.mkpydir("sub1") | ||||||
|  |     sub2 = testdir.mkpydir("sub2") | ||||||
|  |     sub1.join("conftest.py").write(py.code.Source(""" | ||||||
|  |         import py | ||||||
|  |         def pytest_funcarg__arg1(request): | ||||||
|  |             py.test.raises(Exception, "request.getfuncargvalue('arg2')") | ||||||
|  |     """)) | ||||||
|  |     sub2.join("conftest.py").write(py.code.Source(""" | ||||||
|  |         import py | ||||||
|  |         def pytest_funcarg__arg2(request): | ||||||
|  |             py.test.raises(Exception, "request.getfuncargvalue('arg1')") | ||||||
|  |     """)) | ||||||
|  | 
 | ||||||
|  |     sub1.join("test_in_sub1.py").write("def test_1(arg1): pass") | ||||||
|  |     sub2.join("test_in_sub2.py").write("def test_2(arg2): pass") | ||||||
|  |     result = testdir.runpytest("-v") | ||||||
|  |     result.stdout.fnmatch_lines([ | ||||||
|  |         "*2 passed*" | ||||||
|  |     ]) | ||||||
|  |  | ||||||
|  | @ -395,12 +395,6 @@ class TestRegistry: | ||||||
|         l = list(plugins.listattr('x', reverse=True)) |         l = list(plugins.listattr('x', reverse=True)) | ||||||
|         assert l == [43, 42, 41] |         assert l == [43, 42, 41] | ||||||
| 
 | 
 | ||||||
|         class api4:  |  | ||||||
|             x = 44 |  | ||||||
|         l = list(plugins.listattr('x', extra=(api4,))) |  | ||||||
|         assert l == [41,42,43,44] |  | ||||||
|         assert len(list(plugins)) == 3  # otherwise extra added |  | ||||||
| 
 |  | ||||||
| class TestHookRelay: | class TestHookRelay: | ||||||
|     def test_happypath(self): |     def test_happypath(self): | ||||||
|         registry = Registry() |         registry = Registry() | ||||||
|  | @ -441,23 +435,3 @@ class TestHookRelay: | ||||||
|         res = mcm.hello(arg=3) |         res = mcm.hello(arg=3) | ||||||
|         assert res == 4 |         assert res == 4 | ||||||
| 
 | 
 | ||||||
|     def test_hooks_extra_plugins(self): |  | ||||||
|         registry = Registry() |  | ||||||
|         class Api: |  | ||||||
|             def hello(self, arg): |  | ||||||
|                 pass |  | ||||||
|         hookrelay = HookRelay(hookspecs=Api, registry=registry) |  | ||||||
|         hook_hello = hookrelay.hello |  | ||||||
|         class Plugin: |  | ||||||
|             def hello(self, arg): |  | ||||||
|                 return arg + 1 |  | ||||||
|         registry.register(Plugin()) |  | ||||||
|         class Plugin2: |  | ||||||
|             def hello(self, arg): |  | ||||||
|                 return arg + 2 |  | ||||||
|         newhook = hookrelay._makecall("hello", extralookup=Plugin2()) |  | ||||||
|         l = newhook(arg=3) |  | ||||||
|         assert l == [5, 4] |  | ||||||
|         l2 = hook_hello(arg=3) |  | ||||||
|         assert l2 == [4] |  | ||||||
|          |  | ||||||
|  |  | ||||||
|  | @ -461,3 +461,49 @@ class TestReportinfo: | ||||||
|                 def test_method(self): |                 def test_method(self): | ||||||
|                     pass |                     pass | ||||||
|        """ |        """ | ||||||
|  | 
 | ||||||
|  | def test_setup_only_available_in_subdir(testdir): | ||||||
|  |     sub1 = testdir.mkpydir("sub1") | ||||||
|  |     sub2 = testdir.mkpydir("sub2") | ||||||
|  |     sub1.join("conftest.py").write(py.code.Source(""" | ||||||
|  |         import py | ||||||
|  |         def pytest_runtest_setup(item): | ||||||
|  |             assert item.fspath.purebasename == "test_in_sub1" | ||||||
|  |         def pytest_runtest_call(item): | ||||||
|  |             assert item.fspath.purebasename == "test_in_sub1" | ||||||
|  |         def pytest_runtest_teardown(item): | ||||||
|  |             assert item.fspath.purebasename == "test_in_sub1" | ||||||
|  |     """)) | ||||||
|  |     sub2.join("conftest.py").write(py.code.Source(""" | ||||||
|  |         import py | ||||||
|  |         def pytest_runtest_setup(item): | ||||||
|  |             assert item.fspath.purebasename == "test_in_sub2" | ||||||
|  |         def pytest_runtest_call(item): | ||||||
|  |             assert item.fspath.purebasename == "test_in_sub2" | ||||||
|  |         def pytest_runtest_teardown(item): | ||||||
|  |             assert item.fspath.purebasename == "test_in_sub2" | ||||||
|  |     """)) | ||||||
|  |     sub1.join("test_in_sub1.py").write("def test_1(): pass") | ||||||
|  |     sub2.join("test_in_sub2.py").write("def test_2(): pass") | ||||||
|  |     result = testdir.runpytest("-v", "-s") | ||||||
|  |     result.stdout.fnmatch_lines([ | ||||||
|  |         "*2 passed*" | ||||||
|  |     ]) | ||||||
|  | 
 | ||||||
|  | def test_generate_tests_only_done_in_subdir(testdir): | ||||||
|  |     sub1 = testdir.mkpydir("sub1") | ||||||
|  |     sub2 = testdir.mkpydir("sub2") | ||||||
|  |     sub1.join("conftest.py").write(py.code.Source(""" | ||||||
|  |         def pytest_generate_tests(metafunc): | ||||||
|  |             assert metafunc.function.__name__ == "test_1" | ||||||
|  |     """)) | ||||||
|  |     sub2.join("conftest.py").write(py.code.Source(""" | ||||||
|  |         def pytest_generate_tests(metafunc): | ||||||
|  |             assert metafunc.function.__name__ == "test_2" | ||||||
|  |     """)) | ||||||
|  |     sub1.join("test_in_sub1.py").write("def test_1(): pass") | ||||||
|  |     sub2.join("test_in_sub2.py").write("def test_2(): pass") | ||||||
|  |     result = testdir.runpytest("-v", "-s", sub1, sub2, sub1) | ||||||
|  |     result.stdout.fnmatch_lines([ | ||||||
|  |         "*3 passed*" | ||||||
|  |     ]) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue