refine tests and refine code to deal with new xdist semantics.
--HG-- branch : trunk
This commit is contained in:
		
							parent
							
								
									f5e9d91f7b
								
							
						
					
					
						commit
						95de17b652
					
				|  | @ -219,7 +219,7 @@ class TmpTestdir: | |||
|         if not args: | ||||
|             args = [self.tmpdir] | ||||
|         from py._test import config  | ||||
|         oldconfig = py.test.config | ||||
|         oldconfig = config.config_per_process # py.test.config | ||||
|         try: | ||||
|             c = config.config_per_process = py.test.config = pytestConfig() | ||||
|             c.basetemp = oldconfig.mktemp("reparse", numbered=True) | ||||
|  |  | |||
|  | @ -30,16 +30,13 @@ def getproject(path): | |||
|             return Project(parent) | ||||
| 
 | ||||
| class ReSTFile(py.test.collect.File): | ||||
|     def __init__(self, fspath, parent, project=None): | ||||
|     def __init__(self, fspath, parent, project): | ||||
|         super(ReSTFile, self).__init__(fspath=fspath, parent=parent) | ||||
|         if project is None: | ||||
|             project = getproject(fspath) | ||||
|             assert project is not None | ||||
|         self.project = project | ||||
| 
 | ||||
|     def collect(self): | ||||
|         return [ | ||||
|             ReSTSyntaxTest(self.project, "ReSTSyntax", parent=self), | ||||
|             ReSTSyntaxTest("ReSTSyntax", parent=self, project=self.project), | ||||
|             LinkCheckerMaker("checklinks", parent=self), | ||||
|             DoctestText("doctest", parent=self), | ||||
|         ] | ||||
|  | @ -63,8 +60,8 @@ def deindent(s, sep='\n'): | |||
|     return sep.join(lines) | ||||
| 
 | ||||
| class ReSTSyntaxTest(py.test.collect.Item):  | ||||
|     def __init__(self, project, *args, **kwargs): | ||||
|         super(ReSTSyntaxTest, self).__init__(*args, **kwargs) | ||||
|     def __init__(self, name, parent, project): | ||||
|         super(ReSTSyntaxTest, self).__init__(name=name, parent=parent) | ||||
|         self.project = project | ||||
| 
 | ||||
|     def reportinfo(self): | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| """ | ||||
| base test collection objects.  Collectors and test Items form a tree | ||||
| that is usually built iteratively.   | ||||
| test collection nodes, forming a tree, Items are leafs. | ||||
| """  | ||||
| import py | ||||
| 
 | ||||
|  | @ -33,9 +32,9 @@ class Node(object): | |||
|         self.fspath = getattr(parent, 'fspath', None)  | ||||
|         self.ihook = HookProxy(self) | ||||
| 
 | ||||
|     def _checkcollectable(self): | ||||
|         if not hasattr(self, 'fspath'): | ||||
|             self.parent._memocollect() # to reraise exception | ||||
|     def _reraiseunpicklingproblem(self): | ||||
|         if hasattr(self, '_unpickle_exc'): | ||||
|             py.builtin._reraise(*self._unpickle_exc) | ||||
|              | ||||
|     #  | ||||
|     # note to myself: Pickling is uh. | ||||
|  | @ -46,23 +45,25 @@ class Node(object): | |||
|         name, parent = nameparent | ||||
|         try: | ||||
|             colitems = parent._memocollect() | ||||
|         except KeyboardInterrupt: | ||||
|             raise | ||||
|         except Exception: | ||||
|             # seems our parent can't collect us  | ||||
|             # so let's be somewhat operable  | ||||
|             # _checkcollectable() is to tell outsiders about the fact | ||||
|             self.name = name  | ||||
|             self.parent = parent  | ||||
|             self.config = parent.config | ||||
|             #self._obj = "could not unpickle"  | ||||
|         else: | ||||
|             for colitem in colitems: | ||||
|                 if colitem.name == name: | ||||
|                     # we are a copy that will not be returned | ||||
|                     # by our parent  | ||||
|                     self.__dict__ = colitem.__dict__ | ||||
|                     break | ||||
|             else: | ||||
|                 raise ValueError("item %r not found in parent collection %r" %( | ||||
|                     name, [x.name for x in colitems])) | ||||
|         except KeyboardInterrupt: | ||||
|             raise | ||||
|         except Exception: | ||||
|             # our parent can't collect us but we want unpickling to | ||||
|             # otherwise continue - self._reraiseunpicklingproblem() will | ||||
|             # reraise the problem  | ||||
|             self._unpickle_exc = py.std.sys.exc_info() | ||||
|             self.name = name  | ||||
|             self.parent = parent  | ||||
|             self.config = parent.config | ||||
| 
 | ||||
|     def __repr__(self):  | ||||
|         if getattr(self.config.option, 'debug', False): | ||||
|  | @ -268,15 +269,12 @@ class FSCollector(Collector): | |||
|         self.fspath = fspath  | ||||
| 
 | ||||
|     def __getstate__(self): | ||||
|         if isinstance(self.parent, RootCollector): | ||||
|             relpath = self.parent._getrelpath(self.fspath) | ||||
|             return (relpath, self.parent) | ||||
|         else: | ||||
|             return (self.name, self.parent) | ||||
| 
 | ||||
|     def __setstate__(self, picklestate): | ||||
|         name, parent = picklestate | ||||
|         self.__init__(parent.fspath.join(name), parent=parent) | ||||
|         # RootCollector.getbynames() inserts a directory which we need | ||||
|         # to throw out here for proper re-instantiation | ||||
|         if isinstance(self.parent.parent, RootCollector): | ||||
|             assert self.parent.fspath == self.parent.parent.fspath, self.parent | ||||
|             return (self.name, self.parent.parent) # shortcut | ||||
|         return super(Collector, self).__getstate__() | ||||
| 
 | ||||
| class File(FSCollector): | ||||
|     """ base class for collecting tests from a file. """ | ||||
|  | @ -382,6 +380,9 @@ class RootCollector(Directory): | |||
|     def __init__(self, config): | ||||
|         Directory.__init__(self, config.topdir, parent=None, config=config) | ||||
|         self.name = None | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return "<RootCollector fspath=%r>" %(self.fspath,) | ||||
|          | ||||
|     def getbynames(self, names): | ||||
|         current = self.consider(self.config.topdir) | ||||
|  |  | |||
|  | @ -15,9 +15,9 @@ def ensuretemp(string, dir=1): | |||
|     return py.test.config.ensuretemp(string, dir=dir) | ||||
|    | ||||
| class CmdOptions(object): | ||||
|     """ pure container instance for holding cmdline options  | ||||
|         as attributes.  | ||||
|     """ | ||||
|     """ holds cmdline options as attributes.""" | ||||
|     def __init__(self, **kwargs): | ||||
|         self.__dict__.update(kwargs) | ||||
|     def __repr__(self): | ||||
|         return "<CmdOptions %r>" %(self.__dict__,) | ||||
| 
 | ||||
|  | @ -31,8 +31,8 @@ class Config(object): | |||
|     basetemp = None | ||||
|     _sessionclass = None | ||||
| 
 | ||||
|     def __init__(self, topdir=None):  | ||||
|         self.option = CmdOptions() | ||||
|     def __init__(self, topdir=None, option=None):  | ||||
|         self.option = option or CmdOptions() | ||||
|         self.topdir = topdir | ||||
|         self._parser = parseopt.Parser( | ||||
|             usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]", | ||||
|  | @ -47,9 +47,9 @@ class Config(object): | |||
|         self.pluginmanager.consider_conftest(conftestmodule) | ||||
| 
 | ||||
|     def _getmatchingplugins(self, fspath): | ||||
|         conftests = self._conftest._conftestpath2mod.values() | ||||
|         allconftests = self._conftest._conftestpath2mod.values() | ||||
|         plugins = [x for x in self.pluginmanager.getplugins()  | ||||
|                         if x not in conftests] | ||||
|                         if x not in allconftests] | ||||
|         plugins += self._conftest.getconftestmodules(fspath) | ||||
|         return plugins | ||||
| 
 | ||||
|  | @ -114,20 +114,20 @@ class Config(object): | |||
|         for path in self.args: | ||||
|             path = py.path.local(path) | ||||
|             l.append(path.relto(self.topdir))  | ||||
|         return l, vars(self.option) | ||||
|         return l, self.option.__dict__ | ||||
| 
 | ||||
|     def __setstate__(self, repr): | ||||
|         # we have to set py.test.config because loading  | ||||
|         # of conftest files may use it (deprecated)  | ||||
|         # mainly by py.test.config.addoptions()  | ||||
|         py.test.config = self  | ||||
|         # next line will registers default plugins  | ||||
|         self.__init__(topdir=py.path.local()) | ||||
|         self._rootcol = RootCollector(config=self) | ||||
|         global config_per_process | ||||
|         py.test.config = config_per_process = self  | ||||
|         args, cmdlineopts = repr  | ||||
|         cmdlineopts = CmdOptions(**cmdlineopts) | ||||
|         # next line will registers default plugins  | ||||
|         self.__init__(topdir=py.path.local(), option=cmdlineopts) | ||||
|         self._rootcol = RootCollector(config=self) | ||||
|         args = [str(self.topdir.join(x)) for x in args] | ||||
|         self.option = CmdOptions() | ||||
|         self.option.__dict__.update(cmdlineopts) | ||||
|         self._preparse(args) | ||||
|         self._setargs(args) | ||||
| 
 | ||||
|  | @ -177,7 +177,7 @@ class Config(object): | |||
| 
 | ||||
|     def _getcollectclass(self, name, path): | ||||
|         try: | ||||
|             cls = self.getvalue(name, path) | ||||
|             cls = self._conftest.rget(name, path) | ||||
|         except KeyError: | ||||
|             return getattr(py.test.collect, name) | ||||
|         else: | ||||
|  |  | |||
|  | @ -24,6 +24,8 @@ class Session(object): | |||
| 
 | ||||
|     def genitems(self, colitems, keywordexpr=None): | ||||
|         """ yield Items from iterating over the given colitems. """ | ||||
|         if colitems: | ||||
|             colitems = list(colitems) | ||||
|         while colitems:  | ||||
|             next = colitems.pop(0) | ||||
|             if isinstance(next, (tuple, list)): | ||||
|  |  | |||
|  | @ -33,21 +33,16 @@ class Option: | |||
| 
 | ||||
| def pytest_generate_tests(metafunc): | ||||
|     if "option" in metafunc.funcargnames: | ||||
|         metafunc.addcall( | ||||
|             id="default",  | ||||
|             funcargs={'option': Option(verbose=False)} | ||||
|         ) | ||||
|         metafunc.addcall( | ||||
|             id="verbose",  | ||||
|             funcargs={'option': Option(verbose=True)} | ||||
|         ) | ||||
|         if metafunc.config.pluginmanager.hasplugin("xdist"): | ||||
|             nodist = getattr(metafunc.function, 'nodist', False) | ||||
|             if not nodist: | ||||
|                 metafunc.addcall( | ||||
|                     id="verbose-dist",  | ||||
|                     funcargs={'option': Option(dist='each', verbose=True)} | ||||
|                 ) | ||||
|         metafunc.addcall(id="default", param=Option(verbose=False)) | ||||
|         metafunc.addcall(id="verbose", param=Option(verbose=True)) | ||||
|         if not getattr(metafunc.function, 'nodist', False): | ||||
|             metafunc.addcall(id="verbose-dist",  | ||||
|                              param=Option(dist='each', verbose=True)) | ||||
| 
 | ||||
| def pytest_funcarg__option(request): | ||||
|     if request.param.dist: | ||||
|         request.config.pluginmanager.skipifmissing("xdist") | ||||
|     return request.param | ||||
| 
 | ||||
| class TestTerminal: | ||||
|     def test_pass_skip_fail(self, testdir, option): | ||||
|  | @ -255,12 +250,19 @@ class TestTerminal: | |||
|         ]) | ||||
| 
 | ||||
|     def test_keyboard_interrupt_dist(self, testdir, option): | ||||
|         # xxx could be refined to check for return code  | ||||
|         p = testdir.makepyfile(""" | ||||
|             raise KeyboardInterrupt | ||||
|             def test_sleep(): | ||||
|                 import time | ||||
|                 time.sleep(10) | ||||
|         """) | ||||
|         result = testdir.runpytest(*option._getcmdargs()) | ||||
|         assert result.ret == 2 | ||||
|         result.stdout.fnmatch_lines(['*KEYBOARD INTERRUPT*']) | ||||
|         child = testdir.spawn_pytest(" ".join(option._getcmdargs())) | ||||
|         child.expect(".*test session starts.*") | ||||
|         child.kill(2) # keyboard interrupt | ||||
|         child.expect(".*KeyboardInterrupt.*") | ||||
|         #child.expect(".*seconds.*") | ||||
|         child.close() | ||||
|         #assert ret == 2  | ||||
| 
 | ||||
|     @py.test.mark.nodist | ||||
|     def test_keyboard_interrupt(self, testdir, option): | ||||
|  | @ -593,9 +595,10 @@ def test_terminalreporter_reportopt_conftestsetting(testdir): | |||
|     assert result.stdout.fnmatch_lines([ | ||||
|         "*1 passed*" | ||||
|     ]) | ||||
|     def test_trace_reporting(self, testdir): | ||||
|         result = testdir.runpytest("--trace") | ||||
|         assert result.stdout.fnmatch_lines([ | ||||
|             "*active plugins*" | ||||
|         ]) | ||||
|         assert result.ret == 0 | ||||
| 
 | ||||
| def test_trace_reporting(testdir): | ||||
|     result = testdir.runpytest("--traceconfig") | ||||
|     assert result.stdout.fnmatch_lines([ | ||||
|         "*active plugins*" | ||||
|     ]) | ||||
|     assert result.ret == 0 | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ def test_new_instances(testdir): | |||
|     reprec.assertoutcome(passed=2) | ||||
| 
 | ||||
| def test_teardown(testdir): | ||||
|     testpath = testdir.makepyfile(test_three=""" | ||||
|     testpath = testdir.makepyfile(""" | ||||
|         import unittest | ||||
|         pytest_plugins = "pytest_unittest" # XXX  | ||||
|         class MyTestCase(unittest.TestCase): | ||||
|  |  | |||
|  | @ -246,3 +246,132 @@ def test_preparse_ordering(testdir, monkeypatch): | |||
|     plugin = config.pluginmanager.getplugin("mytestplugin") | ||||
|     assert plugin.x == 42 | ||||
| 
 | ||||
| 
 | ||||
| import pickle | ||||
| class TestConfigPickling: | ||||
|     def pytest_funcarg__testdir(self, request): | ||||
|         oldconfig = py.test.config  | ||||
|         print("setting py.test.config to None") | ||||
|         py.test.config = None | ||||
|         def resetglobals(): | ||||
|             py.builtin.print_("setting py.test.config to", oldconfig) | ||||
|             py.test.config = oldconfig | ||||
|         request.addfinalizer(resetglobals) | ||||
|         return request.getfuncargvalue("testdir") | ||||
| 
 | ||||
|     def test_config_getstate_setstate(self, testdir): | ||||
|         from py._test.config import Config | ||||
|         testdir.makepyfile(__init__="", conftest="x=1; y=2") | ||||
|         hello = testdir.makepyfile(hello="") | ||||
|         tmp = testdir.tmpdir | ||||
|         testdir.chdir() | ||||
|         config1 = testdir.parseconfig(hello) | ||||
|         config2 = Config() | ||||
|         config2.__setstate__(config1.__getstate__()) | ||||
|         assert config2.topdir == py.path.local() | ||||
|         config2_relpaths = [py.path.local(x).relto(config2.topdir)  | ||||
|                                 for x in config2.args] | ||||
|         config1_relpaths = [py.path.local(x).relto(config1.topdir)  | ||||
|                                 for x in config1.args] | ||||
| 
 | ||||
|         assert config2_relpaths == config1_relpaths | ||||
|         for name, value in config1.option.__dict__.items(): | ||||
|             assert getattr(config2.option, name) == value | ||||
|         assert config2.getvalue("x") == 1 | ||||
| 
 | ||||
|     def test_config_pickling_customoption(self, testdir): | ||||
|         testdir.makeconftest(""" | ||||
|             def pytest_addoption(parser): | ||||
|                 group = parser.getgroup("testing group") | ||||
|                 group.addoption('-G', '--glong', action="store", default=42,  | ||||
|                     type="int", dest="gdest", help="g value.") | ||||
|         """) | ||||
|         config = testdir.parseconfig("-G", "11") | ||||
|         assert config.option.gdest == 11 | ||||
|         repr = config.__getstate__() | ||||
| 
 | ||||
|         config = testdir.Config() | ||||
|         py.test.raises(AttributeError, "config.option.gdest") | ||||
| 
 | ||||
|         config2 = testdir.Config() | ||||
|         config2.__setstate__(repr)  | ||||
|         assert config2.option.gdest == 11 | ||||
| 
 | ||||
|     def test_config_pickling_and_conftest_deprecated(self, testdir): | ||||
|         tmp = testdir.tmpdir.ensure("w1", "w2", dir=1) | ||||
|         tmp.ensure("__init__.py") | ||||
|         tmp.join("conftest.py").write(py.code.Source(""" | ||||
|             def pytest_addoption(parser): | ||||
|                 group = parser.getgroup("testing group") | ||||
|                 group.addoption('-G', '--glong', action="store", default=42,  | ||||
|                     type="int", dest="gdest", help="g value.") | ||||
|         """)) | ||||
|         config = testdir.parseconfig(tmp, "-G", "11") | ||||
|         assert config.option.gdest == 11 | ||||
|         repr = config.__getstate__() | ||||
| 
 | ||||
|         config = testdir.Config() | ||||
|         py.test.raises(AttributeError, "config.option.gdest") | ||||
| 
 | ||||
|         config2 = testdir.Config() | ||||
|         config2.__setstate__(repr)  | ||||
|         assert config2.option.gdest == 11 | ||||
|         | ||||
|         option = config2.addoptions("testing group",  | ||||
|                 config2.Option('-G', '--glong', action="store", default=42, | ||||
|                        type="int", dest="gdest", help="g value.")) | ||||
|         assert option.gdest == 11 | ||||
| 
 | ||||
|     def test_config_picklability(self, testdir): | ||||
|         config = testdir.parseconfig() | ||||
|         s = pickle.dumps(config) | ||||
|         newconfig = pickle.loads(s) | ||||
|         assert hasattr(newconfig, "topdir") | ||||
|         assert newconfig.topdir == py.path.local() | ||||
| 
 | ||||
|     def test_collector_implicit_config_pickling(self, testdir): | ||||
|         tmpdir = testdir.tmpdir | ||||
|         testdir.chdir() | ||||
|         testdir.makepyfile(hello="def test_x(): pass") | ||||
|         config = testdir.parseconfig(tmpdir) | ||||
|         col = config.getnode(config.topdir) | ||||
|         io = py.io.BytesIO() | ||||
|         pickler = pickle.Pickler(io) | ||||
|         pickler.dump(col) | ||||
|         io.seek(0)  | ||||
|         unpickler = pickle.Unpickler(io) | ||||
|         col2 = unpickler.load() | ||||
|         assert col2.name == col.name  | ||||
|         assert col2.listnames() == col.listnames() | ||||
| 
 | ||||
|     def test_config_and_collector_pickling(self, testdir): | ||||
|         tmpdir = testdir.tmpdir | ||||
|         dir1 = tmpdir.ensure("sourcedir", "somedir", dir=1) | ||||
|         config = testdir.parseconfig() | ||||
|         assert config.topdir == tmpdir | ||||
|         col = config.getnode(dir1.dirpath()) | ||||
|         col1 = config.getnode(dir1) | ||||
|         assert col1.parent == col  | ||||
|         io = py.io.BytesIO() | ||||
|         pickler = pickle.Pickler(io) | ||||
|         pickler.dump(col) | ||||
|         pickler.dump(col1) | ||||
|         pickler.dump(col) | ||||
|         io.seek(0)  | ||||
|         unpickler = pickle.Unpickler(io) | ||||
|         newtopdir = tmpdir.ensure("newtopdir", dir=1) | ||||
|         newtopdir.mkdir("sourcedir").mkdir("somedir") | ||||
|         old = newtopdir.chdir() | ||||
|         try: | ||||
|             newcol = unpickler.load() | ||||
|             newcol2 = unpickler.load() | ||||
|             newcol3 = unpickler.load() | ||||
|             assert newcol2.config is newcol.config | ||||
|             assert newcol2.parent == newcol  | ||||
|             assert newcol2.config.topdir.realpath() == newtopdir.realpath() | ||||
|             newsourcedir = newtopdir.join("sourcedir") | ||||
|             assert newcol.fspath.realpath() == newsourcedir.realpath() | ||||
|             assert newcol2.fspath.basename == dir1.basename | ||||
|             assert newcol2.fspath.relto(newcol2.config.topdir) | ||||
|         finally: | ||||
|             old.chdir()  | ||||
|  |  | |||
|  | @ -193,8 +193,3 @@ class TestNewSession(SessionTests): | |||
|         colfail = [x for x in finished if x.failed] | ||||
|         assert len(colfail) == 1 | ||||
| 
 | ||||
| class TestNewSessionDSession(SessionTests): | ||||
|     def parseconfig(self, *args): | ||||
|         args = ('-n1',) + args | ||||
|         return SessionTests.parseconfig(self, *args) | ||||
|      | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue