majorly refactor collection process
- get rid of py.test.collect.Directory alltogether. - introduce direct node.nodeid attribute - remove now superflous attributes on collect and test reports
This commit is contained in:
		
							parent
							
								
									f181c70d8e
								
							
						
					
					
						commit
						6dac77433e
					
				|  | @ -26,7 +26,8 @@ Changes between 1.3.4 and 2.0.0dev0 | ||||||
|   output on assertion failures for comparisons and other cases (Floris Bruynooghe) |   output on assertion failures for comparisons and other cases (Floris Bruynooghe) | ||||||
| - nose-plugin: pass through type-signature failures in setup/teardown | - nose-plugin: pass through type-signature failures in setup/teardown | ||||||
|   functions instead of not calling them (Ed Singleton) |   functions instead of not calling them (Ed Singleton) | ||||||
| - major refactoring of internal collection handling | - remove py.test.collect.Directory (follows from a major refactoring | ||||||
|  |   and simplification of the collection process) | ||||||
| - majorly reduce py.test core code, shift function/python testing to own plugin | - majorly reduce py.test core code, shift function/python testing to own plugin | ||||||
| - fix issue88 (finding custom test nodes from command line arg) | - fix issue88 (finding custom test nodes from command line arg) | ||||||
| - refine 'tmpdir' creation, will now create basenames better associated | - refine 'tmpdir' creation, will now create basenames better associated | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ see http://pytest.org for documentation and details | ||||||
| 
 | 
 | ||||||
| (c) Holger Krekel and others, 2004-2010 | (c) Holger Krekel and others, 2004-2010 | ||||||
| """ | """ | ||||||
| __version__ = '2.0.0.dev18' | __version__ = '2.0.0.dev19' | ||||||
| 
 | 
 | ||||||
| __all__ = ['config', 'cmdline'] | __all__ = ['config', 'cmdline'] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -126,7 +126,7 @@ def pytest_runtest_protocol(item): | ||||||
|     """ |     """ | ||||||
| pytest_runtest_protocol.firstresult = True | pytest_runtest_protocol.firstresult = True | ||||||
| 
 | 
 | ||||||
| def pytest_runtest_logstart(nodeid, location, fspath): | def pytest_runtest_logstart(nodeid, location): | ||||||
|     """ signal the start of a test run. """ |     """ signal the start of a test run. """ | ||||||
| 
 | 
 | ||||||
| def pytest_runtest_setup(item): | def pytest_runtest_setup(item): | ||||||
|  |  | ||||||
|  | @ -67,7 +67,10 @@ class PluginManager(object): | ||||||
|         self._hints = [] |         self._hints = [] | ||||||
|         self.trace = TagTracer().get("pluginmanage") |         self.trace = TagTracer().get("pluginmanage") | ||||||
|         if os.environ.get('PYTEST_DEBUG'): |         if os.environ.get('PYTEST_DEBUG'): | ||||||
|             self.trace.root.setwriter(sys.stderr.write) |             err = sys.stderr | ||||||
|  |             if hasattr(os, 'dup'): | ||||||
|  |                 err = py.io.dupfile(err) | ||||||
|  |             self.trace.root.setwriter(err.write) | ||||||
|         self.hook = HookRelay([hookspec], pm=self) |         self.hook = HookRelay([hookspec], pm=self) | ||||||
|         self.register(self) |         self.register(self) | ||||||
|         if load: |         if load: | ||||||
|  | @ -370,6 +373,7 @@ class HookCaller: | ||||||
|         self.hookrelay = hookrelay |         self.hookrelay = hookrelay | ||||||
|         self.name = name |         self.name = name | ||||||
|         self.firstresult = firstresult |         self.firstresult = firstresult | ||||||
|  |         self.trace = self.hookrelay.trace | ||||||
| 
 | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return "<HookCaller %r>" %(self.name,) |         return "<HookCaller %r>" %(self.name,) | ||||||
|  | @ -380,10 +384,15 @@ class HookCaller: | ||||||
|         return mc.execute() |         return mc.execute() | ||||||
| 
 | 
 | ||||||
|     def pcall(self, plugins, **kwargs): |     def pcall(self, plugins, **kwargs): | ||||||
|         self.hookrelay.trace(self.name, kwargs) |         self.trace(self.name, kwargs) | ||||||
|  |         self.trace.root.indent += 1 | ||||||
|         methods = self.hookrelay._pm.listattr(self.name, plugins=plugins) |         methods = self.hookrelay._pm.listattr(self.name, plugins=plugins) | ||||||
|         mc = MultiCall(methods, kwargs, firstresult=self.firstresult) |         mc = MultiCall(methods, kwargs, firstresult=self.firstresult) | ||||||
|         return mc.execute() |         res = mc.execute() | ||||||
|  |         if res: | ||||||
|  |             self.trace(res) | ||||||
|  |         self.trace.root.indent -= 1 | ||||||
|  |         return res | ||||||
| 
 | 
 | ||||||
| _preinit = [PluginManager(load=True)] # triggers default plugin importing | _preinit = [PluginManager(load=True)] # triggers default plugin importing | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -133,7 +133,11 @@ class CaptureManager: | ||||||
| 
 | 
 | ||||||
|     def pytest_make_collect_report(self, __multicall__, collector): |     def pytest_make_collect_report(self, __multicall__, collector): | ||||||
|         method = self._getmethod(collector.config, collector.fspath) |         method = self._getmethod(collector.config, collector.fspath) | ||||||
|         self.resumecapture(method) |         try: | ||||||
|  |             self.resumecapture(method) | ||||||
|  |         except ValueError: | ||||||
|  |             return # recursive collect, XXX refactor capturing | ||||||
|  |                    # to allow for more lightweight recursive capturing | ||||||
|         try: |         try: | ||||||
|             rep = __multicall__.execute() |             rep = __multicall__.execute() | ||||||
|         finally: |         finally: | ||||||
|  |  | ||||||
|  | @ -255,7 +255,7 @@ class Config(object): | ||||||
|         ) |         ) | ||||||
|         #: a pluginmanager instance |         #: a pluginmanager instance | ||||||
|         self.pluginmanager = pluginmanager or PluginManager(load=True) |         self.pluginmanager = pluginmanager or PluginManager(load=True) | ||||||
|         self.trace = self.pluginmanager.trace.get("config") |         self.trace = self.pluginmanager.trace.root.get("config") | ||||||
|         self._conftest = Conftest(onimport=self._onimportconftest) |         self._conftest = Conftest(onimport=self._onimportconftest) | ||||||
|         self.hook = self.pluginmanager.hook |         self.hook = self.pluginmanager.hook | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ def pytest_addoption(parser): | ||||||
| def pytest_collect_file(path, parent): | def pytest_collect_file(path, parent): | ||||||
|     config = parent.config |     config = parent.config | ||||||
|     if path.ext == ".py": |     if path.ext == ".py": | ||||||
|         if config.getvalue("doctestmodules"): |         if config.option.doctestmodules: | ||||||
|             return DoctestModule(path, parent) |             return DoctestModule(path, parent) | ||||||
|     elif path.check(fnmatch=config.getvalue("doctestglob")): |     elif path.check(fnmatch=config.getvalue("doctestglob")): | ||||||
|         return DoctestTextfile(path, parent) |         return DoctestTextfile(path, parent) | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import py | import py | ||||||
|  | import os | ||||||
| import time | import time | ||||||
| 
 | 
 | ||||||
| def pytest_addoption(parser): | def pytest_addoption(parser): | ||||||
|  | @ -36,7 +37,9 @@ class LogXML(object): | ||||||
|         self._durations = {} |         self._durations = {} | ||||||
| 
 | 
 | ||||||
|     def _opentestcase(self, report): |     def _opentestcase(self, report): | ||||||
|         names = report.nodenames |         names = report.nodeid.split("::") | ||||||
|  |         names[0] = names[0].replace(os.sep, '.') | ||||||
|  |         names = tuple(names) | ||||||
|         d = {'time': self._durations.pop(names, "0")} |         d = {'time': self._durations.pop(names, "0")} | ||||||
|         names = [x.replace(".py", "") for x in names if x != "()"] |         names = [x.replace(".py", "") for x in names if x != "()"] | ||||||
|         classnames = names[:-1] |         classnames = names[:-1] | ||||||
|  |  | ||||||
|  | @ -105,6 +105,7 @@ class HookRecorder: | ||||||
|         return l |         return l | ||||||
| 
 | 
 | ||||||
|     def contains(self, entries): |     def contains(self, entries): | ||||||
|  |         __tracebackhide__ = True | ||||||
|         from py.builtin import print_ |         from py.builtin import print_ | ||||||
|         i = 0 |         i = 0 | ||||||
|         entries = list(entries) |         entries = list(entries) | ||||||
|  | @ -123,8 +124,7 @@ class HookRecorder: | ||||||
|                     break |                     break | ||||||
|                 print_("NONAMEMATCH", name, "with", call) |                 print_("NONAMEMATCH", name, "with", call) | ||||||
|             else: |             else: | ||||||
|                 raise AssertionError("could not find %r in %r" %( |                 py.test.fail("could not find %r check %r" % (name, check)) | ||||||
|                     name, self.calls[i:])) |  | ||||||
| 
 | 
 | ||||||
|     def popcall(self, name): |     def popcall(self, name): | ||||||
|         for i, call in enumerate(self.calls): |         for i, call in enumerate(self.calls): | ||||||
|  | @ -278,7 +278,16 @@ class TmpTestdir: | ||||||
|     Collection = Collection |     Collection = Collection | ||||||
|     def getnode(self, config, arg): |     def getnode(self, config, arg): | ||||||
|         collection = Collection(config) |         collection = Collection(config) | ||||||
|         return collection.getbyid(collection._normalizearg(arg))[0] |         assert '::' not in str(arg) | ||||||
|  |         p = py.path.local(arg) | ||||||
|  |         x = collection.fspath.bestrelpath(p) | ||||||
|  |         return collection.perform_collect([x], genitems=False)[0] | ||||||
|  | 
 | ||||||
|  |     def getpathnode(self, path): | ||||||
|  |         config = self.parseconfig(path) | ||||||
|  |         collection = Collection(config) | ||||||
|  |         x = collection.fspath.bestrelpath(path) | ||||||
|  |         return collection.perform_collect([x], genitems=False)[0] | ||||||
| 
 | 
 | ||||||
|     def genitems(self, colitems): |     def genitems(self, colitems): | ||||||
|         collection = colitems[0].collection |         collection = colitems[0].collection | ||||||
|  | @ -291,8 +300,9 @@ class TmpTestdir: | ||||||
|         #config = self.parseconfig(*args) |         #config = self.parseconfig(*args) | ||||||
|         config = self.parseconfigure(*args) |         config = self.parseconfigure(*args) | ||||||
|         rec = self.getreportrecorder(config) |         rec = self.getreportrecorder(config) | ||||||
|         items = Collection(config).perform_collect() |         collection = Collection(config) | ||||||
|         return items, rec |         collection.perform_collect() | ||||||
|  |         return collection.items, rec | ||||||
| 
 | 
 | ||||||
|     def runitem(self, source): |     def runitem(self, source): | ||||||
|         # used from runner functional tests |         # used from runner functional tests | ||||||
|  | @ -469,11 +479,12 @@ class TmpTestdir: | ||||||
|         p = py.path.local.make_numbered_dir(prefix="runpytest-", |         p = py.path.local.make_numbered_dir(prefix="runpytest-", | ||||||
|             keep=None, rootdir=self.tmpdir) |             keep=None, rootdir=self.tmpdir) | ||||||
|         args = ('--basetemp=%s' % p, ) + args |         args = ('--basetemp=%s' % p, ) + args | ||||||
|         for x in args: |         #for x in args: | ||||||
|             if '--confcutdir' in str(x): |         #    if '--confcutdir' in str(x): | ||||||
|                 break |         #        break | ||||||
|         else: |         #else: | ||||||
|             args = ('--confcutdir=.',) + args |         #    pass | ||||||
|  |         #    args = ('--confcutdir=.',) + args | ||||||
|         plugins = [x for x in self.plugins if isinstance(x, str)] |         plugins = [x for x in self.plugins if isinstance(x, str)] | ||||||
|         if plugins: |         if plugins: | ||||||
|             args = ('-p', plugins[0]) + args |             args = ('-p', plugins[0]) + args | ||||||
|  | @ -530,7 +541,7 @@ class ReportRecorder(object): | ||||||
|         """ return a testreport whose dotted import path matches """ |         """ return a testreport whose dotted import path matches """ | ||||||
|         l = [] |         l = [] | ||||||
|         for rep in self.getreports(names=names): |         for rep in self.getreports(names=names): | ||||||
|             if not inamepart or inamepart in rep.nodenames: |             if not inamepart or inamepart in rep.nodeid.split("::"): | ||||||
|                 l.append(rep) |                 l.append(rep) | ||||||
|         if not l: |         if not l: | ||||||
|             raise ValueError("could not find test report matching %r: no test reports at all!" % |             raise ValueError("could not find test report matching %r: no test reports at all!" % | ||||||
|  | @ -616,6 +627,8 @@ class LineMatcher: | ||||||
|                 raise ValueError("line %r not found in output" % line) |                 raise ValueError("line %r not found in output" % line) | ||||||
| 
 | 
 | ||||||
|     def fnmatch_lines(self, lines2): |     def fnmatch_lines(self, lines2): | ||||||
|  |         def show(arg1, arg2): | ||||||
|  |             py.builtin.print_(arg1, arg2, file=py.std.sys.stderr) | ||||||
|         lines2 = self._getlines(lines2) |         lines2 = self._getlines(lines2) | ||||||
|         lines1 = self.lines[:] |         lines1 = self.lines[:] | ||||||
|         nextline = None |         nextline = None | ||||||
|  | @ -626,17 +639,17 @@ class LineMatcher: | ||||||
|             while lines1: |             while lines1: | ||||||
|                 nextline = lines1.pop(0) |                 nextline = lines1.pop(0) | ||||||
|                 if line == nextline: |                 if line == nextline: | ||||||
|                     print_("exact match:", repr(line)) |                     show("exact match:", repr(line)) | ||||||
|                     break |                     break | ||||||
|                 elif fnmatch(nextline, line): |                 elif fnmatch(nextline, line): | ||||||
|                     print_("fnmatch:", repr(line)) |                     show("fnmatch:", repr(line)) | ||||||
|                     print_("   with:", repr(nextline)) |                     show("   with:", repr(nextline)) | ||||||
|                     break |                     break | ||||||
|                 else: |                 else: | ||||||
|                     if not nomatchprinted: |                     if not nomatchprinted: | ||||||
|                         print_("nomatch:", repr(line)) |                         show("nomatch:", repr(line)) | ||||||
|                         nomatchprinted = True |                         nomatchprinted = True | ||||||
|                     print_("    and:", repr(nextline)) |                     show("    and:", repr(nextline)) | ||||||
|                 extralines.append(nextline) |                 extralines.append(nextline) | ||||||
|             else: |             else: | ||||||
|                 assert line == nextline |                 py.test.fail("remains unmatched: %r, see stderr" % (line,)) | ||||||
|  |  | ||||||
|  | @ -48,11 +48,10 @@ def pytest_pyfunc_call(__multicall__, pyfuncitem): | ||||||
| def pytest_collect_file(path, parent): | def pytest_collect_file(path, parent): | ||||||
|     ext = path.ext |     ext = path.ext | ||||||
|     pb = path.purebasename |     pb = path.purebasename | ||||||
|     if pb.startswith("test_") or pb.endswith("_test") or \ |     if ext == ".py" and (pb.startswith("test_") or pb.endswith("_test") or | ||||||
|        path in parent.collection._argfspaths: |        parent.collection.isinitpath(path)): | ||||||
|         if ext == ".py": |         return parent.ihook.pytest_pycollect_makemodule( | ||||||
|             return parent.ihook.pytest_pycollect_makemodule( |             path=path, parent=parent) | ||||||
|                 path=path, parent=parent) |  | ||||||
| 
 | 
 | ||||||
| def pytest_pycollect_makemodule(path, parent): | def pytest_pycollect_makemodule(path, parent): | ||||||
|     return Module(path, parent) |     return Module(path, parent) | ||||||
|  | @ -713,11 +712,13 @@ class FuncargRequest: | ||||||
| def showfuncargs(config): | def showfuncargs(config): | ||||||
|     from pytest.plugin.session import Collection |     from pytest.plugin.session import Collection | ||||||
|     collection = Collection(config) |     collection = Collection(config) | ||||||
|     firstid = collection._normalizearg(config.args[0]) |     collection.perform_collect() | ||||||
|     colitem = collection.getbyid(firstid)[0] |     if collection.items: | ||||||
|  |         plugins = getplugins(collection.items[0]) | ||||||
|  |     else: | ||||||
|  |         plugins = getplugins(collection) | ||||||
|     curdir = py.path.local() |     curdir = py.path.local() | ||||||
|     tw = py.io.TerminalWriter() |     tw = py.io.TerminalWriter() | ||||||
|     plugins = getplugins(colitem, withpy=True) |  | ||||||
|     verbose = config.getvalue("verbose") |     verbose = config.getvalue("verbose") | ||||||
|     for plugin in plugins: |     for plugin in plugins: | ||||||
|         available = [] |         available = [] | ||||||
|  |  | ||||||
|  | @ -29,30 +29,12 @@ def pytest_sessionfinish(session, exitstatus): | ||||||
|             session.exitstatus = 1 |             session.exitstatus = 1 | ||||||
| 
 | 
 | ||||||
| class NodeInfo: | class NodeInfo: | ||||||
|     def __init__(self, nodeid, nodenames, fspath, location): |     def __init__(self, location): | ||||||
|         self.nodeid = nodeid |  | ||||||
|         self.nodenames = nodenames |  | ||||||
|         self.fspath = fspath |  | ||||||
|         self.location = location |         self.location = location | ||||||
| 
 | 
 | ||||||
| def getitemnodeinfo(item): |  | ||||||
|     try: |  | ||||||
|         return item._nodeinfo |  | ||||||
|     except AttributeError: |  | ||||||
|         location = item.reportinfo() |  | ||||||
|         location = (str(location[0]), location[1], str(location[2])) |  | ||||||
|         nodenames = tuple(item.listnames()) |  | ||||||
|         nodeid = item.collection.getid(item) |  | ||||||
|         fspath = item.fspath |  | ||||||
|         item._nodeinfo = n = NodeInfo(nodeid, nodenames, fspath, location) |  | ||||||
|         return n |  | ||||||
|          |  | ||||||
| def pytest_runtest_protocol(item): | def pytest_runtest_protocol(item): | ||||||
|     nodeinfo = getitemnodeinfo(item) |  | ||||||
|     item.ihook.pytest_runtest_logstart( |     item.ihook.pytest_runtest_logstart( | ||||||
|         nodeid=nodeinfo.nodeid, |         nodeid=item.nodeid, location=item.location, | ||||||
|         location=nodeinfo.location, |  | ||||||
|         fspath=str(item.fspath), |  | ||||||
|     ) |     ) | ||||||
|     runtestprotocol(item) |     runtestprotocol(item) | ||||||
|     return True |     return True | ||||||
|  | @ -142,16 +124,18 @@ class BaseReport(object): | ||||||
|     failed = property(lambda x: x.outcome == "failed") |     failed = property(lambda x: x.outcome == "failed") | ||||||
|     skipped = property(lambda x: x.outcome == "skipped") |     skipped = property(lambda x: x.outcome == "skipped") | ||||||
| 
 | 
 | ||||||
|  |     @property | ||||||
|  |     def fspath(self): | ||||||
|  |         return self.nodeid.split("::")[0] | ||||||
| 
 | 
 | ||||||
| def pytest_runtest_makereport(item, call): | def pytest_runtest_makereport(item, call): | ||||||
|     nodeinfo = getitemnodeinfo(item) |  | ||||||
|     when = call.when |     when = call.when | ||||||
|     keywords = dict([(x,1) for x in item.keywords]) |     keywords = dict([(x,1) for x in item.keywords]) | ||||||
|     excinfo = call.excinfo |  | ||||||
|     if not call.excinfo: |     if not call.excinfo: | ||||||
|         outcome = "passed" |         outcome = "passed" | ||||||
|         longrepr = None |         longrepr = None | ||||||
|     else: |     else: | ||||||
|  |         excinfo = call.excinfo | ||||||
|         if not isinstance(excinfo, py.code.ExceptionInfo): |         if not isinstance(excinfo, py.code.ExceptionInfo): | ||||||
|             outcome = "failed" |             outcome = "failed" | ||||||
|             longrepr = excinfo |             longrepr = excinfo | ||||||
|  | @ -164,25 +148,18 @@ def pytest_runtest_makereport(item, call): | ||||||
|                 longrepr = item.repr_failure(excinfo) |                 longrepr = item.repr_failure(excinfo) | ||||||
|             else: # exception in setup or teardown |             else: # exception in setup or teardown | ||||||
|                 longrepr = item._repr_failure_py(excinfo) |                 longrepr = item._repr_failure_py(excinfo) | ||||||
|     return TestReport(nodeinfo.nodeid, nodeinfo.nodenames, |     return TestReport(item.nodeid, item.location, | ||||||
|         nodeinfo.fspath, nodeinfo.location, |  | ||||||
|         keywords, outcome, longrepr, when) |         keywords, outcome, longrepr, when) | ||||||
| 
 | 
 | ||||||
| class TestReport(BaseReport): | class TestReport(BaseReport): | ||||||
|     """ Basic test report object (also used for setup and teardown calls if |     """ Basic test report object (also used for setup and teardown calls if | ||||||
|     they fail). |     they fail). | ||||||
|     """ |     """ | ||||||
|     def __init__(self, nodeid, nodenames, fspath, location, |     def __init__(self, nodeid, location, | ||||||
|             keywords, outcome, longrepr, when): |             keywords, outcome, longrepr, when): | ||||||
|         #: normalized collection node id |         #: normalized collection node id | ||||||
|         self.nodeid = nodeid |         self.nodeid = nodeid | ||||||
| 
 | 
 | ||||||
|         #: list of names indicating position in collection tree. |  | ||||||
|         self.nodenames = nodenames |  | ||||||
| 
 |  | ||||||
|         #: the collected path of the file containing the test. |  | ||||||
|         self.fspath = fspath  # where the test was collected |  | ||||||
| 
 |  | ||||||
|         #: a (filesystempath, lineno, domaininfo) tuple indicating the |         #: a (filesystempath, lineno, domaininfo) tuple indicating the | ||||||
|         #: actual location of a test item - it might be different from the |         #: actual location of a test item - it might be different from the | ||||||
|         #: collected one e.g. if a method is inherited from a different module. |         #: collected one e.g. if a method is inherited from a different module. | ||||||
|  | @ -212,39 +189,27 @@ class TeardownErrorReport(BaseReport): | ||||||
|         self.longrepr = longrepr |         self.longrepr = longrepr | ||||||
| 
 | 
 | ||||||
| def pytest_make_collect_report(collector): | def pytest_make_collect_report(collector): | ||||||
|     result = excinfo = None |     call = CallInfo(collector._memocollect, "memocollect") | ||||||
|     try: |  | ||||||
|         result = collector._memocollect() |  | ||||||
|     except KeyboardInterrupt: |  | ||||||
|         raise |  | ||||||
|     except: |  | ||||||
|         excinfo = py.code.ExceptionInfo() |  | ||||||
|     nodenames = tuple(collector.listnames()) |  | ||||||
|     nodeid = collector.collection.getid(collector) |  | ||||||
|     fspath = str(collector.fspath) |  | ||||||
|     reason = longrepr = None |     reason = longrepr = None | ||||||
|     if not excinfo: |     if not call.excinfo: | ||||||
|         outcome = "passed" |         outcome = "passed" | ||||||
|     else: |     else: | ||||||
|         if excinfo.errisinstance(py.test.skip.Exception): |         if call.excinfo.errisinstance(py.test.skip.Exception): | ||||||
|             outcome = "skipped" |             outcome = "skipped" | ||||||
|             reason = str(excinfo.value) |             reason = str(call.excinfo.value) | ||||||
|             longrepr = collector._repr_failure_py(excinfo, "line") |             longrepr = collector._repr_failure_py(call.excinfo, "line") | ||||||
|         else: |         else: | ||||||
|             outcome = "failed" |             outcome = "failed" | ||||||
|             errorinfo = collector.repr_failure(excinfo) |             errorinfo = collector.repr_failure(call.excinfo) | ||||||
|             if not hasattr(errorinfo, "toterminal"): |             if not hasattr(errorinfo, "toterminal"): | ||||||
|                 errorinfo = CollectErrorRepr(errorinfo) |                 errorinfo = CollectErrorRepr(errorinfo) | ||||||
|             longrepr = errorinfo |             longrepr = errorinfo | ||||||
|     return CollectReport(nodenames, nodeid, fspath, |     return CollectReport(collector.nodeid, outcome, longrepr, | ||||||
|         outcome, longrepr, result, reason) |         getattr(call, 'result', None), reason) | ||||||
| 
 | 
 | ||||||
| class CollectReport(BaseReport): | class CollectReport(BaseReport): | ||||||
|     def __init__(self, nodenames, nodeid, fspath, outcome, |     def __init__(self, nodeid, outcome, longrepr, result, reason): | ||||||
|         longrepr, result, reason): |  | ||||||
|         self.nodenames = nodenames |  | ||||||
|         self.nodeid = nodeid |         self.nodeid = nodeid | ||||||
|         self.fspath = fspath |  | ||||||
|         self.outcome = outcome |         self.outcome = outcome | ||||||
|         self.longrepr = longrepr |         self.longrepr = longrepr | ||||||
|         self.result = result or [] |         self.result = result or [] | ||||||
|  | @ -255,7 +220,8 @@ class CollectReport(BaseReport): | ||||||
|         return (self.fspath, None, self.fspath) |         return (self.fspath, None, self.fspath) | ||||||
| 
 | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return "<CollectReport %r outcome=%r>" % (self.nodeid, self.outcome) |         return "<CollectReport %r lenresult=%s outcome=%r>" % ( | ||||||
|  |                 self.nodeid, len(self.result), self.outcome) | ||||||
| 
 | 
 | ||||||
| class CollectErrorRepr(TerminalRepr): | class CollectErrorRepr(TerminalRepr): | ||||||
|     def __init__(self, msg): |     def __init__(self, msg): | ||||||
|  |  | ||||||
|  | @ -14,11 +14,15 @@ EXIT_OK = 0 | ||||||
| EXIT_TESTSFAILED = 1 | EXIT_TESTSFAILED = 1 | ||||||
| EXIT_INTERRUPTED = 2 | EXIT_INTERRUPTED = 2 | ||||||
| EXIT_INTERNALERROR = 3 | EXIT_INTERNALERROR = 3 | ||||||
| EXIT_NOHOSTS = 4 |  | ||||||
| 
 | 
 | ||||||
| def pytest_addoption(parser): | def pytest_addoption(parser): | ||||||
|     parser.addini("norecursedirs", "directory patterns to avoid for recursion", |     parser.addini("norecursedirs", "directory patterns to avoid for recursion", | ||||||
|         type="args", default=('.*', 'CVS', '_darcs', '{arch}')) |         type="args", default=('.*', 'CVS', '_darcs', '{arch}')) | ||||||
|  |     #parser.addini("dirpatterns", | ||||||
|  |     #    "patterns specifying possible locations of test files", | ||||||
|  |     #    type="linelist", default=["**/test_*.txt", | ||||||
|  |     #            "**/test_*.py", "**/*_test.py"] | ||||||
|  |     #) | ||||||
|     group = parser.getgroup("general", "running and selection options") |     group = parser.getgroup("general", "running and selection options") | ||||||
|     group._addoption('-x', '--exitfirst', action="store_true", default=False, |     group._addoption('-x', '--exitfirst', action="store_true", default=False, | ||||||
|                dest="exitfirst", |                dest="exitfirst", | ||||||
|  | @ -44,12 +48,11 @@ def pytest_addoption(parser): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def pytest_namespace(): | def pytest_namespace(): | ||||||
|     return dict(collect=dict(Item=Item, Collector=Collector, |     return dict(collect=dict(Item=Item, Collector=Collector, File=File)) | ||||||
|         File=File, Directory=Directory)) |  | ||||||
|          |          | ||||||
| def pytest_configure(config): | def pytest_configure(config): | ||||||
|     py.test.config = config # compatibiltiy |     py.test.config = config # compatibiltiy | ||||||
|     if config.getvalue("exitfirst"): |     if config.option.exitfirst: | ||||||
|         config.option.maxfail = 1 |         config.option.maxfail = 1 | ||||||
| 
 | 
 | ||||||
| def pytest_cmdline_main(config): | def pytest_cmdline_main(config): | ||||||
|  | @ -84,8 +87,10 @@ def pytest_cmdline_main(config): | ||||||
| def pytest_collection(session): | def pytest_collection(session): | ||||||
|     collection = session.collection |     collection = session.collection | ||||||
|     assert not hasattr(collection, 'items') |     assert not hasattr(collection, 'items') | ||||||
|  | 
 | ||||||
|  |     collection.perform_collect() | ||||||
|     hook = session.config.hook |     hook = session.config.hook | ||||||
|     collection.items = items = collection.perform_collect() |     items = collection.items | ||||||
|     hook.pytest_collection_modifyitems(config=session.config, items=items) |     hook.pytest_collection_modifyitems(config=session.config, items=items) | ||||||
|     hook.pytest_collection_finish(collection=collection) |     hook.pytest_collection_finish(collection=collection) | ||||||
|     return True |     return True | ||||||
|  | @ -108,18 +113,6 @@ def pytest_ignore_collect(path, config): | ||||||
|         ignore_paths.extend([py.path.local(x) for x in excludeopt]) |         ignore_paths.extend([py.path.local(x) for x in excludeopt]) | ||||||
|     return path in ignore_paths |     return path in ignore_paths | ||||||
| 
 | 
 | ||||||
| def pytest_collect_directory(path, parent): |  | ||||||
|     # check if cmdline specified this dir or a subdir directly |  | ||||||
|     for arg in parent.collection._argfspaths: |  | ||||||
|         if path == arg or arg.relto(path): |  | ||||||
|             break |  | ||||||
|     else: |  | ||||||
|         patterns = parent.config.getini("norecursedirs") |  | ||||||
|         for pat in patterns or []: |  | ||||||
|             if path.check(fnmatch=pat): |  | ||||||
|                 return |  | ||||||
|     return Directory(path, parent=parent) |  | ||||||
| 
 |  | ||||||
| class Session(object): | class Session(object): | ||||||
|     class Interrupted(KeyboardInterrupt): |     class Interrupted(KeyboardInterrupt): | ||||||
|         """ signals an interrupted test run. """ |         """ signals an interrupted test run. """ | ||||||
|  | @ -145,160 +138,17 @@ class Session(object): | ||||||
|                     self._testsfailed) |                     self._testsfailed) | ||||||
|     pytest_collectreport = pytest_runtest_logreport |     pytest_collectreport = pytest_runtest_logreport | ||||||
| 
 | 
 | ||||||
| class Collection: |  | ||||||
|     def __init__(self, config): |  | ||||||
|         self.config = config |  | ||||||
|         self.topdir = gettopdir(self.config.args) |  | ||||||
|         self._argfspaths = [py.path.local(decodearg(x)[0]) |  | ||||||
|                                      for x in self.config.args] |  | ||||||
|         x = pytest.collect.Directory(fspath=self.topdir, |  | ||||||
|             config=config, collection=self) |  | ||||||
|         self._topcollector = x.consider_dir(self.topdir) |  | ||||||
|         self._topcollector.parent = None |  | ||||||
| 
 |  | ||||||
|     def _normalizearg(self, arg): |  | ||||||
|         return "::".join(self._parsearg(arg)) |  | ||||||
| 
 |  | ||||||
|     def _parsearg(self, arg, base=None): |  | ||||||
|         """ return normalized name list for a command line specified id |  | ||||||
|         which might be of the form x/y/z::name1::name2 |  | ||||||
|         and should result into the form x::y::z::name1::name2 |  | ||||||
|         """ |  | ||||||
|         if base is None: |  | ||||||
|             base = py.path.local() |  | ||||||
|         parts = str(arg).split("::") |  | ||||||
|         path = base.join(parts[0], abs=True) |  | ||||||
|         if not path.check(): |  | ||||||
|             raise pytest.UsageError("file not found: %s" %(path,)) |  | ||||||
|         topdir = self.topdir |  | ||||||
|         if path != topdir and not path.relto(topdir): |  | ||||||
|             raise pytest.UsageError("path %r is not relative to %r" % |  | ||||||
|                 (str(path), str(topdir))) |  | ||||||
|         topparts = path.relto(topdir).split(path.sep) |  | ||||||
|         return topparts + parts[1:] |  | ||||||
| 
 |  | ||||||
|     def getid(self, node): |  | ||||||
|         """ return id for node, relative to topdir. """ |  | ||||||
|         path = node.fspath |  | ||||||
|         chain = [x for x in node.listchain() if x.fspath == path] |  | ||||||
|         chain = chain[1:] |  | ||||||
|         names = [x.name for x in chain if x.name != "()"] |  | ||||||
|         relpath = path.relto(self.topdir) |  | ||||||
|         if not relpath: |  | ||||||
|             assert path == self.topdir |  | ||||||
|             path = '' |  | ||||||
|         else: |  | ||||||
|             path = relpath |  | ||||||
|             if os.sep != "/": |  | ||||||
|                 path = str(path).replace(os.sep, "/") |  | ||||||
|         names.insert(0, path) |  | ||||||
|         return "::".join(names) |  | ||||||
| 
 |  | ||||||
|     def getbyid(self, id): |  | ||||||
|         """ return one or more nodes matching the id. """ |  | ||||||
|         names = [x for x in id.split("::") if x] |  | ||||||
|         if names and '/' in names[0]: |  | ||||||
|             names[:1] = names[0].split("/") |  | ||||||
|         return list(self.matchnodes([self._topcollector], names)) |  | ||||||
| 
 |  | ||||||
|     def perform_collect(self): |  | ||||||
|         items = [] |  | ||||||
|         for arg in self.config.args: |  | ||||||
|             names = self._parsearg(arg) |  | ||||||
|             try: |  | ||||||
|                 for node in self.matchnodes([self._topcollector], names): |  | ||||||
|                     items.extend(self.genitems(node)) |  | ||||||
|             except NoMatch: |  | ||||||
|                 raise pytest.UsageError("can't collect: %s" % (arg,)) |  | ||||||
|         return items |  | ||||||
| 
 |  | ||||||
|     def matchnodes(self, matching, names): |  | ||||||
|         if not matching: |  | ||||||
|             return |  | ||||||
|         if not names: |  | ||||||
|             for x in matching: |  | ||||||
|                 yield x |  | ||||||
|             return |  | ||||||
|         name = names[0] |  | ||||||
|         names = names[1:] |  | ||||||
|         for node in matching: |  | ||||||
|             if isinstance(node, pytest.collect.Item): |  | ||||||
|                 if not name: |  | ||||||
|                     yield node |  | ||||||
|                 continue |  | ||||||
|             assert isinstance(node, pytest.collect.Collector) |  | ||||||
|             node.ihook.pytest_collectstart(collector=node) |  | ||||||
|             rep = node.ihook.pytest_make_collect_report(collector=node) |  | ||||||
|             #print "matching", rep.result, "against name", name |  | ||||||
|             if rep.passed: |  | ||||||
|                 if not name: |  | ||||||
|                     for x in rep.result: |  | ||||||
|                         yield x |  | ||||||
|                 else: |  | ||||||
|                     matched = False |  | ||||||
|                     for x in rep.result: |  | ||||||
|                         try: |  | ||||||
|                             if x.name == name or x.fspath.basename == name: |  | ||||||
|                                 for x in self.matchnodes([x], names): |  | ||||||
|                                     yield x |  | ||||||
|                                 matched = True |  | ||||||
|                             elif x.name == "()": # XXX special Instance() case |  | ||||||
|                                 for x in self.matchnodes([x], [name] + names): |  | ||||||
|                                     yield x |  | ||||||
|                                 matched = True |  | ||||||
|                         except NoMatch: |  | ||||||
|                             pass |  | ||||||
|                     if not matched: |  | ||||||
|                         node.ihook.pytest_collectreport(report=rep) |  | ||||||
|                         raise NoMatch(name) |  | ||||||
|             node.ihook.pytest_collectreport(report=rep) |  | ||||||
| 
 |  | ||||||
|     def genitems(self, node): |  | ||||||
|         if isinstance(node, pytest.collect.Item): |  | ||||||
|             node.ihook.pytest_itemcollected(item=node) |  | ||||||
|             yield node |  | ||||||
|         else: |  | ||||||
|             assert isinstance(node, pytest.collect.Collector) |  | ||||||
|             node.ihook.pytest_collectstart(collector=node) |  | ||||||
|             rep = node.ihook.pytest_make_collect_report(collector=node) |  | ||||||
|             if rep.passed: |  | ||||||
|                 for subnode in rep.result: |  | ||||||
|                     for x in self.genitems(subnode): |  | ||||||
|                         yield x |  | ||||||
|             node.ihook.pytest_collectreport(report=rep) |  | ||||||
| 
 |  | ||||||
| class NoMatch(Exception): | class NoMatch(Exception): | ||||||
|     """ raised if matching cannot locate a matching names. """ |     """ raised if matching cannot locate a matching names. """ | ||||||
| 
 | 
 | ||||||
| def gettopdir(args): |  | ||||||
|     """ return the top directory for the given paths. |  | ||||||
|         if the common base dir resides in a python package |  | ||||||
|         parent directory of the root package is returned. |  | ||||||
|     """ |  | ||||||
|     fsargs = [py.path.local(decodearg(arg)[0]) for arg in args] |  | ||||||
|     p = fsargs and fsargs[0] or None |  | ||||||
|     for x in fsargs[1:]: |  | ||||||
|         p = p.common(x) |  | ||||||
|     assert p, "cannot determine common basedir of %s" %(fsargs,) |  | ||||||
|     pkgdir = p.pypkgpath() |  | ||||||
|     if pkgdir is None: |  | ||||||
|         if p.check(file=1): |  | ||||||
|             p = p.dirpath() |  | ||||||
|         return p |  | ||||||
|     else: |  | ||||||
|         return pkgdir.dirpath() |  | ||||||
| 
 |  | ||||||
| def decodearg(arg): |  | ||||||
|     arg = str(arg) |  | ||||||
|     return arg.split("::") |  | ||||||
| 
 |  | ||||||
| class HookProxy: | class HookProxy: | ||||||
|     def __init__(self, node): |     def __init__(self, fspath, config): | ||||||
|         self.node = node |         self.fspath = fspath | ||||||
|  |         self.config = config | ||||||
|     def __getattr__(self, name): |     def __getattr__(self, name): | ||||||
|         hookmethod = getattr(self.node.config.hook, name) |         hookmethod = getattr(self.config.hook, name) | ||||||
|         def call_matching_hooks(**kwargs): |         def call_matching_hooks(**kwargs): | ||||||
|             plugins = self.node.config._getmatchingplugins(self.node.fspath) |             plugins = self.config._getmatchingplugins(self.fspath) | ||||||
|             return hookmethod.pcall(plugins, **kwargs) |             return hookmethod.pcall(plugins, **kwargs) | ||||||
|         return call_matching_hooks |         return call_matching_hooks | ||||||
| 
 | 
 | ||||||
|  | @ -329,7 +179,7 @@ class Node(object): | ||||||
|          |          | ||||||
|         #: the file where this item is contained/collected from. |         #: the file where this item is contained/collected from. | ||||||
|         self.fspath = getattr(parent, 'fspath', None) |         self.fspath = getattr(parent, 'fspath', None) | ||||||
|         self.ihook = HookProxy(self) |         self.ihook = self.collection.gethookproxy(self.fspath) | ||||||
|         self.keywords = {self.name: True} |         self.keywords = {self.name: True} | ||||||
| 
 | 
 | ||||||
|     Module = compatproperty("Module") |     Module = compatproperty("Module") | ||||||
|  | @ -339,14 +189,19 @@ class Node(object): | ||||||
|     Item = compatproperty("Item") |     Item = compatproperty("Item") | ||||||
| 
 | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         if getattr(self.config.option, 'debug', False): |         return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None)) | ||||||
|             return "<%s %r %0x>" %(self.__class__.__name__, |  | ||||||
|                 getattr(self, 'name', None), id(self)) |  | ||||||
|         else: |  | ||||||
|             return "<%s %r>" %(self.__class__.__name__, |  | ||||||
|                 getattr(self, 'name', None)) |  | ||||||
| 
 | 
 | ||||||
|     # methods for ordering nodes |     # methods for ordering nodes | ||||||
|  |     @property | ||||||
|  |     def nodeid(self): | ||||||
|  |         try: | ||||||
|  |             return self._nodeid | ||||||
|  |         except AttributeError: | ||||||
|  |             self._nodeid = x = self._makeid() | ||||||
|  |             return x | ||||||
|  | 
 | ||||||
|  |     def _makeid(self): | ||||||
|  |         return self.parent.nodeid + "::" + self.name | ||||||
| 
 | 
 | ||||||
|     def __eq__(self, other): |     def __eq__(self, other): | ||||||
|         if not isinstance(other, Node): |         if not isinstance(other, Node): | ||||||
|  | @ -447,7 +302,7 @@ class Collector(Node): | ||||||
| 
 | 
 | ||||||
|     def _memocollect(self): |     def _memocollect(self): | ||||||
|         """ internal helper method to cache results of calling collect(). """ |         """ internal helper method to cache results of calling collect(). """ | ||||||
|         return self._memoizedcall('_collected', self.collect) |         return self._memoizedcall('_collected', lambda: list(self.collect())) | ||||||
| 
 | 
 | ||||||
|     def _prunetraceback(self, excinfo): |     def _prunetraceback(self, excinfo): | ||||||
|         if hasattr(self, 'fspath'): |         if hasattr(self, 'fspath'): | ||||||
|  | @ -460,55 +315,177 @@ class Collector(Node): | ||||||
| 
 | 
 | ||||||
| class FSCollector(Collector): | class FSCollector(Collector): | ||||||
|     def __init__(self, fspath, parent=None, config=None, collection=None): |     def __init__(self, fspath, parent=None, config=None, collection=None): | ||||||
|         fspath = py.path.local(fspath) |         fspath = py.path.local(fspath) # xxx only for test_resultlog.py? | ||||||
|         super(FSCollector, self).__init__(fspath.basename, |         name = parent and fspath.relto(parent.fspath) or fspath.basename | ||||||
|             parent, config, collection) |         super(FSCollector, self).__init__(name, parent, config, collection) | ||||||
|         self.fspath = fspath |         self.fspath = fspath | ||||||
| 
 | 
 | ||||||
|  |     def _makeid(self): | ||||||
|  |         if self == self.collection: | ||||||
|  |             return "." | ||||||
|  |         relpath = self.collection.fspath.bestrelpath(self.fspath) | ||||||
|  |         if os.sep != "/": | ||||||
|  |             relpath = str(path).replace(os.sep, "/") | ||||||
|  |         return relpath | ||||||
|  | 
 | ||||||
| class File(FSCollector): | class File(FSCollector): | ||||||
|     """ base class for collecting tests from a file. """ |     """ base class for collecting tests from a file. """ | ||||||
| 
 | 
 | ||||||
| class Directory(FSCollector): |  | ||||||
|     def collect(self): |  | ||||||
|         l = [] |  | ||||||
|         for path in self.fspath.listdir(sort=True): |  | ||||||
|             res = self.consider(path) |  | ||||||
|             if res is not None: |  | ||||||
|                 if isinstance(res, (list, tuple)): |  | ||||||
|                     l.extend(res) |  | ||||||
|                 else: |  | ||||||
|                     l.append(res) |  | ||||||
|         return l |  | ||||||
| 
 |  | ||||||
|     def consider(self, path): |  | ||||||
|         if self.ihook.pytest_ignore_collect(path=path, config=self.config): |  | ||||||
|            return |  | ||||||
|         if path.check(file=1): |  | ||||||
|             res = self.consider_file(path) |  | ||||||
|         elif path.check(dir=1): |  | ||||||
|             res = self.consider_dir(path) |  | ||||||
|         else: |  | ||||||
|             res = None |  | ||||||
|         if isinstance(res, list): |  | ||||||
|             # throw out identical results |  | ||||||
|             l = [] |  | ||||||
|             for x in res: |  | ||||||
|                 if x not in l: |  | ||||||
|                     assert x.parent == self, (x.parent, self) |  | ||||||
|                     assert x.fspath == path, (x.fspath, path) |  | ||||||
|                     l.append(x) |  | ||||||
|             res = l |  | ||||||
|         return res |  | ||||||
| 
 |  | ||||||
|     def consider_file(self, path): |  | ||||||
|         return self.ihook.pytest_collect_file(path=path, parent=self) |  | ||||||
| 
 |  | ||||||
|     def consider_dir(self, path): |  | ||||||
|         return self.ihook.pytest_collect_directory(path=path, parent=self) |  | ||||||
| 
 |  | ||||||
| class Item(Node): | class Item(Node): | ||||||
|     """ a basic test invocation item. Note that for a single function |     """ a basic test invocation item. Note that for a single function | ||||||
|     there might be multiple test invocation items. |     there might be multiple test invocation items. | ||||||
|     """ |     """ | ||||||
|     def reportinfo(self): |     def reportinfo(self): | ||||||
|         return self.fspath, None, "" |         return self.fspath, None, "" | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def location(self): | ||||||
|  |         try: | ||||||
|  |             return self._location | ||||||
|  |         except AttributeError: | ||||||
|  |             location = self.reportinfo() | ||||||
|  |             location = (str(location[0]), location[1], str(location[2])) | ||||||
|  |             self._location = location | ||||||
|  |             return location | ||||||
|  | 
 | ||||||
|  | class Collection(FSCollector): | ||||||
|  |     def __init__(self, config): | ||||||
|  |         super(Collection, self).__init__(py.path.local(), parent=None, | ||||||
|  |             config=config, collection=self) | ||||||
|  |         self.trace = config.trace.root.get("collection") | ||||||
|  |         self._norecursepatterns = config.getini("norecursedirs") | ||||||
|  | 
 | ||||||
|  |     def isinitpath(self, path): | ||||||
|  |         return path in self._initialpaths | ||||||
|  | 
 | ||||||
|  |     def gethookproxy(self, fspath): | ||||||
|  |         return HookProxy(fspath, self.config) | ||||||
|  | 
 | ||||||
|  |     def perform_collect(self, args=None, genitems=True): | ||||||
|  |         if args is None: | ||||||
|  |             args = self.config.args | ||||||
|  |         self.trace("perform_collect", self, args) | ||||||
|  |         self.trace.root.indent += 1 | ||||||
|  |         self._notfound = [] | ||||||
|  |         self._initialpaths = set() | ||||||
|  |         self._initialargs = args | ||||||
|  |         for arg in args: | ||||||
|  |             parts = self._parsearg(arg) | ||||||
|  |             self._initialpaths.add(parts[0]) | ||||||
|  |         self.ihook.pytest_collectstart(collector=self) | ||||||
|  |         rep = self.ihook.pytest_make_collect_report(collector=self) | ||||||
|  |         self.ihook.pytest_collectreport(report=rep) | ||||||
|  |         self.trace.root.indent -= 1 | ||||||
|  |         if self._notfound: | ||||||
|  |             for arg, exc in self._notfound: | ||||||
|  |                 line = "no name %r in any of %r" % (exc.args[1], exc.args[0]) | ||||||
|  |                 raise pytest.UsageError("not found: %s\n%s" %(arg, line)) | ||||||
|  |         if not genitems: | ||||||
|  |             return rep.result | ||||||
|  |         else: | ||||||
|  |             self.items = items = [] | ||||||
|  |             if rep.passed: | ||||||
|  |                 for node in rep.result: | ||||||
|  |                     self.items.extend(self.genitems(node)) | ||||||
|  |             return items | ||||||
|  | 
 | ||||||
|  |     def collect(self): | ||||||
|  |         for arg in self._initialargs: | ||||||
|  |             self.trace("processing arg", arg) | ||||||
|  |             self.trace.root.indent += 1 | ||||||
|  |             try: | ||||||
|  |                 for x in self._collect(arg): | ||||||
|  |                     yield x | ||||||
|  |             except NoMatch: | ||||||
|  |                 # we are inside a make_report hook so | ||||||
|  |                 # we cannot directly pass through the exception | ||||||
|  |                 self._notfound.append((arg, sys.exc_info()[1])) | ||||||
|  |                 self.trace.root.indent -= 1 | ||||||
|  |                 break | ||||||
|  |             self.trace.root.indent -= 1 | ||||||
|  | 
 | ||||||
|  |     def _collect(self, arg): | ||||||
|  |         names = self._parsearg(arg) | ||||||
|  |         path = names.pop(0) | ||||||
|  |         if path.check(dir=1): | ||||||
|  |             assert not names, "invalid arg %r" %(arg,) | ||||||
|  |             for path in path.visit(rec=self._recurse, bf=True, sort=True): | ||||||
|  |                 for x in self._collectfile(path): | ||||||
|  |                     yield x | ||||||
|  |         else: | ||||||
|  |             assert path.check(file=1) | ||||||
|  |             for x in self.matchnodes(self._collectfile(path), names): | ||||||
|  |                 yield x | ||||||
|  | 
 | ||||||
|  |     def _collectfile(self, path): | ||||||
|  |         ihook = self.gethookproxy(path) | ||||||
|  |         if ihook.pytest_ignore_collect(path=path, config=self.config): | ||||||
|  |            return () | ||||||
|  |         return ihook.pytest_collect_file(path=path, parent=self) | ||||||
|  | 
 | ||||||
|  |     def _recurse(self, path): | ||||||
|  |         ihook = self.gethookproxy(path) | ||||||
|  |         if ihook.pytest_ignore_collect(path=path, config=self.config): | ||||||
|  |            return | ||||||
|  |         for pat in self._norecursepatterns: | ||||||
|  |             if path.check(fnmatch=pat): | ||||||
|  |                 return False | ||||||
|  |         ihook.pytest_collect_directory(path=path, parent=self) | ||||||
|  |         return True | ||||||
|  | 
 | ||||||
|  |     def _parsearg(self, arg): | ||||||
|  |         """ return (fspath, names) tuple after checking the file exists. """ | ||||||
|  |         parts = str(arg).split("::") | ||||||
|  |         path = self.fspath.join(parts[0], abs=True) | ||||||
|  |         if not path.check(): | ||||||
|  |             raise pytest.UsageError("file not found: %s" %(path,)) | ||||||
|  |         parts[0] = path | ||||||
|  |         return parts | ||||||
|  |     | ||||||
|  |     def matchnodes(self, matching, names): | ||||||
|  |         self.trace("matchnodes", matching, names) | ||||||
|  |         self.trace.root.indent += 1 | ||||||
|  |         nodes = self._matchnodes(matching, names) | ||||||
|  |         num = len(nodes) | ||||||
|  |         self.trace("matchnodes finished -> ", num, "nodes") | ||||||
|  |         self.trace.root.indent -= 1 | ||||||
|  |         if num == 0: | ||||||
|  |             raise NoMatch(matching, names[:1]) | ||||||
|  |         return nodes | ||||||
|  | 
 | ||||||
|  |     def _matchnodes(self, matching, names): | ||||||
|  |         if not matching or not names: | ||||||
|  |             return matching | ||||||
|  |         name = names[0] | ||||||
|  |         assert name | ||||||
|  |         nextnames = names[1:] | ||||||
|  |         resultnodes = [] | ||||||
|  |         for node in matching: | ||||||
|  |             if isinstance(node, pytest.collect.Item): | ||||||
|  |                 resultnodes.append(node) | ||||||
|  |                 continue | ||||||
|  |             assert isinstance(node, pytest.collect.Collector) | ||||||
|  |             node.ihook.pytest_collectstart(collector=node) | ||||||
|  |             rep = node.ihook.pytest_make_collect_report(collector=node) | ||||||
|  |             if rep.passed: | ||||||
|  |                 for x in rep.result: | ||||||
|  |                     if x.name == name: | ||||||
|  |                         resultnodes.extend(self.matchnodes([x], nextnames)) | ||||||
|  |             node.ihook.pytest_collectreport(report=rep) | ||||||
|  |         return resultnodes | ||||||
|  | 
 | ||||||
|  |     def genitems(self, node): | ||||||
|  |         self.trace("genitems", node) | ||||||
|  |         if isinstance(node, pytest.collect.Item): | ||||||
|  |             node.ihook.pytest_itemcollected(item=node) | ||||||
|  |             yield node | ||||||
|  |         else: | ||||||
|  |             assert isinstance(node, pytest.collect.Collector) | ||||||
|  |             node.ihook.pytest_collectstart(collector=node) | ||||||
|  |             rep = node.ihook.pytest_make_collect_report(collector=node) | ||||||
|  |             if rep.passed: | ||||||
|  |                 for subnode in rep.result: | ||||||
|  |                     for x in self.genitems(subnode): | ||||||
|  |                         yield x | ||||||
|  |             node.ihook.pytest_collectreport(report=rep) | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -115,10 +115,10 @@ class TerminalReporter: | ||||||
|     def write_fspath_result(self, fspath, res): |     def write_fspath_result(self, fspath, res): | ||||||
|         if fspath != self.currentfspath: |         if fspath != self.currentfspath: | ||||||
|             self.currentfspath = fspath |             self.currentfspath = fspath | ||||||
|             fspath = self.curdir.bestrelpath(fspath) |             #fspath = self.curdir.bestrelpath(fspath) | ||||||
|             self._tw.line() |             self._tw.line() | ||||||
|             relpath = self.curdir.bestrelpath(fspath) |             #relpath = self.curdir.bestrelpath(fspath) | ||||||
|             self._tw.write(relpath + " ") |             self._tw.write(fspath + " ") | ||||||
|         self._tw.write(res) |         self._tw.write(res) | ||||||
| 
 | 
 | ||||||
|     def write_ensure_prefix(self, prefix, extra="", **kwargs): |     def write_ensure_prefix(self, prefix, extra="", **kwargs): | ||||||
|  | @ -163,14 +163,15 @@ class TerminalReporter: | ||||||
|     def pytest__teardown_final_logerror(self, report): |     def pytest__teardown_final_logerror(self, report): | ||||||
|         self.stats.setdefault("error", []).append(report) |         self.stats.setdefault("error", []).append(report) | ||||||
| 
 | 
 | ||||||
|     def pytest_runtest_logstart(self, nodeid, location, fspath): |     def pytest_runtest_logstart(self, nodeid, location): | ||||||
|         # ensure that the path is printed before the |         # ensure that the path is printed before the | ||||||
|         # 1st test of a module starts running |         # 1st test of a module starts running | ||||||
|  |         fspath = nodeid.split("::")[0] | ||||||
|         if self.showlongtestinfo: |         if self.showlongtestinfo: | ||||||
|             line = self._locationline(fspath, *location) |             line = self._locationline(fspath, *location) | ||||||
|             self.write_ensure_prefix(line, "") |             self.write_ensure_prefix(line, "") | ||||||
|         elif self.showfspath: |         elif self.showfspath: | ||||||
|             self.write_fspath_result(py.path.local(fspath), "") |             self.write_fspath_result(fspath, "") | ||||||
| 
 | 
 | ||||||
|     def pytest_runtest_logreport(self, report): |     def pytest_runtest_logreport(self, report): | ||||||
|         rep = report |         rep = report | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								setup.py
								
								
								
								
							
							
						
						
									
										2
									
								
								setup.py
								
								
								
								
							|  | @ -22,7 +22,7 @@ def main(): | ||||||
|         name='pytest', |         name='pytest', | ||||||
|         description='py.test: simple powerful testing with Python', |         description='py.test: simple powerful testing with Python', | ||||||
|         long_description = long_description, |         long_description = long_description, | ||||||
|         version='2.0.0.dev18', |         version='2.0.0.dev19', | ||||||
|         url='http://pytest.org', |         url='http://pytest.org', | ||||||
|         license='MIT license', |         license='MIT license', | ||||||
|         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], |         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], | ||||||
|  |  | ||||||
|  | @ -76,14 +76,13 @@ class TestGeneralUsage: | ||||||
|         p1 = testdir.makepyfile("") |         p1 = testdir.makepyfile("") | ||||||
|         p2 = testdir.makefile(".pyc", "123") |         p2 = testdir.makefile(".pyc", "123") | ||||||
|         result = testdir.runpytest(p1, p2) |         result = testdir.runpytest(p1, p2) | ||||||
|         assert result.ret != 0 |         assert result.ret | ||||||
|         result.stderr.fnmatch_lines([ |         result.stderr.fnmatch_lines([ | ||||||
|             "*ERROR: can't collect:*%s" %(p2.basename,) |             "*ERROR: not found:*%s" %(p2.basename,) | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     @py.test.mark.xfail |  | ||||||
|     def test_early_skip(self, testdir): |     def test_early_skip(self, testdir): | ||||||
|         testdir.mkdir("xyz") |         testdir.mkdir("xyz") | ||||||
|         testdir.makeconftest(""" |         testdir.makeconftest(""" | ||||||
|  | @ -97,7 +96,6 @@ class TestGeneralUsage: | ||||||
|             "*1 skip*" |             "*1 skip*" | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def test_issue88_initial_file_multinodes(self, testdir): |     def test_issue88_initial_file_multinodes(self, testdir): | ||||||
|         testdir.makeconftest(""" |         testdir.makeconftest(""" | ||||||
|             import py |             import py | ||||||
|  | @ -145,7 +143,7 @@ class TestGeneralUsage: | ||||||
|             print (py.__file__) |             print (py.__file__) | ||||||
|             print (py.__path__) |             print (py.__path__) | ||||||
|             os.chdir(os.path.dirname(os.getcwd())) |             os.chdir(os.path.dirname(os.getcwd())) | ||||||
|             print (py.log.Producer) |             print (py.log) | ||||||
|         """)) |         """)) | ||||||
|         result = testdir.runpython(p, prepend=False) |         result = testdir.runpython(p, prepend=False) | ||||||
|         assert not result.ret |         assert not result.ret | ||||||
|  | @ -211,6 +209,27 @@ class TestGeneralUsage: | ||||||
|         assert res.ret == 0 |         assert res.ret == 0 | ||||||
|         res.stdout.fnmatch_lines(["*1 skipped*"]) |         res.stdout.fnmatch_lines(["*1 skipped*"]) | ||||||
|          |          | ||||||
|  |     def test_direct_addressing_selects(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             def pytest_generate_tests(metafunc): | ||||||
|  |                 metafunc.addcall({'i': 1}, id="1") | ||||||
|  |                 metafunc.addcall({'i': 2}, id="2") | ||||||
|  |             def test_func(i): | ||||||
|  |                 pass | ||||||
|  |         """) | ||||||
|  |         res = testdir.runpytest(p.basename + "::" + "test_func[1]") | ||||||
|  |         assert res.ret == 0 | ||||||
|  |         res.stdout.fnmatch_lines(["*1 passed*"]) | ||||||
|  | 
 | ||||||
|  |     def test_direct_addressing_notfound(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             def test_func(): | ||||||
|  |                 pass | ||||||
|  |         """) | ||||||
|  |         res = testdir.runpytest(p.basename + "::" + "test_notfound") | ||||||
|  |         assert res.ret | ||||||
|  |         res.stderr.fnmatch_lines(["*ERROR*not found*"]) | ||||||
|  | 
 | ||||||
| class TestInvocationVariants: | class TestInvocationVariants: | ||||||
|     def test_earlyinit(self, testdir): |     def test_earlyinit(self, testdir): | ||||||
|         p = testdir.makepyfile(""" |         p = testdir.makepyfile(""" | ||||||
|  |  | ||||||
|  | @ -9,11 +9,13 @@ def runandparse(testdir, *args): | ||||||
|     return result, xmldoc |     return result, xmldoc | ||||||
| 
 | 
 | ||||||
| def assert_attr(node, **kwargs): | def assert_attr(node, **kwargs): | ||||||
|  |     __tracebackhide__ = True | ||||||
|     for name, expected in kwargs.items(): |     for name, expected in kwargs.items(): | ||||||
|         anode = node.getAttributeNode(name) |         anode = node.getAttributeNode(name) | ||||||
|         assert anode, "node %r has no attribute %r" %(node, name) |         assert anode, "node %r has no attribute %r" %(node, name) | ||||||
|         val = anode.value |         val = anode.value | ||||||
|         assert val == str(expected) |         if val != str(expected): | ||||||
|  |             py.test.fail("%r != %r" %(str(val), str(expected))) | ||||||
| 
 | 
 | ||||||
| class TestPython: | class TestPython: | ||||||
|     def test_summing_simple(self, testdir): |     def test_summing_simple(self, testdir): | ||||||
|  | @ -50,7 +52,7 @@ class TestPython: | ||||||
|         assert_attr(node, errors=1, tests=0) |         assert_attr(node, errors=1, tests=0) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_setup_error.test_setup_error", |             classname="test_setup_error", | ||||||
|             name="test_function") |             name="test_function") | ||||||
|         fnode = tnode.getElementsByTagName("error")[0] |         fnode = tnode.getElementsByTagName("error")[0] | ||||||
|         assert_attr(fnode, message="test setup failure") |         assert_attr(fnode, message="test setup failure") | ||||||
|  | @ -68,9 +70,21 @@ class TestPython: | ||||||
|         assert_attr(node, failures=1) |         assert_attr(node, failures=1) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_classname_instance.test_classname_instance.TestClass", |             classname="test_classname_instance.TestClass", | ||||||
|             name="test_method") |             name="test_method") | ||||||
| 
 | 
 | ||||||
|  |     def test_classname_nested_dir(self, testdir): | ||||||
|  |         p = testdir.tmpdir.ensure("sub", "test_hello.py") | ||||||
|  |         p.write("def test_func(): 0/0") | ||||||
|  |         result, dom = runandparse(testdir) | ||||||
|  |         assert result.ret | ||||||
|  |         node = dom.getElementsByTagName("testsuite")[0] | ||||||
|  |         assert_attr(node, failures=1) | ||||||
|  |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|  |         assert_attr(tnode, | ||||||
|  |             classname="sub.test_hello", | ||||||
|  |             name="test_func") | ||||||
|  | 
 | ||||||
|     def test_internal_error(self, testdir): |     def test_internal_error(self, testdir): | ||||||
|         testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0") |         testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0") | ||||||
|         testdir.makepyfile("def test_function(): pass") |         testdir.makepyfile("def test_function(): pass") | ||||||
|  | @ -92,7 +106,7 @@ class TestPython: | ||||||
|         assert_attr(node, failures=1, tests=1) |         assert_attr(node, failures=1, tests=1) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_failure_function.test_failure_function", |             classname="test_failure_function", | ||||||
|             name="test_fail") |             name="test_fail") | ||||||
|         fnode = tnode.getElementsByTagName("failure")[0] |         fnode = tnode.getElementsByTagName("failure")[0] | ||||||
|         assert_attr(fnode, message="test failure") |         assert_attr(fnode, message="test failure") | ||||||
|  | @ -112,11 +126,11 @@ class TestPython: | ||||||
|         assert_attr(node, failures=2, tests=2) |         assert_attr(node, failures=2, tests=2) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_failure_escape.test_failure_escape", |             classname="test_failure_escape", | ||||||
|             name="test_func[<]") |             name="test_func[<]") | ||||||
|         tnode = node.getElementsByTagName("testcase")[1] |         tnode = node.getElementsByTagName("testcase")[1] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_failure_escape.test_failure_escape", |             classname="test_failure_escape", | ||||||
|             name="test_func[&]") |             name="test_func[&]") | ||||||
| 
 | 
 | ||||||
|     def test_junit_prefixing(self, testdir): |     def test_junit_prefixing(self, testdir): | ||||||
|  | @ -133,11 +147,11 @@ class TestPython: | ||||||
|         assert_attr(node, failures=1, tests=2) |         assert_attr(node, failures=1, tests=2) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="xyz.test_junit_prefixing.test_junit_prefixing", |             classname="xyz.test_junit_prefixing", | ||||||
|             name="test_func") |             name="test_func") | ||||||
|         tnode = node.getElementsByTagName("testcase")[1] |         tnode = node.getElementsByTagName("testcase")[1] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="xyz.test_junit_prefixing.test_junit_prefixing." |             classname="xyz.test_junit_prefixing." | ||||||
|                       "TestHello", |                       "TestHello", | ||||||
|             name="test_hello") |             name="test_hello") | ||||||
| 
 | 
 | ||||||
|  | @ -153,7 +167,7 @@ class TestPython: | ||||||
|         assert_attr(node, skips=1, tests=0) |         assert_attr(node, skips=1, tests=0) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_xfailure_function.test_xfailure_function", |             classname="test_xfailure_function", | ||||||
|             name="test_xfail") |             name="test_xfail") | ||||||
|         fnode = tnode.getElementsByTagName("skipped")[0] |         fnode = tnode.getElementsByTagName("skipped")[0] | ||||||
|         assert_attr(fnode, message="expected test failure") |         assert_attr(fnode, message="expected test failure") | ||||||
|  | @ -172,7 +186,7 @@ class TestPython: | ||||||
|         assert_attr(node, skips=1, tests=0) |         assert_attr(node, skips=1, tests=0) | ||||||
|         tnode = node.getElementsByTagName("testcase")[0] |         tnode = node.getElementsByTagName("testcase")[0] | ||||||
|         assert_attr(tnode, |         assert_attr(tnode, | ||||||
|             classname="test_xfailure_xpass.test_xfailure_xpass", |             classname="test_xfailure_xpass", | ||||||
|             name="test_xpass") |             name="test_xpass") | ||||||
|         fnode = tnode.getElementsByTagName("skipped")[0] |         fnode = tnode.getElementsByTagName("skipped")[0] | ||||||
|         assert_attr(fnode, message="xfail-marked test passes unexpectedly") |         assert_attr(fnode, message="xfail-marked test passes unexpectedly") | ||||||
|  |  | ||||||
|  | @ -235,7 +235,7 @@ class TestFunction: | ||||||
|             param = 1 |             param = 1 | ||||||
|             funcargs = {} |             funcargs = {} | ||||||
|             id = "world" |             id = "world" | ||||||
|         collection = object() |         collection = testdir.Collection(config) | ||||||
|         f5 = py.test.collect.Function(name="name", config=config, |         f5 = py.test.collect.Function(name="name", config=config, | ||||||
|             callspec=callspec1, callobj=isinstance, collection=collection) |             callspec=callspec1, callobj=isinstance, collection=collection) | ||||||
|         f5b = py.test.collect.Function(name="name", config=config, |         f5b = py.test.collect.Function(name="name", config=config, | ||||||
|  | @ -395,8 +395,8 @@ def test_generate_tests_only_done_in_subdir(testdir): | ||||||
| 
 | 
 | ||||||
| def test_modulecol_roundtrip(testdir): | def test_modulecol_roundtrip(testdir): | ||||||
|     modcol = testdir.getmodulecol("pass", withinit=True) |     modcol = testdir.getmodulecol("pass", withinit=True) | ||||||
|     trail = modcol.collection.getid(modcol) |     trail = modcol.nodeid | ||||||
|     newcol = modcol.collection.getbyid(trail)[0] |     newcol = modcol.collection.perform_collect([trail], genitems=0)[0] | ||||||
|     assert modcol.name == newcol.name |     assert modcol.name == newcol.name | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1058,8 +1058,7 @@ class TestReportInfo: | ||||||
|         """) |         """) | ||||||
|         item = testdir.getitem("def test_func(): pass") |         item = testdir.getitem("def test_func(): pass") | ||||||
|         runner = item.config.pluginmanager.getplugin("runner") |         runner = item.config.pluginmanager.getplugin("runner") | ||||||
|         nodeinfo = runner.getitemnodeinfo(item) |         assert item.location == ("ABCDE", 42, "custom") | ||||||
|         assert nodeinfo.location == ("ABCDE", 42, "custom") |  | ||||||
| 
 | 
 | ||||||
|     def test_func_reportinfo(self, testdir): |     def test_func_reportinfo(self, testdir): | ||||||
|         item = testdir.getitem("def test_func(): pass") |         item = testdir.getitem("def test_func(): pass") | ||||||
|  |  | ||||||
|  | @ -257,9 +257,9 @@ class TestCollectionReports: | ||||||
|         assert not rep.skipped |         assert not rep.skipped | ||||||
|         assert rep.passed |         assert rep.passed | ||||||
|         locinfo = rep.location |         locinfo = rep.location | ||||||
|         assert locinfo[0] == col.fspath |         assert locinfo[0] == col.fspath.basename | ||||||
|         assert not locinfo[1] |         assert not locinfo[1] | ||||||
|         assert locinfo[2] == col.fspath |         assert locinfo[2] == col.fspath.basename | ||||||
|         res = rep.result |         res = rep.result | ||||||
|         assert len(res) == 2 |         assert len(res) == 2 | ||||||
|         assert res[0].name == "test_func1" |         assert res[0].name == "test_func1" | ||||||
|  |  | ||||||
|  | @ -17,13 +17,14 @@ class SessionTests: | ||||||
|         assert len(skipped) == 0 |         assert len(skipped) == 0 | ||||||
|         assert len(passed) == 1 |         assert len(passed) == 1 | ||||||
|         assert len(failed) == 3 |         assert len(failed) == 3 | ||||||
|         assert failed[0].nodenames[-1] == "test_one_one" |         end = lambda x: x.nodeid.split("::")[-1] | ||||||
|         assert failed[1].nodenames[-1] == "test_other" |         assert end(failed[0]) == "test_one_one" | ||||||
|         assert failed[2].nodenames[-1] == "test_two" |         assert end(failed[1]) == "test_other" | ||||||
|  |         assert end(failed[2]) == "test_two" | ||||||
|         itemstarted = reprec.getcalls("pytest_itemcollected") |         itemstarted = reprec.getcalls("pytest_itemcollected") | ||||||
|         assert len(itemstarted) == 4 |         assert len(itemstarted) == 4 | ||||||
|         colstarted = reprec.getcalls("pytest_collectstart") |         colstarted = reprec.getcalls("pytest_collectstart") | ||||||
|         assert len(colstarted) == 1 + 1 # XXX ExtraTopCollector |         assert len(colstarted) == 1 + 1 | ||||||
|         col = colstarted[1].collector |         col = colstarted[1].collector | ||||||
|         assert isinstance(col, py.test.collect.Module) |         assert isinstance(col, py.test.collect.Module) | ||||||
| 
 | 
 | ||||||
|  | @ -186,7 +187,7 @@ class TestNewSession(SessionTests): | ||||||
|         started = reprec.getcalls("pytest_collectstart") |         started = reprec.getcalls("pytest_collectstart") | ||||||
|         finished = reprec.getreports("pytest_collectreport") |         finished = reprec.getreports("pytest_collectreport") | ||||||
|         assert len(started) == len(finished) |         assert len(started) == len(finished) | ||||||
|         assert len(started) == 8 + 1 # XXX extra TopCollector |         assert len(started) == 8 # XXX extra TopCollector | ||||||
|         colfail = [x for x in finished if x.failed] |         colfail = [x for x in finished if x.failed] | ||||||
|         colskipped = [x for x in finished if x.skipped] |         colskipped = [x for x in finished if x.skipped] | ||||||
|         assert len(colfail) == 1 |         assert len(colfail) == 1 | ||||||
|  |  | ||||||
|  | @ -1,14 +1,9 @@ | ||||||
| """ | """ | ||||||
| terminal reporting of the full testing process. | terminal reporting of the full testing process. | ||||||
| """ | """ | ||||||
| import py | import pytest,py | ||||||
| import sys | import sys | ||||||
| 
 | 
 | ||||||
| # =============================================================================== |  | ||||||
| # plugin tests |  | ||||||
| # |  | ||||||
| # =============================================================================== |  | ||||||
| 
 |  | ||||||
| from pytest.plugin.terminal import TerminalReporter, \ | from pytest.plugin.terminal import TerminalReporter, \ | ||||||
|     CollectonlyReporter,  repr_pythonversion, getreportopt |     CollectonlyReporter,  repr_pythonversion, getreportopt | ||||||
| from pytest.plugin import runner | from pytest.plugin import runner | ||||||
|  | @ -95,9 +90,8 @@ class TestTerminal: | ||||||
|         item = testdir.getitem("def test_func(): pass") |         item = testdir.getitem("def test_func(): pass") | ||||||
|         tr = TerminalReporter(item.config, file=linecomp.stringio) |         tr = TerminalReporter(item.config, file=linecomp.stringio) | ||||||
|         item.config.pluginmanager.register(tr) |         item.config.pluginmanager.register(tr) | ||||||
|         nodeid = item.collection.getid(item) |  | ||||||
|         location = item.reportinfo() |         location = item.reportinfo() | ||||||
|         tr.config.hook.pytest_runtest_logstart(nodeid=nodeid, |         tr.config.hook.pytest_runtest_logstart(nodeid=item.nodeid, | ||||||
|             location=location, fspath=str(item.fspath)) |             location=location, fspath=str(item.fspath)) | ||||||
|         linecomp.assert_contains_lines([ |         linecomp.assert_contains_lines([ | ||||||
|             "*test_show_runtest_logstart.py*" |             "*test_show_runtest_logstart.py*" | ||||||
|  | @ -424,6 +418,7 @@ class TestTerminalFunctional: | ||||||
|             "*test_verbose_reporting.py:10: test_gen*FAIL*", |             "*test_verbose_reporting.py:10: test_gen*FAIL*", | ||||||
|         ]) |         ]) | ||||||
|         assert result.ret == 1 |         assert result.ret == 1 | ||||||
|  |         pytest.xfail("repair xdist") | ||||||
|         pytestconfig.pluginmanager.skipifmissing("xdist") |         pytestconfig.pluginmanager.skipifmissing("xdist") | ||||||
|         result = testdir.runpytest(p1, '-v', '-n 1') |         result = testdir.runpytest(p1, '-v', '-n 1') | ||||||
|         result.stdout.fnmatch_lines([ |         result.stdout.fnmatch_lines([ | ||||||
|  |  | ||||||
|  | @ -1,288 +0,0 @@ | ||||||
| import py |  | ||||||
| 
 |  | ||||||
| class TestCollector: |  | ||||||
|     def test_collect_versus_item(self): |  | ||||||
|         from pytest.collect import Collector, Item |  | ||||||
|         assert not issubclass(Collector, Item) |  | ||||||
|         assert not issubclass(Item, Collector) |  | ||||||
| 
 |  | ||||||
|     def test_compat_attributes(self, testdir, recwarn): |  | ||||||
|         modcol = testdir.getmodulecol(""" |  | ||||||
|             def test_pass(): pass |  | ||||||
|             def test_fail(): assert 0 |  | ||||||
|         """) |  | ||||||
|         recwarn.clear() |  | ||||||
|         assert modcol.Module == py.test.collect.Module |  | ||||||
|         recwarn.pop(DeprecationWarning) |  | ||||||
|         assert modcol.Class == py.test.collect.Class |  | ||||||
|         recwarn.pop(DeprecationWarning) |  | ||||||
|         assert modcol.Item == py.test.collect.Item |  | ||||||
|         recwarn.pop(DeprecationWarning) |  | ||||||
|         assert modcol.File == py.test.collect.File |  | ||||||
|         recwarn.pop(DeprecationWarning) |  | ||||||
|         assert modcol.Function == py.test.collect.Function |  | ||||||
|         recwarn.pop(DeprecationWarning) |  | ||||||
| 
 |  | ||||||
|     def test_check_equality(self, testdir): |  | ||||||
|         modcol = testdir.getmodulecol(""" |  | ||||||
|             def test_pass(): pass |  | ||||||
|             def test_fail(): assert 0 |  | ||||||
|         """) |  | ||||||
|         fn1 = testdir.collect_by_name(modcol, "test_pass") |  | ||||||
|         assert isinstance(fn1, py.test.collect.Function) |  | ||||||
|         fn2 = testdir.collect_by_name(modcol, "test_pass") |  | ||||||
|         assert isinstance(fn2, py.test.collect.Function) |  | ||||||
| 
 |  | ||||||
|         assert fn1 == fn2 |  | ||||||
|         assert fn1 != modcol |  | ||||||
|         if py.std.sys.version_info < (3, 0): |  | ||||||
|             assert cmp(fn1, fn2) == 0 |  | ||||||
|         assert hash(fn1) == hash(fn2) |  | ||||||
| 
 |  | ||||||
|         fn3 = testdir.collect_by_name(modcol, "test_fail") |  | ||||||
|         assert isinstance(fn3, py.test.collect.Function) |  | ||||||
|         assert not (fn1 == fn3) |  | ||||||
|         assert fn1 != fn3 |  | ||||||
| 
 |  | ||||||
|         for fn in fn1,fn2,fn3: |  | ||||||
|             assert fn != 3 |  | ||||||
|             assert fn != modcol |  | ||||||
|             assert fn != [1,2,3] |  | ||||||
|             assert [1,2,3] != fn |  | ||||||
|             assert modcol != fn |  | ||||||
| 
 |  | ||||||
|     def test_getparent(self, testdir): |  | ||||||
|         modcol = testdir.getmodulecol(""" |  | ||||||
|             class TestClass: |  | ||||||
|                  def test_foo(): |  | ||||||
|                      pass |  | ||||||
|         """) |  | ||||||
|         cls = testdir.collect_by_name(modcol, "TestClass") |  | ||||||
|         fn = testdir.collect_by_name( |  | ||||||
|             testdir.collect_by_name(cls, "()"), "test_foo") |  | ||||||
| 
 |  | ||||||
|         parent = fn.getparent(py.test.collect.Module) |  | ||||||
|         assert parent is modcol |  | ||||||
| 
 |  | ||||||
|         parent = fn.getparent(py.test.collect.Function) |  | ||||||
|         assert parent is fn |  | ||||||
| 
 |  | ||||||
|         parent = fn.getparent(py.test.collect.Class) |  | ||||||
|         assert parent is cls |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def test_getcustomfile_roundtrip(self, testdir): |  | ||||||
|         hello = testdir.makefile(".xxx", hello="world") |  | ||||||
|         testdir.makepyfile(conftest=""" |  | ||||||
|             import py |  | ||||||
|             class CustomFile(py.test.collect.File): |  | ||||||
|                 pass |  | ||||||
|             def pytest_collect_file(path, parent): |  | ||||||
|                 if path.ext == ".xxx": |  | ||||||
|                     return CustomFile(path, parent=parent) |  | ||||||
|         """) |  | ||||||
|         config = testdir.parseconfig(hello) |  | ||||||
|         node = testdir.getnode(config, hello) |  | ||||||
|         assert isinstance(node, py.test.collect.File) |  | ||||||
|         assert node.name == "hello.xxx" |  | ||||||
|         id = node.collection.getid(node) |  | ||||||
|         nodes = node.collection.getbyid(id) |  | ||||||
|         assert len(nodes) == 1 |  | ||||||
|         assert isinstance(nodes[0], py.test.collect.File) |  | ||||||
| 
 |  | ||||||
| class TestCollectFS: |  | ||||||
|     def test_ignored_certain_directories(self, testdir): |  | ||||||
|         tmpdir = testdir.tmpdir |  | ||||||
|         tmpdir.ensure("_darcs", 'test_notfound.py') |  | ||||||
|         tmpdir.ensure("CVS", 'test_notfound.py') |  | ||||||
|         tmpdir.ensure("{arch}", 'test_notfound.py') |  | ||||||
|         tmpdir.ensure(".whatever", 'test_notfound.py') |  | ||||||
|         tmpdir.ensure(".bzr", 'test_notfound.py') |  | ||||||
|         tmpdir.ensure("normal", 'test_found.py') |  | ||||||
| 
 |  | ||||||
|         result = testdir.runpytest("--collectonly") |  | ||||||
|         s = result.stdout.str() |  | ||||||
|         assert "test_notfound" not in s |  | ||||||
|         assert "test_found" in s |  | ||||||
| 
 |  | ||||||
|     def test_custom_norecursedirs(self, testdir): |  | ||||||
|         testdir.makeini(""" |  | ||||||
|             [pytest] |  | ||||||
|             norecursedirs = mydir xyz* |  | ||||||
|         """) |  | ||||||
|         tmpdir = testdir.tmpdir |  | ||||||
|         tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass") |  | ||||||
|         tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0") |  | ||||||
|         tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass") |  | ||||||
|         rec = testdir.inline_run() |  | ||||||
|         rec.assertoutcome(passed=1) |  | ||||||
|         rec = testdir.inline_run("xyz123/test_2.py") |  | ||||||
|         rec.assertoutcome(failed=1) |  | ||||||
| 
 |  | ||||||
|     def test_found_certain_testfiles(self, testdir): |  | ||||||
|         p1 = testdir.makepyfile(test_found = "pass", found_test="pass") |  | ||||||
|         col = testdir.getnode(testdir.parseconfig(p1), p1.dirpath()) |  | ||||||
|         items = col.collect() # Directory collect returns files sorted by name |  | ||||||
|         assert len(items) == 2 |  | ||||||
|         assert items[1].name == 'test_found.py' |  | ||||||
|         assert items[0].name == 'found_test.py' |  | ||||||
| 
 |  | ||||||
|     def test_directory_file_sorting(self, testdir): |  | ||||||
|         p1 = testdir.makepyfile(test_one="hello") |  | ||||||
|         p1.dirpath().mkdir("x") |  | ||||||
|         p1.dirpath().mkdir("dir1") |  | ||||||
|         testdir.makepyfile(test_two="hello") |  | ||||||
|         p1.dirpath().mkdir("dir2") |  | ||||||
|         config = testdir.parseconfig() |  | ||||||
|         col = testdir.getnode(config, p1.dirpath()) |  | ||||||
|         names = [x.name for x in col.collect()] |  | ||||||
|         assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] |  | ||||||
| 
 |  | ||||||
| class TestCollectPluginHookRelay: |  | ||||||
|     def test_pytest_collect_file(self, testdir): |  | ||||||
|         tmpdir = testdir.tmpdir |  | ||||||
|         wascalled = [] |  | ||||||
|         class Plugin: |  | ||||||
|             def pytest_collect_file(self, path, parent): |  | ||||||
|                 wascalled.append(path) |  | ||||||
|         config = testdir.Config() |  | ||||||
|         config.pluginmanager.register(Plugin()) |  | ||||||
|         config.parse([tmpdir]) |  | ||||||
|         col = testdir.getnode(config, tmpdir) |  | ||||||
|         testdir.makefile(".abc", "xyz") |  | ||||||
|         res = col.collect() |  | ||||||
|         assert len(wascalled) == 1 |  | ||||||
|         assert wascalled[0].ext == '.abc' |  | ||||||
| 
 |  | ||||||
|     def test_pytest_collect_directory(self, testdir): |  | ||||||
|         tmpdir = testdir.tmpdir |  | ||||||
|         wascalled = [] |  | ||||||
|         class Plugin: |  | ||||||
|             def pytest_collect_directory(self, path, parent): |  | ||||||
|                 wascalled.append(path.basename) |  | ||||||
|                 return py.test.collect.Directory(path, parent) |  | ||||||
|         testdir.plugins.append(Plugin()) |  | ||||||
|         testdir.mkdir("hello") |  | ||||||
|         testdir.mkdir("world") |  | ||||||
|         reprec = testdir.inline_run() |  | ||||||
|         assert "hello" in wascalled |  | ||||||
|         assert "world" in wascalled |  | ||||||
|         # make sure the directories do not get double-appended |  | ||||||
|         colreports = reprec.getreports("pytest_collectreport") |  | ||||||
|         names = [rep.nodenames[-1] for rep in colreports] |  | ||||||
|         assert names.count("hello") == 1 |  | ||||||
| 
 |  | ||||||
| class TestPrunetraceback: |  | ||||||
|     def test_collection_error(self, testdir): |  | ||||||
|         p = testdir.makepyfile(""" |  | ||||||
|             import not_exists |  | ||||||
|         """) |  | ||||||
|         result = testdir.runpytest(p) |  | ||||||
|         assert "__import__" not in result.stdout.str(), "too long traceback" |  | ||||||
|         result.stdout.fnmatch_lines([ |  | ||||||
|             "*ERROR collecting*", |  | ||||||
|             "*mport*not_exists*" |  | ||||||
|         ]) |  | ||||||
| 
 |  | ||||||
|     def test_custom_repr_failure(self, testdir): |  | ||||||
|         p = testdir.makepyfile(""" |  | ||||||
|             import not_exists |  | ||||||
|         """) |  | ||||||
|         testdir.makeconftest(""" |  | ||||||
|             import py |  | ||||||
|             def pytest_collect_file(path, parent): |  | ||||||
|                 return MyFile(path, parent) |  | ||||||
|             class MyError(Exception): |  | ||||||
|                 pass |  | ||||||
|             class MyFile(py.test.collect.File): |  | ||||||
|                 def collect(self): |  | ||||||
|                     raise MyError() |  | ||||||
|                 def repr_failure(self, excinfo): |  | ||||||
|                     if excinfo.errisinstance(MyError): |  | ||||||
|                         return "hello world" |  | ||||||
|                     return py.test.collect.File.repr_failure(self, excinfo) |  | ||||||
|         """) |  | ||||||
| 
 |  | ||||||
|         result = testdir.runpytest(p) |  | ||||||
|         result.stdout.fnmatch_lines([ |  | ||||||
|             "*ERROR collecting*", |  | ||||||
|             "*hello world*", |  | ||||||
|         ]) |  | ||||||
| 
 |  | ||||||
|     @py.test.mark.xfail(reason="other mechanism for adding to reporting needed") |  | ||||||
|     def test_collect_report_postprocessing(self, testdir): |  | ||||||
|         p = testdir.makepyfile(""" |  | ||||||
|             import not_exists |  | ||||||
|         """) |  | ||||||
|         testdir.makeconftest(""" |  | ||||||
|             import py |  | ||||||
|             def pytest_make_collect_report(__multicall__): |  | ||||||
|                 rep = __multicall__.execute() |  | ||||||
|                 rep.headerlines += ["header1"] |  | ||||||
|                 return rep |  | ||||||
|         """) |  | ||||||
|         result = testdir.runpytest(p) |  | ||||||
|         result.stdout.fnmatch_lines([ |  | ||||||
|             "*ERROR collecting*", |  | ||||||
|             "*header1*", |  | ||||||
|         ]) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class TestCustomConftests: |  | ||||||
|     def test_ignore_collect_path(self, testdir): |  | ||||||
|         testdir.makeconftest(""" |  | ||||||
|             def pytest_ignore_collect(path, config): |  | ||||||
|                 return path.basename.startswith("x") or \ |  | ||||||
|                        path.basename == "test_one.py" |  | ||||||
|         """) |  | ||||||
|         testdir.mkdir("xy123").ensure("test_hello.py").write( |  | ||||||
|             "syntax error" |  | ||||||
|         ) |  | ||||||
|         testdir.makepyfile("def test_hello(): pass") |  | ||||||
|         testdir.makepyfile(test_one="syntax error") |  | ||||||
|         result = testdir.runpytest() |  | ||||||
|         assert result.ret == 0 |  | ||||||
|         result.stdout.fnmatch_lines(["*1 passed*"]) |  | ||||||
| 
 |  | ||||||
|     def test_collectignore_exclude_on_option(self, testdir): |  | ||||||
|         testdir.makeconftest(""" |  | ||||||
|             collect_ignore = ['hello', 'test_world.py'] |  | ||||||
|             def pytest_addoption(parser): |  | ||||||
|                 parser.addoption("--XX", action="store_true", default=False) |  | ||||||
|             def pytest_configure(config): |  | ||||||
|                 if config.getvalue("XX"): |  | ||||||
|                     collect_ignore[:] = [] |  | ||||||
|         """) |  | ||||||
|         testdir.mkdir("hello") |  | ||||||
|         testdir.makepyfile(test_world="#") |  | ||||||
|         reprec = testdir.inline_run(testdir.tmpdir) |  | ||||||
|         names = [rep.nodenames[-1] |  | ||||||
|                     for rep in reprec.getreports("pytest_collectreport")] |  | ||||||
|         assert 'hello' not in names |  | ||||||
|         assert 'test_world.py' not in names |  | ||||||
|         reprec = testdir.inline_run(testdir.tmpdir, "--XX") |  | ||||||
|         names = [rep.nodenames[-1] |  | ||||||
|                     for rep in reprec.getreports("pytest_collectreport")] |  | ||||||
|         assert 'hello' in names |  | ||||||
|         assert 'test_world.py' in names |  | ||||||
| 
 |  | ||||||
|     def test_pytest_fs_collect_hooks_are_seen(self, testdir): |  | ||||||
|         conf = testdir.makeconftest(""" |  | ||||||
|             import py |  | ||||||
|             class MyDirectory(py.test.collect.Directory): |  | ||||||
|                 pass |  | ||||||
|             class MyModule(py.test.collect.Module): |  | ||||||
|                 pass |  | ||||||
|             def pytest_collect_directory(path, parent): |  | ||||||
|                 return MyDirectory(path, parent) |  | ||||||
|             def pytest_collect_file(path, parent): |  | ||||||
|                 return MyModule(path, parent) |  | ||||||
|         """) |  | ||||||
|         sub = testdir.mkdir("sub") |  | ||||||
|         p = testdir.makepyfile("def test_x(): pass") |  | ||||||
|         result = testdir.runpytest("--collectonly") |  | ||||||
|         result.stdout.fnmatch_lines([ |  | ||||||
|             "*MyDirectory*", |  | ||||||
|             "*MyModule*", |  | ||||||
|             "*test_x*" |  | ||||||
|         ]) |  | ||||||
|  | @ -1,6 +1,283 @@ | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| from pytest.plugin.session import Collection, gettopdir | from pytest.plugin.session import Collection | ||||||
|  | 
 | ||||||
|  | class TestCollector: | ||||||
|  |     def test_collect_versus_item(self): | ||||||
|  |         from pytest.collect import Collector, Item | ||||||
|  |         assert not issubclass(Collector, Item) | ||||||
|  |         assert not issubclass(Item, Collector) | ||||||
|  | 
 | ||||||
|  |     def test_compat_attributes(self, testdir, recwarn): | ||||||
|  |         modcol = testdir.getmodulecol(""" | ||||||
|  |             def test_pass(): pass | ||||||
|  |             def test_fail(): assert 0 | ||||||
|  |         """) | ||||||
|  |         recwarn.clear() | ||||||
|  |         assert modcol.Module == py.test.collect.Module | ||||||
|  |         recwarn.pop(DeprecationWarning) | ||||||
|  |         assert modcol.Class == py.test.collect.Class | ||||||
|  |         recwarn.pop(DeprecationWarning) | ||||||
|  |         assert modcol.Item == py.test.collect.Item | ||||||
|  |         recwarn.pop(DeprecationWarning) | ||||||
|  |         assert modcol.File == py.test.collect.File | ||||||
|  |         recwarn.pop(DeprecationWarning) | ||||||
|  |         assert modcol.Function == py.test.collect.Function | ||||||
|  |         recwarn.pop(DeprecationWarning) | ||||||
|  | 
 | ||||||
|  |     def test_check_equality(self, testdir): | ||||||
|  |         modcol = testdir.getmodulecol(""" | ||||||
|  |             def test_pass(): pass | ||||||
|  |             def test_fail(): assert 0 | ||||||
|  |         """) | ||||||
|  |         fn1 = testdir.collect_by_name(modcol, "test_pass") | ||||||
|  |         assert isinstance(fn1, py.test.collect.Function) | ||||||
|  |         fn2 = testdir.collect_by_name(modcol, "test_pass") | ||||||
|  |         assert isinstance(fn2, py.test.collect.Function) | ||||||
|  | 
 | ||||||
|  |         assert fn1 == fn2 | ||||||
|  |         assert fn1 != modcol | ||||||
|  |         if py.std.sys.version_info < (3, 0): | ||||||
|  |             assert cmp(fn1, fn2) == 0 | ||||||
|  |         assert hash(fn1) == hash(fn2) | ||||||
|  | 
 | ||||||
|  |         fn3 = testdir.collect_by_name(modcol, "test_fail") | ||||||
|  |         assert isinstance(fn3, py.test.collect.Function) | ||||||
|  |         assert not (fn1 == fn3) | ||||||
|  |         assert fn1 != fn3 | ||||||
|  | 
 | ||||||
|  |         for fn in fn1,fn2,fn3: | ||||||
|  |             assert fn != 3 | ||||||
|  |             assert fn != modcol | ||||||
|  |             assert fn != [1,2,3] | ||||||
|  |             assert [1,2,3] != fn | ||||||
|  |             assert modcol != fn | ||||||
|  | 
 | ||||||
|  |     def test_getparent(self, testdir): | ||||||
|  |         modcol = testdir.getmodulecol(""" | ||||||
|  |             class TestClass: | ||||||
|  |                  def test_foo(): | ||||||
|  |                      pass | ||||||
|  |         """) | ||||||
|  |         cls = testdir.collect_by_name(modcol, "TestClass") | ||||||
|  |         fn = testdir.collect_by_name( | ||||||
|  |             testdir.collect_by_name(cls, "()"), "test_foo") | ||||||
|  | 
 | ||||||
|  |         parent = fn.getparent(py.test.collect.Module) | ||||||
|  |         assert parent is modcol | ||||||
|  | 
 | ||||||
|  |         parent = fn.getparent(py.test.collect.Function) | ||||||
|  |         assert parent is fn | ||||||
|  | 
 | ||||||
|  |         parent = fn.getparent(py.test.collect.Class) | ||||||
|  |         assert parent is cls | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def test_getcustomfile_roundtrip(self, testdir): | ||||||
|  |         hello = testdir.makefile(".xxx", hello="world") | ||||||
|  |         testdir.makepyfile(conftest=""" | ||||||
|  |             import py | ||||||
|  |             class CustomFile(py.test.collect.File): | ||||||
|  |                 pass | ||||||
|  |             def pytest_collect_file(path, parent): | ||||||
|  |                 if path.ext == ".xxx": | ||||||
|  |                     return CustomFile(path, parent=parent) | ||||||
|  |         """) | ||||||
|  |         node = testdir.getpathnode(hello) | ||||||
|  |         assert isinstance(node, py.test.collect.File) | ||||||
|  |         assert node.name == "hello.xxx" | ||||||
|  |         nodes = node.collection.perform_collect([node.nodeid], genitems=False) | ||||||
|  |         assert len(nodes) == 1 | ||||||
|  |         assert isinstance(nodes[0], py.test.collect.File) | ||||||
|  | 
 | ||||||
|  | class TestCollectFS: | ||||||
|  |     def test_ignored_certain_directories(self, testdir): | ||||||
|  |         tmpdir = testdir.tmpdir | ||||||
|  |         tmpdir.ensure("_darcs", 'test_notfound.py') | ||||||
|  |         tmpdir.ensure("CVS", 'test_notfound.py') | ||||||
|  |         tmpdir.ensure("{arch}", 'test_notfound.py') | ||||||
|  |         tmpdir.ensure(".whatever", 'test_notfound.py') | ||||||
|  |         tmpdir.ensure(".bzr", 'test_notfound.py') | ||||||
|  |         tmpdir.ensure("normal", 'test_found.py') | ||||||
|  | 
 | ||||||
|  |         result = testdir.runpytest("--collectonly") | ||||||
|  |         s = result.stdout.str() | ||||||
|  |         assert "test_notfound" not in s | ||||||
|  |         assert "test_found" in s | ||||||
|  | 
 | ||||||
|  |     def test_custom_norecursedirs(self, testdir): | ||||||
|  |         testdir.makeini(""" | ||||||
|  |             [pytest] | ||||||
|  |             norecursedirs = mydir xyz* | ||||||
|  |         """) | ||||||
|  |         tmpdir = testdir.tmpdir | ||||||
|  |         tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass") | ||||||
|  |         tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0") | ||||||
|  |         tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass") | ||||||
|  |         rec = testdir.inline_run() | ||||||
|  |         rec.assertoutcome(passed=1) | ||||||
|  |         rec = testdir.inline_run("xyz123/test_2.py") | ||||||
|  |         rec.assertoutcome(failed=1) | ||||||
|  | 
 | ||||||
|  | class TestCollectPluginHookRelay: | ||||||
|  |     def test_pytest_collect_file(self, testdir): | ||||||
|  |         wascalled = [] | ||||||
|  |         class Plugin: | ||||||
|  |             def pytest_collect_file(self, path, parent): | ||||||
|  |                 wascalled.append(path) | ||||||
|  |         testdir.makefile(".abc", "xyz") | ||||||
|  |         testdir.pytestmain([testdir.tmpdir], plugins=[Plugin()]) | ||||||
|  |         assert len(wascalled) == 1 | ||||||
|  |         assert wascalled[0].ext == '.abc' | ||||||
|  | 
 | ||||||
|  |     def test_pytest_collect_directory(self, testdir): | ||||||
|  |         wascalled = [] | ||||||
|  |         class Plugin: | ||||||
|  |             def pytest_collect_directory(self, path, parent): | ||||||
|  |                 wascalled.append(path.basename) | ||||||
|  |         testdir.mkdir("hello") | ||||||
|  |         testdir.mkdir("world") | ||||||
|  |         testdir.pytestmain(testdir.tmpdir, plugins=[Plugin()]) | ||||||
|  |         assert "hello" in wascalled | ||||||
|  |         assert "world" in wascalled | ||||||
|  | 
 | ||||||
|  | class TestPrunetraceback: | ||||||
|  |     def test_collection_error(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             import not_exists | ||||||
|  |         """) | ||||||
|  |         result = testdir.runpytest(p) | ||||||
|  |         assert "__import__" not in result.stdout.str(), "too long traceback" | ||||||
|  |         result.stdout.fnmatch_lines([ | ||||||
|  |             "*ERROR collecting*", | ||||||
|  |             "*mport*not_exists*" | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  |     def test_custom_repr_failure(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             import not_exists | ||||||
|  |         """) | ||||||
|  |         testdir.makeconftest(""" | ||||||
|  |             import py | ||||||
|  |             def pytest_collect_file(path, parent): | ||||||
|  |                 return MyFile(path, parent) | ||||||
|  |             class MyError(Exception): | ||||||
|  |                 pass | ||||||
|  |             class MyFile(py.test.collect.File): | ||||||
|  |                 def collect(self): | ||||||
|  |                     raise MyError() | ||||||
|  |                 def repr_failure(self, excinfo): | ||||||
|  |                     if excinfo.errisinstance(MyError): | ||||||
|  |                         return "hello world" | ||||||
|  |                     return py.test.collect.File.repr_failure(self, excinfo) | ||||||
|  |         """) | ||||||
|  | 
 | ||||||
|  |         result = testdir.runpytest(p) | ||||||
|  |         result.stdout.fnmatch_lines([ | ||||||
|  |             "*ERROR collecting*", | ||||||
|  |             "*hello world*", | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  |     @py.test.mark.xfail(reason="other mechanism for adding to reporting needed") | ||||||
|  |     def test_collect_report_postprocessing(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             import not_exists | ||||||
|  |         """) | ||||||
|  |         testdir.makeconftest(""" | ||||||
|  |             import py | ||||||
|  |             def pytest_make_collect_report(__multicall__): | ||||||
|  |                 rep = __multicall__.execute() | ||||||
|  |                 rep.headerlines += ["header1"] | ||||||
|  |                 return rep | ||||||
|  |         """) | ||||||
|  |         result = testdir.runpytest(p) | ||||||
|  |         result.stdout.fnmatch_lines([ | ||||||
|  |             "*ERROR collecting*", | ||||||
|  |             "*header1*", | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestCustomConftests: | ||||||
|  |     def test_ignore_collect_path(self, testdir): | ||||||
|  |         testdir.makeconftest(""" | ||||||
|  |             def pytest_ignore_collect(path, config): | ||||||
|  |                 return path.basename.startswith("x") or \ | ||||||
|  |                        path.basename == "test_one.py" | ||||||
|  |         """) | ||||||
|  |         testdir.mkdir("xy123").ensure("test_hello.py").write( | ||||||
|  |             "syntax error" | ||||||
|  |         ) | ||||||
|  |         testdir.makepyfile("def test_hello(): pass") | ||||||
|  |         testdir.makepyfile(test_one="syntax error") | ||||||
|  |         result = testdir.runpytest() | ||||||
|  |         assert result.ret == 0 | ||||||
|  |         result.stdout.fnmatch_lines(["*1 passed*"]) | ||||||
|  | 
 | ||||||
|  |     def test_collectignore_exclude_on_option(self, testdir): | ||||||
|  |         testdir.makeconftest(""" | ||||||
|  |             collect_ignore = ['hello', 'test_world.py'] | ||||||
|  |             def pytest_addoption(parser): | ||||||
|  |                 parser.addoption("--XX", action="store_true", default=False) | ||||||
|  |             def pytest_configure(config): | ||||||
|  |                 if config.getvalue("XX"): | ||||||
|  |                     collect_ignore[:] = [] | ||||||
|  |         """) | ||||||
|  |         testdir.mkdir("hello") | ||||||
|  |         testdir.makepyfile(test_world="def test_hello(): pass") | ||||||
|  |         result = testdir.runpytest() | ||||||
|  |         assert result.ret == 0 | ||||||
|  |         assert "passed" not in result.stdout.str() | ||||||
|  |         result = testdir.runpytest("--XX") | ||||||
|  |         assert result.ret == 0 | ||||||
|  |         assert "passed" in result.stdout.str() | ||||||
|  | 
 | ||||||
|  |     def test_pytest_fs_collect_hooks_are_seen(self, testdir): | ||||||
|  |         conf = testdir.makeconftest(""" | ||||||
|  |             import py | ||||||
|  |             class MyModule(py.test.collect.Module): | ||||||
|  |                 pass | ||||||
|  |             def pytest_collect_file(path, parent): | ||||||
|  |                 if path.ext == ".py": | ||||||
|  |                     return MyModule(path, parent) | ||||||
|  |         """) | ||||||
|  |         sub = testdir.mkdir("sub") | ||||||
|  |         p = testdir.makepyfile("def test_x(): pass") | ||||||
|  |         result = testdir.runpytest("--collectonly") | ||||||
|  |         result.stdout.fnmatch_lines([ | ||||||
|  |             "*MyModule*", | ||||||
|  |             "*test_x*" | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  |     def test_pytest_collect_file_from_sister_dir(self, testdir): | ||||||
|  |         sub1 = testdir.mkpydir("sub1") | ||||||
|  |         sub2 = testdir.mkpydir("sub2") | ||||||
|  |         conf1 = testdir.makeconftest(""" | ||||||
|  |             import py | ||||||
|  |             class MyModule1(py.test.collect.Module): | ||||||
|  |                 pass | ||||||
|  |             def pytest_collect_file(path, parent): | ||||||
|  |                 if path.ext == ".py": | ||||||
|  |                     return MyModule1(path, parent) | ||||||
|  |         """) | ||||||
|  |         conf1.move(sub1.join(conf1.basename)) | ||||||
|  |         conf2 = testdir.makeconftest(""" | ||||||
|  |             import py | ||||||
|  |             class MyModule2(py.test.collect.Module): | ||||||
|  |                 pass | ||||||
|  |             def pytest_collect_file(path, parent): | ||||||
|  |                 if path.ext == ".py": | ||||||
|  |                     return MyModule2(path, parent) | ||||||
|  |         """) | ||||||
|  |         conf2.move(sub2.join(conf2.basename)) | ||||||
|  |         p = testdir.makepyfile("def test_x(): pass") | ||||||
|  |         p.copy(sub1.join(p.basename)) | ||||||
|  |         p.copy(sub2.join(p.basename)) | ||||||
|  |         result = testdir.runpytest("--collectonly") | ||||||
|  |         result.stdout.fnmatch_lines([ | ||||||
|  |             "*MyModule1*", | ||||||
|  |             "*MyModule2*", | ||||||
|  |             "*test_x*" | ||||||
|  |         ]) | ||||||
| 
 | 
 | ||||||
| class TestCollection: | class TestCollection: | ||||||
|     def test_parsearg(self, testdir): |     def test_parsearg(self, testdir): | ||||||
|  | @ -13,16 +290,15 @@ class TestCollection: | ||||||
|         subdir.chdir() |         subdir.chdir() | ||||||
|         config = testdir.parseconfig(p.basename) |         config = testdir.parseconfig(p.basename) | ||||||
|         rcol = Collection(config=config) |         rcol = Collection(config=config) | ||||||
|         assert rcol.topdir == testdir.tmpdir |         assert rcol.fspath == subdir | ||||||
|         parts = rcol._parsearg(p.basename) |         parts = rcol._parsearg(p.basename) | ||||||
|         assert parts[0] ==  "sub" | 
 | ||||||
|         assert parts[1] ==  p.basename |         assert parts[0] ==  target | ||||||
|         assert len(parts) == 2 |         assert len(parts) == 1 | ||||||
|         parts = rcol._parsearg(p.basename + "::test_func") |         parts = rcol._parsearg(p.basename + "::test_func") | ||||||
|         assert parts[0] ==  "sub" |         assert parts[0] ==  target | ||||||
|         assert parts[1] ==  p.basename |         assert parts[1] ==  "test_func" | ||||||
|         assert parts[2] ==  "test_func" |         assert len(parts) == 2 | ||||||
|         assert len(parts) == 3 |  | ||||||
| 
 | 
 | ||||||
|     def test_collect_topdir(self, testdir): |     def test_collect_topdir(self, testdir): | ||||||
|         p = testdir.makepyfile("def test_func(): pass") |         p = testdir.makepyfile("def test_func(): pass") | ||||||
|  | @ -30,14 +306,14 @@ class TestCollection: | ||||||
|         config = testdir.parseconfig(id) |         config = testdir.parseconfig(id) | ||||||
|         topdir = testdir.tmpdir |         topdir = testdir.tmpdir | ||||||
|         rcol = Collection(config) |         rcol = Collection(config) | ||||||
|         assert topdir == rcol.topdir |         assert topdir == rcol.fspath | ||||||
|         hookrec = testdir.getreportrecorder(config) |         rootid = rcol.nodeid | ||||||
|         items = rcol.perform_collect() |         #root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0] | ||||||
|         assert len(items) == 1 |         #assert root2 == rcol, rootid | ||||||
|         root = items[0].listchain()[0] |         colitems = rcol.perform_collect([rcol.nodeid], genitems=False) | ||||||
|         root_id = rcol.getid(root) |         assert len(colitems) == 1 | ||||||
|         root2 = rcol.getbyid(root_id)[0] |         assert colitems[0].fspath == p | ||||||
|         assert root2.fspath == root.fspath | 
 | ||||||
| 
 | 
 | ||||||
|     def test_collect_protocol_single_function(self, testdir): |     def test_collect_protocol_single_function(self, testdir): | ||||||
|         p = testdir.makepyfile("def test_func(): pass") |         p = testdir.makepyfile("def test_func(): pass") | ||||||
|  | @ -45,13 +321,14 @@ class TestCollection: | ||||||
|         config = testdir.parseconfig(id) |         config = testdir.parseconfig(id) | ||||||
|         topdir = testdir.tmpdir |         topdir = testdir.tmpdir | ||||||
|         rcol = Collection(config) |         rcol = Collection(config) | ||||||
|         assert topdir == rcol.topdir |         assert topdir == rcol.fspath | ||||||
|         hookrec = testdir.getreportrecorder(config) |         hookrec = testdir.getreportrecorder(config) | ||||||
|         items = rcol.perform_collect() |         rcol.perform_collect() | ||||||
|  |         items = rcol.items | ||||||
|         assert len(items) == 1 |         assert len(items) == 1 | ||||||
|         item = items[0] |         item = items[0] | ||||||
|         assert item.name == "test_func" |         assert item.name == "test_func" | ||||||
|         newid = rcol.getid(item) |         newid = item.nodeid | ||||||
|         assert newid == id |         assert newid == id | ||||||
|         py.std.pprint.pprint(hookrec.hookrecorder.calls) |         py.std.pprint.pprint(hookrec.hookrecorder.calls) | ||||||
|         hookrec.hookrecorder.contains([ |         hookrec.hookrecorder.contains([ | ||||||
|  | @ -60,8 +337,8 @@ class TestCollection: | ||||||
|             ("pytest_collectstart", "collector.fspath == p"), |             ("pytest_collectstart", "collector.fspath == p"), | ||||||
|             ("pytest_make_collect_report", "collector.fspath == p"), |             ("pytest_make_collect_report", "collector.fspath == p"), | ||||||
|             ("pytest_pycollect_makeitem", "name == 'test_func'"), |             ("pytest_pycollect_makeitem", "name == 'test_func'"), | ||||||
|             ("pytest_collectreport", "report.fspath == p"), |             ("pytest_collectreport", "report.nodeid.startswith(p.basename)"), | ||||||
|             ("pytest_collectreport", "report.fspath == topdir") |             ("pytest_collectreport", "report.nodeid == '.'") | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|     def test_collect_protocol_method(self, testdir): |     def test_collect_protocol_method(self, testdir): | ||||||
|  | @ -70,19 +347,19 @@ class TestCollection: | ||||||
|                 def test_method(self): |                 def test_method(self): | ||||||
|                     pass |                     pass | ||||||
|         """) |         """) | ||||||
|         normid = p.basename + "::TestClass::test_method" |         normid = p.basename + "::TestClass::()::test_method" | ||||||
|         for id in [p.basename, |         for id in [p.basename, | ||||||
|                    p.basename + "::TestClass", |                    p.basename + "::TestClass", | ||||||
|                    p.basename + "::TestClass::()", |                    p.basename + "::TestClass::()", | ||||||
|                    p.basename + "::TestClass::()::test_method", |  | ||||||
|                    normid, |                    normid, | ||||||
|                    ]: |                    ]: | ||||||
|             config = testdir.parseconfig(id) |             config = testdir.parseconfig(id) | ||||||
|             rcol = Collection(config=config) |             rcol = Collection(config=config) | ||||||
|             nodes = rcol.perform_collect() |             rcol.perform_collect() | ||||||
|             assert len(nodes) == 1 |             items = rcol.items | ||||||
|             assert nodes[0].name == "test_method" |             assert len(items) == 1 | ||||||
|             newid = rcol.getid(nodes[0]) |             assert items[0].name == "test_method" | ||||||
|  |             newid = items[0].nodeid | ||||||
|             assert newid == normid |             assert newid == normid | ||||||
| 
 | 
 | ||||||
|     def test_collect_custom_nodes_multi_id(self, testdir): |     def test_collect_custom_nodes_multi_id(self, testdir): | ||||||
|  | @ -104,20 +381,21 @@ class TestCollection: | ||||||
|         config = testdir.parseconfig(id) |         config = testdir.parseconfig(id) | ||||||
|         rcol = Collection(config) |         rcol = Collection(config) | ||||||
|         hookrec = testdir.getreportrecorder(config) |         hookrec = testdir.getreportrecorder(config) | ||||||
|         items = rcol.perform_collect() |         rcol.perform_collect() | ||||||
|  |         items = rcol.items | ||||||
|         py.std.pprint.pprint(hookrec.hookrecorder.calls) |         py.std.pprint.pprint(hookrec.hookrecorder.calls) | ||||||
|         assert len(items) == 2 |         assert len(items) == 2 | ||||||
|         hookrec.hookrecorder.contains([ |         hookrec.hookrecorder.contains([ | ||||||
|             ("pytest_collectstart", |             ("pytest_collectstart", | ||||||
|                 "collector.fspath == collector.collection.topdir"), |                 "collector.fspath == collector.collection.fspath"), | ||||||
|             ("pytest_collectstart", |             ("pytest_collectstart", | ||||||
|                 "collector.__class__.__name__ == 'SpecialFile'"), |                 "collector.__class__.__name__ == 'SpecialFile'"), | ||||||
|             ("pytest_collectstart", |             ("pytest_collectstart", | ||||||
|                 "collector.__class__.__name__ == 'Module'"), |                 "collector.__class__.__name__ == 'Module'"), | ||||||
|             ("pytest_pycollect_makeitem", "name == 'test_func'"), |             ("pytest_pycollect_makeitem", "name == 'test_func'"), | ||||||
|             ("pytest_collectreport", "report.fspath == p"), |             ("pytest_collectreport", "report.nodeid.startswith(p.basename)"), | ||||||
|             ("pytest_collectreport", |             #("pytest_collectreport", | ||||||
|                 "report.fspath == %r" % str(rcol.topdir)), |             #    "report.fspath == %r" % str(rcol.fspath)), | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|     def test_collect_subdir_event_ordering(self, testdir): |     def test_collect_subdir_event_ordering(self, testdir): | ||||||
|  | @ -128,134 +406,87 @@ class TestCollection: | ||||||
|         config = testdir.parseconfig() |         config = testdir.parseconfig() | ||||||
|         rcol = Collection(config) |         rcol = Collection(config) | ||||||
|         hookrec = testdir.getreportrecorder(config) |         hookrec = testdir.getreportrecorder(config) | ||||||
|         items = rcol.perform_collect() |         rcol.perform_collect() | ||||||
|  |         items = rcol.items | ||||||
|         assert len(items) == 1 |         assert len(items) == 1 | ||||||
|         py.std.pprint.pprint(hookrec.hookrecorder.calls) |         py.std.pprint.pprint(hookrec.hookrecorder.calls) | ||||||
|         hookrec.hookrecorder.contains([ |         hookrec.hookrecorder.contains([ | ||||||
|             ("pytest_collectstart", "collector.fspath == aaa"), |  | ||||||
|             ("pytest_collectstart", "collector.fspath == test_aaa"), |             ("pytest_collectstart", "collector.fspath == test_aaa"), | ||||||
|             ("pytest_pycollect_makeitem", "name == 'test_func'"), |             ("pytest_pycollect_makeitem", "name == 'test_func'"), | ||||||
|             ("pytest_collectreport", "report.fspath == test_aaa"), |             ("pytest_collectreport", | ||||||
|             ("pytest_collectreport", "report.fspath == aaa"), |                     "report.nodeid.startswith('aaa/test_aaa.py')"), | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|     def test_collect_two_commandline_args(self, testdir): |     def test_collect_two_commandline_args(self, testdir): | ||||||
|         p = testdir.makepyfile("def test_func(): pass") |         p = testdir.makepyfile("def test_func(): pass") | ||||||
|         aaa = testdir.mkpydir("aaa") |         aaa = testdir.mkpydir("aaa") | ||||||
|         bbb = testdir.mkpydir("bbb") |         bbb = testdir.mkpydir("bbb") | ||||||
|         p.copy(aaa.join("test_aaa.py")) |         test_aaa = aaa.join("test_aaa.py") | ||||||
|         p.move(bbb.join("test_bbb.py")) |         p.copy(test_aaa) | ||||||
|  |         test_bbb = bbb.join("test_bbb.py") | ||||||
|  |         p.move(test_bbb) | ||||||
| 
 | 
 | ||||||
|         id = "." |         id = "." | ||||||
|         config = testdir.parseconfig(id) |         config = testdir.parseconfig(id) | ||||||
|         rcol = Collection(config) |         rcol = Collection(config) | ||||||
|         hookrec = testdir.getreportrecorder(config) |         hookrec = testdir.getreportrecorder(config) | ||||||
|         items = rcol.perform_collect() |         rcol.perform_collect() | ||||||
|  |         items = rcol.items | ||||||
|         assert len(items) == 2 |         assert len(items) == 2 | ||||||
|         py.std.pprint.pprint(hookrec.hookrecorder.calls) |         py.std.pprint.pprint(hookrec.hookrecorder.calls) | ||||||
|         hookrec.hookrecorder.contains([ |         hookrec.hookrecorder.contains([ | ||||||
|             ("pytest_collectstart", "collector.fspath == aaa"), |             ("pytest_collectstart", "collector.fspath == test_aaa"), | ||||||
|             ("pytest_pycollect_makeitem", "name == 'test_func'"), |             ("pytest_pycollect_makeitem", "name == 'test_func'"), | ||||||
|             ("pytest_collectreport", "report.fspath == aaa"), |             ("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"), | ||||||
|             ("pytest_collectstart", "collector.fspath == bbb"), |             ("pytest_collectstart", "collector.fspath == test_bbb"), | ||||||
|             ("pytest_pycollect_makeitem", "name == 'test_func'"), |             ("pytest_pycollect_makeitem", "name == 'test_func'"), | ||||||
|             ("pytest_collectreport", "report.fspath == bbb"), |             ("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"), | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|     def test_serialization_byid(self, testdir): |     def test_serialization_byid(self, testdir): | ||||||
|         p = testdir.makepyfile("def test_func(): pass") |         p = testdir.makepyfile("def test_func(): pass") | ||||||
|         config = testdir.parseconfig() |         config = testdir.parseconfig() | ||||||
|         rcol = Collection(config) |         rcol = Collection(config) | ||||||
|         items = rcol.perform_collect() |         rcol.perform_collect() | ||||||
|  |         items = rcol.items | ||||||
|         assert len(items) == 1 |         assert len(items) == 1 | ||||||
|         item, = items |         item, = items | ||||||
|         id = rcol.getid(item) |  | ||||||
|         newcol = Collection(config) |         newcol = Collection(config) | ||||||
|         item2, = newcol.getbyid(id) |         item2, = newcol.perform_collect([item.nodeid], genitems=False) | ||||||
|         assert item2.name == item.name |         assert item2.name == item.name | ||||||
|         assert item2.fspath == item.fspath |         assert item2.fspath == item.fspath | ||||||
|         item2b, = newcol.getbyid(id) |         item2b, = newcol.perform_collect([item.nodeid], genitems=False) | ||||||
|         assert item2b is item2 |         assert item2b == item2 | ||||||
| 
 |  | ||||||
| class Test_gettopdir: |  | ||||||
|     def test_gettopdir(self, testdir): |  | ||||||
|         tmp = testdir.tmpdir |  | ||||||
|         assert gettopdir([tmp]) == tmp |  | ||||||
|         topdir = gettopdir([tmp.join("hello"), tmp.join("world")]) |  | ||||||
|         assert topdir == tmp |  | ||||||
|         somefile = tmp.ensure("somefile.py") |  | ||||||
|         assert gettopdir([somefile]) == tmp |  | ||||||
| 
 |  | ||||||
|     def test_gettopdir_pypkg(self, testdir): |  | ||||||
|         tmp = testdir.tmpdir |  | ||||||
|         a = tmp.ensure('a', dir=1) |  | ||||||
|         b = tmp.ensure('a', 'b', '__init__.py') |  | ||||||
|         c = tmp.ensure('a', 'b', 'c.py') |  | ||||||
|         Z = tmp.ensure('Z', dir=1) |  | ||||||
|         assert gettopdir([c]) == a |  | ||||||
|         assert gettopdir([c, Z]) == tmp |  | ||||||
|         assert gettopdir(["%s::xyc" % c]) == a |  | ||||||
|         assert gettopdir(["%s::xyc::abc" % c]) == a |  | ||||||
|         assert gettopdir(["%s::xyc" % c, "%s::abc" % Z]) == tmp |  | ||||||
| 
 | 
 | ||||||
| def getargnode(collection, arg): | def getargnode(collection, arg): | ||||||
|     return collection.getbyid(collection._normalizearg(str(arg)))[0] |     argpath = arg.relto(collection.fspath) | ||||||
|  |     return collection.perform_collect([argpath], genitems=False)[0] | ||||||
| 
 | 
 | ||||||
| class Test_getinitialnodes: | class Test_getinitialnodes: | ||||||
|     def test_onedir(self, testdir): |  | ||||||
|         config = testdir.reparseconfig([testdir.tmpdir]) |  | ||||||
|         c = Collection(config) |  | ||||||
|         col = getargnode(c, testdir.tmpdir) |  | ||||||
|         assert isinstance(col, py.test.collect.Directory) |  | ||||||
|         for col in col.listchain(): |  | ||||||
|             assert col.config is config |  | ||||||
|         t2 = getargnode(c, testdir.tmpdir) |  | ||||||
|         assert col == t2 |  | ||||||
| 
 |  | ||||||
|     def test_curdir_and_subdir(self, testdir, tmpdir): |  | ||||||
|         a = tmpdir.ensure("a", dir=1) |  | ||||||
|         config = testdir.reparseconfig([tmpdir, a]) |  | ||||||
|         c = Collection(config) |  | ||||||
|          |  | ||||||
|         col1 = getargnode(c, tmpdir) |  | ||||||
|         col2 = getargnode(c, a) |  | ||||||
|         assert col1.name == tmpdir.basename |  | ||||||
|         assert col2.name == 'a' |  | ||||||
|         for col in (col1, col2): |  | ||||||
|             for subcol in col.listchain(): |  | ||||||
|                 assert col.config is config |  | ||||||
| 
 |  | ||||||
|     def test_global_file(self, testdir, tmpdir): |     def test_global_file(self, testdir, tmpdir): | ||||||
|         x = tmpdir.ensure("x.py") |         x = tmpdir.ensure("x.py") | ||||||
|         config = testdir.reparseconfig([x]) |         config = testdir.reparseconfig([x]) | ||||||
|         col = getargnode(Collection(config), x) |         col = testdir.getnode(config, x) | ||||||
|         assert isinstance(col, py.test.collect.Module) |         assert isinstance(col, py.test.collect.Module) | ||||||
|         assert col.name == 'x.py' |         assert col.name == 'x.py' | ||||||
|         assert col.parent.name == tmpdir.basename |         assert col.parent.name == testdir.tmpdir.basename | ||||||
|         assert col.parent.parent is None |         assert col.parent.parent is None | ||||||
|         for col in col.listchain(): |         for col in col.listchain(): | ||||||
|             assert col.config is config |             assert col.config is config | ||||||
| 
 | 
 | ||||||
|     def test_global_dir(self, testdir, tmpdir): |     def test_pkgfile(self, testdir): | ||||||
|         x = tmpdir.ensure("a", dir=1) |         testdir.chdir() | ||||||
|  |         tmpdir = testdir.tmpdir | ||||||
|  |         subdir = tmpdir.join("subdir") | ||||||
|  |         x = subdir.ensure("x.py") | ||||||
|  |         subdir.ensure("__init__.py") | ||||||
|         config = testdir.reparseconfig([x]) |         config = testdir.reparseconfig([x]) | ||||||
|         col = getargnode(Collection(config), x) |         col = testdir.getnode(config, x) | ||||||
|         assert isinstance(col, py.test.collect.Directory) |  | ||||||
|         print(col.listchain()) |  | ||||||
|         assert col.name == 'a' |  | ||||||
|         assert col.parent is None |  | ||||||
|         assert col.config is config |  | ||||||
| 
 |  | ||||||
|     def test_pkgfile(self, testdir, tmpdir): |  | ||||||
|         tmpdir = tmpdir.join("subdir") |  | ||||||
|         x = tmpdir.ensure("x.py") |  | ||||||
|         tmpdir.ensure("__init__.py") |  | ||||||
|         config = testdir.reparseconfig([x]) |  | ||||||
|         col = getargnode(Collection(config), x) |  | ||||||
|         assert isinstance(col, py.test.collect.Module) |         assert isinstance(col, py.test.collect.Module) | ||||||
|         assert col.name == 'x.py' |         print col.obj | ||||||
|         assert col.parent.name == x.dirpath().basename |         print col.listchain() | ||||||
|         assert col.parent.parent.parent is None |         assert col.name == 'subdir/x.py' | ||||||
|  |         assert col.parent.parent is None | ||||||
|         for col in col.listchain(): |         for col in col.listchain(): | ||||||
|             assert col.config is config |             assert col.config is config | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -81,7 +81,7 @@ class TestConfigAPI: | ||||||
|         config.trace.root.setwriter(l.append) |         config.trace.root.setwriter(l.append) | ||||||
|         config.trace("hello") |         config.trace("hello") | ||||||
|         assert len(l) == 1 |         assert len(l) == 1 | ||||||
|         assert l[0] == "[pytest:config] hello\n" |         assert l[0] == "[pytest] hello\n" | ||||||
| 
 | 
 | ||||||
|     def test_config_getvalue_honours_conftest(self, testdir): |     def test_config_getvalue_honours_conftest(self, testdir): | ||||||
|         testdir.makepyfile(conftest="x=1") |         testdir.makepyfile(conftest="x=1") | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue