From hpk at codespeak.net Wed Jul 2 10:42:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 2 Jul 2008 10:42:26 +0200 (CEST) Subject: [py-svn] r56231 - py/trunk/py/bin Message-ID: <20080702084226.49340169E9C@codespeak.net> Author: hpk Date: Wed Jul 2 10:42:24 2008 New Revision: 56231 Added: py/trunk/py/bin/py.which (contents, props changed) Log: add a small script that tells where an import of a python module package would come from. py.which is meant to be the equivalent of "which" in unix. Added: py/trunk/py/bin/py.which ============================================================================== --- (empty file) +++ py/trunk/py/bin/py.which Wed Jul 2 10:42:24 2008 @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +"""\ +py.which [name] + +print the location of the given python module or package name +""" + +import sys + +if __name__ == '__main__': + name = sys.argv[1] + try: + mod = __import__(name) + except ImportError: + print >>sys.stderr, "could not import:", name + else: + try: + location = mod.__file__ + except AttributeError: + print >>sys.stderr, "module (has no __file__):", mod + else: + print location From hpk at codespeak.net Fri Jul 4 08:51:54 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 08:51:54 +0200 (CEST) Subject: [py-svn] r56284 - in py/trunk/py: . test/testing Message-ID: <20080704065154.44CB5398005@codespeak.net> Author: hpk Date: Fri Jul 4 08:51:51 2008 New Revision: 56284 Modified: py/trunk/py/__init__.py py/trunk/py/test/testing/test_session.py Log: removing unadvertised new names and adjusting the test to show how you can put custom messages otherwise. Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Fri Jul 4 08:51:51 2008 @@ -31,8 +31,6 @@ 'test.skip' : ('./test/item.py', 'skip'), 'test.fail' : ('./test/item.py', 'fail'), 'test.exit' : ('./test/session.py', 'exit'), - 'test.broken' : ('./test/item.py', 'Broken'), - 'test.notimplemented' : ('./test/item.py', '_NotImplemented'), 'test.pdb' : ('./test/custompdb.py', 'set_trace'), # configuration/initialization related test api Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Fri Jul 4 08:51:51 2008 @@ -282,10 +282,10 @@ tmp.ensure("test_one.py").write(py.code.Source(""" import py def test_1(): - py.test.skip(py.test.broken('stuff')) + py.test.skip("Broken: stuff") def test_2(): - py.test.skip(py.test.notimplemented('stuff')) + py.test.skip("Not implemented: stuff") """)) tmp.ensure("__init__.py") config = py.test.config._reparse([tmp]) @@ -294,6 +294,6 @@ session.main(all.append) skips = getskipped(all) assert len(skips) == 2 - assert str(skips[0].skipped.value) == 'Broken: stuff' - assert str(skips[1].skipped.value) == 'Not implemented: stuff' + assert str(skips[0].skipped.value).find('Broken: stuff') != -1 + assert str(skips[1].skipped.value).find('Not implemented: stuff') != -1 From hpk at codespeak.net Fri Jul 4 09:14:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 09:14:37 +0200 (CEST) Subject: [py-svn] r56285 - in py/trunk/py/test: . testing Message-ID: <20080704071437.648452A00DB@codespeak.net> Author: hpk Date: Fri Jul 4 09:14:36 2008 New Revision: 56285 Modified: py/trunk/py/test/collect.py py/trunk/py/test/doctest.py py/trunk/py/test/item.py py/trunk/py/test/testing/test_doctest.py Log: sanitize doctesting a bit. this also fixes the problem that you could not have "import" working in doctests previously. thanks nshepperd. Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Fri Jul 4 09:14:36 2008 @@ -502,13 +502,17 @@ call, args = obj, () return call, args -class DoctestFile(PyCollectorMixin, FSCollector): +class DoctestFile(Module): + # XXX fix py.test reporting + # we subclass Module here in order to get py.test's reporting + # show the ".txt" filename in the test run much like a + # python module shows up. instead py.test needs to + # support more direct means of influencing reporting. def run(self): return [self.fspath.basename] def join(self, name): - from py.__.test.doctest import DoctestText - if name == self.fspath.basename: - item = DoctestText(self.fspath.basename, parent=self) - item._content = self.fspath.read() - return item + if name == self.fspath.basename: + from py.__.test.doctest import DoctestFileContent + return DoctestFileContent(name, self) + Modified: py/trunk/py/test/doctest.py ============================================================================== --- py/trunk/py/test/doctest.py (original) +++ py/trunk/py/test/doctest.py Fri Jul 4 09:14:36 2008 @@ -1,9 +1,6 @@ import py -class DoctestText(py.test.collect.Item): - - def _setcontent(self, content): - self._content = content +class DoctestFileContent(py.test.collect.Item): #def buildname2items(self): # parser = py.compat.doctest.DoctestParser() @@ -16,18 +13,12 @@ # d[str(i)] = ex def run(self): - mod = py.std.types.ModuleType(self.name) - #for line in s.split('\n'): - # if line.startswith(prefix): - # exec py.code.Source(line[len(prefix):]).compile() in mod.__dict__ - # line = "" - # else: - # l.append(line) - self.execute(mod, self._content) + self.execute() - def execute(self, mod, docstring): - mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) + def execute(self): + failed, tot = py.compat.doctest.testfile(str(self.fspath), module_relative=False, verbose=1) + #mod.__file__ = str(self.fspath) + #failed, tot = py.compat.doctest.testmod(mod, verbose=1) if failed: py.test.fail("doctest %s: %s failed out of %s" %( self.fspath, failed, tot)) Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Fri Jul 4 09:14:36 2008 @@ -78,14 +78,6 @@ def __repr__(self): return self.msg -class Broken(BaseReason): - def __repr__(self): - return "Broken: %s" % (self.msg,) - -class _NotImplemented(BaseReason): - def __repr__(self): - return "Not implemented: %s" % (self.msg,) - # whatever comes here.... def skip(msg=BaseReason()): Modified: py/trunk/py/test/testing/test_doctest.py ============================================================================== --- py/trunk/py/test/testing/test_doctest.py (original) +++ py/trunk/py/test/testing/test_doctest.py Fri Jul 4 09:14:36 2008 @@ -1,25 +1,30 @@ import py -from py.__.test.doctest import DoctestText from py.__.test.outcome import Skipped, Failed, Passed, Outcome + +def setup_module(mod): + mod.tmp = py.test.ensuretemp(__name__) + def test_simple_docteststring(): - testitem = DoctestText(name="dummy", parent=None) - testitem._setcontent(""" - >>> i = 0 - >>> i + 1 - 1 - """) + p = tmp.join("test_simple_docteststring") + p.write(py.code.Source(""" + >>> i = 0 + >>> i + 1 + 1 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) res = testitem.run() assert res is None def test_simple_docteststring_failing(): - testitem = DoctestText(name="dummy2", parent=None) - testitem._setcontent(""" - >>> i = 0 - >>> i + 1 - 2 - """) + p = tmp.join("test_simple_docteststring_failing") + p.write(py.code.Source(""" + >>> i = 0 + >>> i + 1 + 2 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) py.test.raises(Failed, "testitem.run()") @@ -39,6 +44,6 @@ col = config._getcollector(x) items = list(col._tryiter(py.test.collect.Item)) assert len(items) == 1 - assert isinstance(items[0], DoctestText) + assert isinstance(items[0].parent, py.test.collect.DoctestFile) From hpk at codespeak.net Fri Jul 4 10:01:16 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 10:01:16 +0200 (CEST) Subject: [py-svn] r56286 - py/trunk/py/doc Message-ID: <20080704080116.C3052169F1D@codespeak.net> Author: hpk Date: Fri Jul 4 10:01:15 2008 New Revision: 56286 Modified: py/trunk/py/doc/TODO.txt Log: note that --showlocals doesn't work on trunk Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Fri Jul 4 10:01:15 2008 @@ -4,8 +4,11 @@ py.test -------------- +- BUG: write test/fix --showlocals (not showing anything) + - review and refactor architecture of py.test with particular respect to: + - allow custom reporting - writing (stacked) extensions / plugins (compared to Nose) - event naming and processing - porting existing extensions (htmlconftest / buildbot / PyPy's conftest's ...) From hpk at codespeak.net Fri Jul 4 10:45:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 10:45:36 +0200 (CEST) Subject: [py-svn] r56290 - in py/extradoc/talk: . ep2008 Message-ID: <20080704084536.5D81716A07A@codespeak.net> Author: hpk Date: Fri Jul 4 10:45:34 2008 New Revision: 56290 Added: py/extradoc/talk/ py/extradoc/talk/ep2008/ py/extradoc/talk/ep2008/pytest.txt - copied, changed from r56289, pypy/extradoc/talk/ep2007/pytest/pytest.txt py/extradoc/talk/ep2008/ui - copied unchanged from r56289, pypy/extradoc/talk/ep2007/pytest/ui Log: adding beginnings of py.test tutorial talk for ep2007 Copied: py/extradoc/talk/ep2008/pytest.txt (from r56289, pypy/extradoc/talk/ep2007/pytest/pytest.txt) ============================================================================== --- pypy/extradoc/talk/ep2007/pytest/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 10:45:34 2008 @@ -1,22 +1,23 @@ .. include:: ================================================================= -py.test: towards interactive, distributed and rapid testing +py.test: rapid testing with minimal effort ================================================================= -:authors: Holger Krekel, Maciej Fijalkowski (merlinux GmbH) -:event: 9.7.2007, EuroPython 2007, Vilnius - +:authors: Holger Krekel (merlinux GmbH) +:event: 7.7.2008, EuroPython 2008, Vilnius Intro =================== -- py.test released 0.9 as part of py lib, feb 2007 +- py.test released 0.9.1 early 2008 - cross-project external testing tool -- many mature and experimental features +- many mature features - minimal boilerplate approach -- developed partly for purposes of PyPy -- this talk will assess status and discuss future +- this talk will introduce its usage with a particular focus + on its extension mechanism + +XXX review and modify the below recap: main features ====================== From hpk at codespeak.net Fri Jul 4 14:14:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 14:14:31 +0200 (CEST) Subject: [py-svn] r56299 - py/extradoc/talk/ep2008 Message-ID: <20080704121431.4BC95169F6F@codespeak.net> Author: hpk Date: Fri Jul 4 14:14:30 2008 New Revision: 56299 Added: py/extradoc/talk/ep2008/test_ospath1.py (contents, props changed) py/extradoc/talk/ep2008/test_ospath2.py (contents, props changed) py/extradoc/talk/ep2008/test_ospath3.py (contents, props changed) Modified: py/extradoc/talk/ep2008/ (props changed) py/extradoc/talk/ep2008/pytest.txt Log: snapshot of my py.test tutorial Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 14:14:30 2008 @@ -7,17 +7,119 @@ :authors: Holger Krekel (merlinux GmbH) :event: 7.7.2008, EuroPython 2008, Vilnius -Intro +What is py.test? =================== -- py.test released 0.9.1 early 2008 -- cross-project external testing tool +- cross-project testing tool +- minimal boilerplate - many mature features -- minimal boilerplate approach -- this talk will introduce its usage with a particular focus - on its extension mechanism -XXX review and modify the below +Structure of Tutorial +========================== + +1. write and run test + +2. generative tests + +3. layering setup and teardown state + +4. writing and using extensions + + +Install py.test +============================ + +- install "py.test" (see http://pylib.org) + - via "easy_install py" + - via download and "python setup.py install" + - via svn and adding py/bin to system $PATH + + +Write and run a test +=================================== + +let's write a "test_ospath1.py":: + + import os.path + + def test_abspath(): + assert os.path.abspath("/a/b/c") == "/a/b/c" + p = "a/b" + cur = os.path.join(os.getcwd(), "a/b") + assert os.path.abspath("a/b/c") == "/a/b/c" + + +then issue on the command line:: + + py.test test_ospath1.py + + + +Putting tests in Test Classes +================================== + +test_ospath2.py (one test failing):: + + import os.path + class TestOSPath: + def test_absolute(self): + assert os.path.abspath("/a/b/c") == "/a/b/c" + def test_relative(self): + p = "a/b" + cur = os.path.join(os.getcwd(), "a/b") + assert os.path.abspath("a/b/c") == cur + + +observations and notes +======================== + +- py.test automatically collects ``test_*`` functions +- use ``py.test --collectonly`` to inspect collection +- you can write tests at module global level +- no need to import or subclass from py.test +- assertion failures provide detailed info + +Generative / Parametrized tests +================================= + +test_ospath3.py:: + + class TestOSPath: + def test_gen_absolute(self): + def checkabssame(p1): + print "checking on", repr(p1) + assert os.path.abspath(p1) == p1 + for s in "/ /a /a/b c".split(): + yield checkabssame, s + + def test_gen_relative(self): + for p in "a a/b".split(): + p = "a/b" + expected = os.path.join(os.getcwd(), p) + yield lambda x,y: x==y, p, expected + + +Noticeable Features +============================ + +- stdout/stderr is captured per-test +- ``-l | --showlocals``: show local variables in traceback +- ``--pdb``: jump into pdb for failures +- ``-k KEYWORD``: select tests to run +- ``-x | --exitfirst``: stop on first test failure + + +Test Driven Development (at its best) +======================================= + +- enter ``py.test --looponfailing``: + - does a full test run + - keeps a set of failing tests + - checks for file changes and re-runs failing tests + - if all failures are fixed, re-run the whole + + +XXX look and erase below recap: main features ====================== Added: py/extradoc/talk/ep2008/test_ospath1.py ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/test_ospath1.py Fri Jul 4 14:14:30 2008 @@ -0,0 +1,8 @@ + +import os.path + +def test_abspath(): + assert os.path.abspath("/a/b/c") == "/a/b/c" + p1 = "a/b" + p2 = os.path.join(os.getcwd(), "a/c") + assert os.path.abspath(p1) == p2 Added: py/extradoc/talk/ep2008/test_ospath2.py ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/test_ospath2.py Fri Jul 4 14:14:30 2008 @@ -0,0 +1,9 @@ +import os.path +class TestOSPath: + def test_absolute(self): + assert os.path.abspath("/a/b/c") == "/a/b/c" + def test_relative(self): + p = "a/b" + cur = os.path.join(os.getcwd(), "a/b") + assert os.path.abspath("a/b/c") == cur + Added: py/extradoc/talk/ep2008/test_ospath3.py ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/test_ospath3.py Fri Jul 4 14:14:30 2008 @@ -0,0 +1,16 @@ + +import os.path + +class TestOSPath: + def test_gen_absolute(self): + def checkabssame(p1): + print "checking on", repr(p1) + assert os.path.abspath(p1) == p1 + for s in "/ /a /a/b c".split(): + yield checkabssame, s + + def test_gen_relative(self): + for p in "a a/b".split(): + p = "a/b" + expected = os.path.join(os.getcwd(), p) + yield lambda x,y: x==y, p, expected From hpk at codespeak.net Fri Jul 4 15:53:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 15:53:13 +0200 (CEST) Subject: [py-svn] r56303 - py/extradoc/talk/ep2008 Message-ID: <20080704135313.E5724168543@codespeak.net> Author: hpk Date: Fri Jul 4 15:53:12 2008 New Revision: 56303 Added: py/extradoc/talk/ep2008/test_doc.txt py/extradoc/talk/ep2008/test_ospath4.py Modified: py/extradoc/talk/ep2008/pytest.txt py/extradoc/talk/ep2008/test_ospath1.py Log: roughly completing the first section Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 15:53:12 2008 @@ -17,16 +17,23 @@ Structure of Tutorial ========================== -1. write and run test +writing and running tests: + - test functions + - test classes + - generative tests + - test driven development + - setup and teardown test state + - skipping tests + - doctests + +writing and using extensions: + - conftest.py files + - test collection hooks + - test running hooks + - existing extensions -2. generative tests -3. layering setup and teardown state - -4. writing and using extensions - - -Install py.test +First: Install py.test ============================ - install "py.test" (see http://pylib.org) @@ -35,26 +42,28 @@ - via svn and adding py/bin to system $PATH -Write and run a test +Write and run a first test =================================== let's write a "test_ospath1.py":: - import os.path + import os.path, py def test_abspath(): assert os.path.abspath("/a/b/c") == "/a/b/c" p = "a/b" cur = os.path.join(os.getcwd(), "a/b") assert os.path.abspath("a/b/c") == "/a/b/c" - + + def test_typeerror(): + assert py.test.raises(TypeError, "os.path.abspath()") + XXX check why "os.path.abspath(3)" cannot display source lines correctly then issue on the command line:: py.test test_ospath1.py - Putting tests in Test Classes ================================== @@ -76,7 +85,7 @@ - py.test automatically collects ``test_*`` functions - use ``py.test --collectonly`` to inspect collection - you can write tests at module global level -- no need to import or subclass from py.test +- no need to subclass from py.test - assertion failures provide detailed info Generative / Parametrized tests @@ -99,7 +108,7 @@ yield lambda x,y: x==y, p, expected -Noticeable Features +Useful Features ============================ - stdout/stderr is captured per-test @@ -118,8 +127,67 @@ - checks for file changes and re-runs failing tests - if all failures are fixed, re-run the whole +- great for refactoring + + +Setup and Teardown of test state +================================== + +hooks at module, class, instance level, example:: + +import py, os.path + +def setup_module(mod): + mod.tmpdir = py.test.ensuretemp(__name__) + +class TestOS: + def setup_method(self, method): + self.tmpdir = tmpdir.mkdir(method.__name__) + print "chdir() to ", self.tmpdir + self.oldcwd = self.tmpdir.chdir() + def teardown_method(self, method): + self.oldcwd.chdir() + + def test_relative(self): + p = "a/b" + expected = os.path.join(str(self.tmpdir), p) + assert os.path.abspath(p) == expected + "xxx" + + +Skipping tests +=================== + +the above tests do not make sense for win32, +you can insert e.g.:: + + class TestOSPosix: + def setup_class(cls): + if sys.platform == "win32": + py.test.skip("posix platform required") + +you can of course also place such skips inside test functions +or in other test setup functions. + +Doctests +================= + +py.test automatically collects test_doc.txt files:: + + this is an example doc test for the py.test tutorial + + >>> x = 3 + >>> print x + 3 + + +XXX write and refactor below +Extending py.test +=========================== +k +- py.test allows to insert custom test items +- py.test consults "conftest.py" files + -XXX look and erase below recap: main features ====================== Added: py/extradoc/talk/ep2008/test_doc.txt ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/test_doc.txt Fri Jul 4 15:53:12 2008 @@ -0,0 +1,6 @@ + +this is an example doc test for the py.test tutorial + +>>> x = 3 +>>> print x +3 Modified: py/extradoc/talk/ep2008/test_ospath1.py ============================================================================== --- py/extradoc/talk/ep2008/test_ospath1.py (original) +++ py/extradoc/talk/ep2008/test_ospath1.py Fri Jul 4 15:53:12 2008 @@ -1,8 +1,11 @@ - -import os.path +import os.path, py def test_abspath(): assert os.path.abspath("/a/b/c") == "/a/b/c" - p1 = "a/b" - p2 = os.path.join(os.getcwd(), "a/c") - assert os.path.abspath(p1) == p2 + p = "a/b" + cur = os.path.join(os.getcwd(), "a/b") + assert os.path.abspath("a/b/c") == "/a/b/c" + +def test_typeerror(): + assert py.test.raises(TypeError, "os.path.abspath()") + Added: py/extradoc/talk/ep2008/test_ospath4.py ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/test_ospath4.py Fri Jul 4 15:53:12 2008 @@ -0,0 +1,18 @@ + +import py, os.path + +def setup_module(mod): + mod.tmpdir = py.test.ensuretemp(__name__) + +class TestOS: + def setup_method(self, method): + self.tmpdir = tmpdir.mkdir(method.__name__) + print "chdir() to ", self.tmpdir + self.oldcwd = self.tmpdir.chdir() + def teardown_method(self, method): + self.oldcwd.chdir() + + def test_relative(self): + p = "a/b" + expected = os.path.join(str(self.tmpdir), p) + assert os.path.abspath(p) == expected + "xxx" From hpk at codespeak.net Fri Jul 4 16:03:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 16:03:19 +0200 (CEST) Subject: [py-svn] r56304 - py/trunk/py/doc Message-ID: <20080704140319.ADD48168545@codespeak.net> Author: hpk Date: Fri Jul 4 16:03:19 2008 New Revision: 56304 Modified: py/trunk/py/doc/test.txt Log: almost any version works Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Fri Jul 4 16:03:19 2008 @@ -501,7 +501,7 @@ * ssh daemon running * ssh keys setup to allow login without a password -* python (2.3 or 2.4 should work) +* python * unix like machine (reliance on ``os.fork``) How to use it From hpk at codespeak.net Fri Jul 4 18:36:42 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 18:36:42 +0200 (CEST) Subject: [py-svn] r56308 - py/extradoc/talk/ep2008 Message-ID: <20080704163642.7EBE4169F26@codespeak.net> Author: hpk Date: Fri Jul 4 18:36:41 2008 New Revision: 56308 Modified: py/extradoc/talk/ep2008/pytest.txt Log: complete the draft talk Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 18:36:41 2008 @@ -4,7 +4,7 @@ py.test: rapid testing with minimal effort ================================================================= -:authors: Holger Krekel (merlinux GmbH) +:author: Holger Krekel, merlinux GmbH :event: 7.7.2008, EuroPython 2008, Vilnius What is py.test? @@ -180,13 +180,94 @@ 3 -XXX write and refactor below -Extending py.test +Customizing py.test =========================== -k -- py.test allows to insert custom test items -- py.test consults "conftest.py" files - + +- write "confest.py" files at project or global level +- conftest.py allow to: + - integrate new test "items" or "collectors" + - add command line options + - influence the collection process +- conftest.py's are picked up "upwards" +- debug aid:: + py.test --collectonly + py.test --traceconfig + + +The collection process +=========================== + +- test collection starts from directories or files +- collection process forms a tree: + - leaves: so called "items", e.g. ``py.test.collect.Function`` + - nodes: so called "collectors" contain further collectors or items +- lookup of Collector/Item class from conftest.py files + + +Example conftest.py: add ReST check support +============================================== + +look into py/doc/conftest.py: + +- this produces non-python test items +- ``py.test --collectonly`` shows the custom tree +- produces syntax and link checks +- drop the conftest.py file into your own project/doc directory + +Example conftest.py: html page generation +=========================================== + +http://codespeak.net/svn/user/arigo/hack/misc/htmlconftest + +- creates html output with "clickable" failures +- creates a nice historic view on test failures +- see http://wyvern.cs.uni-duesseldorf.de/pypytest/summary.html +- see README.sh for usage for your project + +Example conftest.py: run Prolog tests +====================================== + +http://codespeak.net/svn/user/cfbolz/jitpl/dist/test/conftest.py + +- creates PrologTest items +- collects items and runs them through Prolog interpreter + + +Example conftest.py: cross-platform testing +============================================= + +- goal: remotely run tests on windows +- check out py/misc/conftest-socketgatewayrun.py +- requires one small script on the windows side +- transfers your source code to windows, runs tests, + reports on your terminal + + +Summary extensions +======================== + +- conftest.py's can be re-used per-project +- can add new (non-python) test items +- if you use the above conftest.py's or have some of your own + please subscribe http://codespeak.net/mailman/listinfo/py-dev + +note, however: +- no easy customization of reporting (yet) +- some details will change for py lib 1.0 release + +XXX refactor / do extra slides see what could stay / below + + + + + + + + + + + + recap: main features From hpk at codespeak.net Fri Jul 4 21:05:58 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 21:05:58 +0200 (CEST) Subject: [py-svn] r56312 - py/extradoc/talk/ep2008 Message-ID: <20080704190558.B0C24698126@codespeak.net> Author: hpk Date: Fri Jul 4 21:05:56 2008 New Revision: 56312 Modified: py/extradoc/talk/ep2008/pytest.txt Log: fix ReST Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 21:05:56 2008 @@ -135,23 +135,23 @@ hooks at module, class, instance level, example:: -import py, os.path + import py, os.path -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(__name__) + def setup_module(mod): + mod.tmpdir = py.test.ensuretemp(__name__) -class TestOS: - def setup_method(self, method): - self.tmpdir = tmpdir.mkdir(method.__name__) - print "chdir() to ", self.tmpdir - self.oldcwd = self.tmpdir.chdir() - def teardown_method(self, method): - self.oldcwd.chdir() - - def test_relative(self): - p = "a/b" - expected = os.path.join(str(self.tmpdir), p) - assert os.path.abspath(p) == expected + "xxx" + class TestOS: + def setup_method(self, method): + self.tmpdir = tmpdir.mkdir(method.__name__) + print "chdir() to ", self.tmpdir + self.oldcwd = self.tmpdir.chdir() + def teardown_method(self, method): + self.oldcwd.chdir() + + def test_relative(self): + p = "a/b" + expected = os.path.join(str(self.tmpdir), p) + assert os.path.abspath(p) == expected + "xxx" Skipping tests From cfbolz at codespeak.net Fri Jul 4 21:18:36 2008 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 4 Jul 2008 21:18:36 +0200 (CEST) Subject: [py-svn] r56313 - py/extradoc/talk/ep2008 Message-ID: <20080704191836.B898D698040@codespeak.net> Author: cfbolz Date: Fri Jul 4 21:18:34 2008 New Revision: 56313 Modified: py/extradoc/talk/ep2008/pytest.txt Log: typos Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 21:18:34 2008 @@ -275,7 +275,7 @@ - assertions by assert statement, nice debugging - unittests, generative tests, doctests, rest-tests -- automatic customaize collection of tests +- automatic customize collection of tests - select tests by keyword - capture stdout/stderr per-test @@ -294,7 +294,7 @@ ad-hoc distribution of tests (2) ===================================== -- synchronise source code "1 -> N" simultanously +- synchronise source code "1 -> N" simultaneously - uses py.execnet: From hpk at codespeak.net Fri Jul 4 22:45:58 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 22:45:58 +0200 (CEST) Subject: [py-svn] r56314 - py/extradoc/talk/ep2008 Message-ID: <20080704204558.DF2FA698086@codespeak.net> Author: hpk Date: Fri Jul 4 22:45:56 2008 New Revision: 56314 Added: py/extradoc/talk/ep2008/pytest.html py/extradoc/talk/ep2008/summary.html Log: adding some html Added: py/extradoc/talk/ep2008/pytest.html ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/pytest.html Fri Jul 4 22:45:56 2008 @@ -0,0 +1,754 @@ + + + + + + + +py.test: rapid testing with minimal effort + + + + + + + + + + + + + + + +
+
+
+ + +
+
+
+

py.test: rapid testing with minimal effort

+ +++ + + + + + +
Author:Holger Krekel, merlinux GmbH
event:7.7.2008, EuroPython 2008, Vilnius
+ + + + + + + + + +
+
+

What is py.test?

+
    +
  • cross-project testing tool
  • +
  • minimal boilerplate
  • +
  • many mature features
  • +
+
+
+

Structure of Tutorial

+
+
writing and running tests:
+
    +
  • test functions
  • +
  • test classes
  • +
  • generative tests
  • +
  • test driven development
  • +
  • setup and teardown test state
  • +
  • skipping tests
  • +
  • doctests
  • +
+
+
writing and using extensions:
+
    +
  • conftest.py files
  • +
  • test collection hooks
  • +
  • test running hooks
  • +
  • existing extensions
  • +
+
+
+
+
+

First: Install py.test

+
    +
  • install "py.test" (see http://pylib.org) +- via "easy_install py" +- via download and "python setup.py install" +- via svn and adding py/bin to system $PATH
  • +
+
+
+

Write and run a first test

+

let's write a "test_ospath1.py":

+
+import os.path, py
+
+def test_abspath():
+    assert os.path.abspath("/a/b/c") == "/a/b/c"
+    p = "a/b"
+    cur = os.path.join(os.getcwd(), "a/b")
+    assert os.path.abspath("a/b/c") == "/a/b/c"
+
+def test_typeerror():
+    assert py.test.raises(TypeError, "os.path.abspath()")
+    XXX check why "os.path.abspath(3)" cannot display source lines correctly
+
+

then issue on the command line:

+
+py.test test_ospath1.py
+
+
+
+

Putting tests in Test Classes

+

test_ospath2.py (one test failing):

+
+import os.path
+class TestOSPath:
+    def test_absolute(self):
+        assert os.path.abspath("/a/b/c") == "/a/b/c"
+    def test_relative(self):
+        p = "a/b"
+        cur = os.path.join(os.getcwd(), "a/b")
+        assert os.path.abspath("a/b/c") == cur
+
+
+
+

observations and notes

+
    +
  • py.test automatically collects test_* functions
  • +
  • use py.test --collectonly to inspect collection
  • +
  • you can write tests at module global level
  • +
  • no need to subclass from py.test
  • +
  • assertion failures provide detailed info
  • +
+
+
+

Generative / Parametrized tests

+

test_ospath3.py:

+
+class TestOSPath:
+    def test_gen_absolute(self):
+        def checkabssame(p1):
+            print "checking on", repr(p1)
+            assert os.path.abspath(p1) == p1
+        for s in "/ /a /a/b c".split():
+            yield checkabssame, s
+
+    def test_gen_relative(self):
+        for p in "a a/b".split():
+            p = "a/b"
+            expected = os.path.join(os.getcwd(), p)
+            yield lambda x,y: x==y, p, expected
+
+
+
+

Useful Features

+
    +
  • stdout/stderr is captured per-test
  • +
  • -l | --showlocals: show local variables in traceback
  • +
  • --pdb: jump into pdb for failures
  • +
  • -k KEYWORD: select tests to run
  • +
  • -x | --exitfirst: stop on first test failure
  • +
+
+
+

Test Driven Development (at its best)

+
    +
  • enter py.test --looponfailing: +- does a full test run +- keeps a set of failing tests +- checks for file changes and re-runs failing tests +- if all failures are fixed, re-run the whole
  • +
  • great for refactoring
  • +
+
+
+

Setup and Teardown of test state

+

hooks at module, class, instance level, example:

+
+import py, os.path
+
+def setup_module(mod):
+    mod.tmpdir = py.test.ensuretemp(__name__)
+
+class TestOS:
+    def setup_method(self, method):
+        self.tmpdir = tmpdir.mkdir(method.__name__)
+        print "chdir() to ", self.tmpdir
+        self.oldcwd = self.tmpdir.chdir()
+    def teardown_method(self, method):
+        self.oldcwd.chdir()
+
+    def test_relative(self):
+        p = "a/b"
+        expected = os.path.join(str(self.tmpdir), p)
+        assert os.path.abspath(p) == expected  + "xxx"
+
+
+
+

Skipping tests

+

the above tests do not make sense for win32, +you can insert e.g.:

+
+class TestOSPosix:
+    def setup_class(cls):
+        if sys.platform == "win32":
+            py.test.skip("posix platform required")
+
+

you can of course also place such skips inside test functions +or in other test setup functions.

+
+
+

Doctests

+

py.test automatically collects test_doc.txt files:

+
+this is an example doc test for the py.test tutorial
+
+>>> x = 3
+>>> print x
+3
+
+
+
+

Customizing py.test

+
    +
  • write "confest.py" files at project or global level

    +
  • +
  • conftest.py allow to: +- integrate new test "items" or "collectors" +- add command line options +- influence the collection process

    +
  • +
  • conftest.py's are picked up "upwards"

    +
  • +
  • +
    debug aid::
    +

    py.test --collectonly +py.test --traceconfig

    +
    +
    +
  • +
+
+
+

The collection process

+
    +
  • test collection starts from directories or files
  • +
  • collection process forms a tree: +- leaves: so called "items", e.g. py.test.collect.Function +- nodes: so called "collectors" contain further collectors or items
  • +
  • lookup of Collector/Item class from conftest.py files
  • +
+
+
+

Example conftest.py: add ReST check support

+

look into py/doc/conftest.py:

+
    +
  • this produces non-python test items
  • +
  • py.test --collectonly shows the custom tree
  • +
  • produces syntax and link checks
  • +
  • drop the conftest.py file into your own project/doc directory
  • +
+
+
+

Example conftest.py: html page generation

+

http://codespeak.net/svn/user/arigo/hack/misc/htmlconftest

+ +
+
+

Example conftest.py: run Prolog tests

+

http://codespeak.net/svn/user/cfbolz/jitpl/dist/test/conftest.py

+
    +
  • creates PrologTest items
  • +
  • collects items and runs them through Prolog interpreter
  • +
+
+
+

Example conftest.py: cross-platform testing

+
    +
  • goal: remotely run tests on windows
  • +
  • check out py/misc/conftest-socketgatewayrun.py
  • +
  • requires one small script on the windows side
  • +
  • transfers your source code to windows, runs tests, +reports on your terminal
  • +
+
+
+

Summary extensions

+ +

note, however: +- no easy customization of reporting (yet) +- some details will change for py lib 1.0 release

+

XXX refactor / do extra slides see what could stay / below

+
+
+

recap: main features

+
    +
  • assertions by assert statement, nice debugging
  • +
  • unittests, generative tests, doctests, rest-tests
  • +
  • automatic customize collection of tests
  • +
  • select tests by keyword
  • +
  • capture stdout/stderr per-test
  • +
+
+
+

ad-hoc distribution of tests

+

py.test --dist sends tests to remote places +or multiple processors

+
    +
  • for each host:
      +
    • setup (ssh) connection
    • +
    • sync local source code to remote place
    • +
    • trigger running of (isolated) tests
    • +
    +
  • +
+
+
+

ad-hoc distribution of tests (2)

+
    +
  • synchronise source code "1 -> N" simultaneously
  • +
  • uses py.execnet:
      +
    • can connect e.g. via SSH
    • +
    • expects plain Python executable on remote side
    • +
    • no need to pre-install other software remotely
    • +
    +
  • +
  • generated AJAX application async displays test results
  • +
  • demo
  • +
+
+
+

cross-platform testing

+
    +
  • test run on linux, test execution on windows
  • +
  • uses py.execnet (same technique as "--dist")
  • +
  • works well with "--looponfailing"
  • +
+
+
+

status cross-platform/distributed testing

+
    +
  • should basically work for any project
  • +
  • gives full tracebacks, most options work
  • +
+

todo:

+
    +
  • make introspection/pdb work
  • +
  • use screen for access to failed tests
  • +
  • accellerate py.execnet setup
  • +
  • more unification among testing modes
  • +
+
+
+

collect info about functions (apigen)

+
    +
  • collect function signatures (via settrace)
  • +
  • track input values/types, stacktraces, return values
  • +
  • web page with rich automatically produced information
  • +
  • TODO: decoupling of collecting info and generating html
  • +
+
+
+

doctests

+
    +
  • basically work (automatically collected from text files)
  • +
  • uses 2.5's doctest module
  • +
  • needs more fine-grained integration
  • +
+
+
+

Unifying Test Reporting

+

py.test contains two approaches for processing test results:

+
    +
  • "old style": methods invoked on session object
  • +
  • "new style": reporting events are sent to Reporter object
  • +
+

next:

+
    +
  • use reporting event architecture pervasively
  • +
  • refactor existing reporting extensions
  • +
+
+
+

py.test extensions (conftest)

+
    +
  • all conftest.py files are considered in traversed directories
  • +
  • can extend/modify testing process in arbitrary ways
  • +
  • however:
      +
    • often requires too much knowledge of internal architecture
    • +
    • no convention for organising shared test support code
    • +
    +
  • +
  • ergo: introduce mechanism to share test support code / plugins
  • +
+
+
+

Platform support

+
    +
  • py lib works on linux, freebsd, windows and OSX, ...
  • +
  • works on python 2.3, 2.4, 2.5
  • +
  • py lib provides doctest/optparse/... unified compat modules
  • +
  • todo: improve and automate packaging/installation
  • +
+
+
+

test networks

+
    +
  • currently model for 1:1 process-to-process
  • +
  • "A <-> B and B <-> C" connections do not imply "A <-> C"
  • +
  • fix that!
  • +
  • then: manage (large) network of test hosts
  • +
  • select set of test deployment hosts by platform, load
  • +
  • have modes for testing on several platforms at once
  • +
+
+
+

difference to "buildbot" ...

+
    +
  • py.test generally works from developers WC
  • +
  • no need to first commit and wait for later (nightly) test run
  • +
  • developer's choice of test setting
  • +
  • more interactivity
  • +
+
+
+

summary development topics

+
    +
  • refactor/unify reporting mechanisms
  • +
  • introduce shared test support code (plugins)
  • +
  • improve py.execnet (towards networks)
  • +
  • interactive debugging / introspection everywhere
  • +
  • systematically persist test results
  • +
+
+
+

Goal

+

Maximize fun and efficiency of (test-driven) development

+
+
+ + Added: py/extradoc/talk/ep2008/summary.html ============================================================================== --- (empty file) +++ py/extradoc/talk/ep2008/summary.html Fri Jul 4 22:45:56 2008 @@ -0,0 +1,258 @@ + +Summary of the failures in the latest 15 revisions + + +

Summary of the failures in the latest 15 revisions

+See also the in-progress/latest run. +Here is the latest logged stdout of the test runner. +
+You can start a full test run manually (unless one is +already running): +
+
+ 55940 (19 Jun)                                 45 failures, 1680 skips
+ | 55972 (20 Jun)                               54 failures, 1680 skips
+ | | 55999 (21 Jun)                             52 failures, 1680 skips
+ | | | 56011 (22 Jun)                           53 failures, 1680 skips
+ | | | | 56024 (23 Jun)                         45 failures, 1679 skips
+ | | | | | 56048 (25 Jun)                       47 failures, 1679 skips
+ | | | | | | 56058 (26 Jun)                     45 failures, 1679 skips
+ | | | | | | | 56102 (27 Jun)                   45 failures, 1679 skips
+ | | | | | | | | 56143 (28 Jun)                 54 failures, 1678 skips
+ | | | | | | | | | 56168 (29 Jun)               53 failures, 1678 skips
+ | | | | | | | | | | 56176 (30 Jun)             53 failures, 1678 skips
+ | | | | | | | | | | | 56194 (01 Jul)           45 failures, 1679 skips
+ | | | | | | | | | | | | 56215 (02 Jul)         44 failures, 1681 skips
+ | | | | | | | | | | | | | 56236 (03 Jul)       43 failures, 1681 skips
+ | | | | | | | | | | | | | | 56260 (04 Jul)    170 failures, 1678 skips
+ | | | | | | | | | | | | | | |
+
+ s s s s s s s s s s s s s s F  jit.codegen.cli.test.test_gencli_ts                TestTimeshiftClitest_convert_const_to_redbox
+ s s s s s s s s s s s s s s F  jit.codegen.cli.test.test_gencli_ts                TestTimeshiftClitest_convert_greenvar_to_redvar
+ s s s s s s s s s s s s s s F  jit.codegen.cli.test.test_gencli_ts                TestTimeshiftClitest_loop_folding
+ s s s s s s s s s s s s s s F  jit.codegen.cli.test.test_gencli_ts                TestTimeshiftClitest_simple_opt_const_propagation1
+ s s s s s s s s s s s s s s F  jit.codegen.cli.test.test_gencli_ts                TestTimeshiftClitest_simple_opt_const_propagation2
+ s s s s s s s s s s s s s s F  jit.codegen.cli.test.test_gencli_ts                TestTimeshiftClitest_very_simple
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_exception          TestExceptiontest_not_segregated_malloc_exception_path
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_portal             TestPortaltest_method_call_nonpromote
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_portal             TestPortaltest_method_call_promote
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_portal             TestPortaltest_virt_obj_method_call_promote
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_tl                 TestTLRtest_tl
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_tlc                TestTLCtest_factorial
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_tlc                TestTLCtest_nth_item
+ F F F F F F F F F F F F F F F  jit.codegen.i386.test.test_genc_tlr                TestTLRtest_tlr
+ . . . . . . . . . . . . . . F  jit.hintannotator.test.test_annotator              TestOOTypetest_raise_and_catch_exc
+ . . . . . . . . . . . . . . F  jit.hintannotator.test.test_annotator              TestOOTypetest_raise_exc
+ . . . . . . . . . . . . . . F  jit.hintannotator.test.test_annotator              TestOOTypetest_simple_meth
+ F F F F F F F F F F F F F F F  jit.hintannotator.test.test_toy                    TestLLTypetest_hannotate_tl
+ F F F F F F F F F F F F F F F  jit.hintannotator.test.test_toy                    TestLLTypetest_hannotate_tl_novirtual
+ F F F F F F F F F F F F F F F  jit.hintannotator.test.test_toy                    TestLLTypetest_hannotate_tlc_novirtual
+ . . . . . . . . . . . . . . F  jit.hintannotator.test.test_toy                    TestOOTypetest_hannotate_tl_novirtual
+ . . . . . . . . . . . . . . F  jit.hintannotator.test.test_toy                    TestOOTypetest_hannotate_tlc_novirtual
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_0tlc                     TestTLCtest_factorial
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_0tlc                     TestTLCtest_nth_item
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_1tl                      TestTLtest_tl
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_exception                TestExceptiontest_not_segregated_malloc_exception_path
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_portal                   TestPortaltest_method_call_nonpromote
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_portal                   TestPortaltest_method_call_promote
+ F F F F F F F F F F F F F F F  jit.timeshifter.test.test_portal                   TestPortaltest_virt_obj_method_call_promote
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_arith_plus_minus
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_convert_const_to_redbox
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_convert_greenvar_to_redvar
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_green_across_split
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_loop_folding
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_loop_merging
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_merge_3_redconsts_before_return
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_merge_const_before_return
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_simple_opt_const_propagation1
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_simple_opt_const_propagation2
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_two_loops_merging
+ . . . . . . . . . . . . . . F  jit.timeshifter.test.test_timeshift                TestOOTypetest_very_simple
+ F F F F F F F F F F F F F F    lang.gameboy.test.test_cpu                         
+ F F F F F F F F F F F F F F    lang.gameboy.test.test_cpu_2                       
+ F F F F F F F F F F F F F F    lang.gameboy.test.test_gameboy                     
+ F F F F F F F F F F F F F F F  lang.gameboy.test.test_gameboy_implementaton       
+ F F F F F F F F F F F F F F    lang.gameboy.test.test_register                    
+ F F F F F F F F F F F F F F    lang.gameboy.test.test_rom                         
+ F F F F F F F F F F F F F F    lang.gameboy.test.test_video                       
+ F F F F F F F F F F F F F F F  lang.test.test_translation                         TestTranslationtest_js
+ F F F F F F F F F F F F F F F  lib.app_test.html                                  
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_backto_main
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_bogus_bind
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_is_zombie
+ s s s s s s s s F F F s s s s  lib.app_test.test_coroutine                        Test_Coroutinetest_is_zombie_del
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_kill
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_raise_propagate
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_simple_task
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_strange_test
+ . . . . . . . . F F F . . . .  lib.app_test.test_coroutine                        Test_Coroutinetest_wrapped_main
+                 F F F F F F    lib.app_test.test_functools                        
+ F F F F F F F F F F F F F F F  module._lsprof.test.test_cprofile                  AppTestCProfiletest_cprofile
+ . . . . . . . . . F F F . . .  module.sys.test.test_sysmodule                     AppTestSysModulePortedFromCPythontest_setcheckinterval
+ F F F F F F F F F . . . F . .  module.thread.test.test_ll_thread                  TestUsingFrameworktest_gc_locking
+ F F . . . . . . . . . . . . .  module.thread.test.test_local                      AppTestLocaltest_local_1
+ F F F F F F F F                module.zipimport.test.test_undocumented            
+ F F F F F F F F F F F F F F F  objspace.std.test.test_ropeunicodeobject           AppTestRopeObjecttest_decode
+ . F F F . . . . . . . . . . .  rpython.memory.test.test_gc                        TestGenerationalGCtest_finalizer_order
+ . F F F . . . . . . . . . . .  rpython.memory.test.test_gc                        TestGrowingSemiSpaceGCtest_finalizer_order
+ . F F F . . . . . . . . . . .  rpython.memory.test.test_gc                        TestHybridGCtest_finalizer_order
+ . F F F . . . . . . . . . . .  rpython.memory.test.test_gc                        TestSemiSpaceGCtest_finalizer_order
+ . . . . . . . . . . . . . . F  translator.backendopt.test.test_removeassert       test_and
+ . . . . . . . . . . . . . . F  translator.backendopt.test.test_removeassert       test_isinstance
+ . . . . . . . . . . . . . . F  translator.backendopt.test.test_removeassert       test_or
+ . . . . . . . . . . . . . . F  translator.backendopt.test.test_removeassert       test_simple
+ . . . . . . . . . . . . . . F  translator.backendopt.test.test_removeassert       test_simple_melting_away
+ . . . . . . . . . . . . . . F  translator.backendopt.test.test_removeassert       test_with_exception
+ . F F F . . . . . . . . . . .  translator.c.gcc.test.test_asmgcroot               TestAsmGCRootWithSemiSpaceGCtest_finalizer_order
+ . F F F . . . . . . . . . . .  translator.c.test.test_newgc                       TestGenerationalGCtest_finalizer_order
+ . F F F . . . . . . . . . . .  translator.c.test.test_newgc                       TestHybridGCtest_finalizer_order
+ . F F F . . . . . . . . . . .  translator.c.test.test_newgc                       TestSemiSpaceGCtest_finalizer_order
+ . F . F . F . . F . . . . . F  translator.cli.test.test_carbonpython              TestCarbonPythontest_export_cliclass
+ F F F F F F F F F F F F F F F  translator.cli.test.test_string                    TestCliStringtest_rfind_empty_string
+ F F F F F F F F F F F F F F F  translator.cli.test.test_unicode                   TestCliUnicodetest_prebuilt_ll_strings
+ F F F F F F F F F F F F F F F  translator.cli.test.test_unicode                   TestCliUnicodetest_rfind_empty_string
+ . . . . . . . . . . . . . . F  translator.js.examples.djangoping.test.test_build  test_build
+ . . . . . . . . . . . . . . F  translator.js.examples.test.test_examples          test_bnb_build
+ . . . . . . . . . . . . . . F  translator.js.examples.test.test_examples          test_console_2_build
+ . . . . . . . . . . . . . . F  translator.js.examples.test.test_examples          test_console_build
+ . . . . . . . . . . . . . . F  translator.js.examples.test.test_examples          test_guestbook_build
+ . . . . . F F F . . . . . . .  translator.js.examples.test.test_examples          test_ping_play1
+ . . . . . . . . . . . . . . F  translator.js.test.test_basicexternal              test_basicexternal_raise_method_call
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_always_raise
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_catch_base_exception
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_catches
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_pass_exc
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_raise_outside_testfn
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_reraise1
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_reraise2
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_simple1
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_simple2
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_simple3
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_simple_exception
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_try_raise_choose
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_two_exceptionsA
+ . . . . . . . . . . . . . . F  translator.js.test.test_exception                  test_two_exceptionsB
+ . . . . . . . . . . . . . . F  translator.js.test.test_main                       test_bookkeeper_cleanup
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsClasstest___class___attribute
+ F F F F F F F F F F F F F F F  translator.js.test.test_rclass                     TestJsClasstest_circular_hash_initialization
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsClasstest_runtime_exception
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsClasstest_type
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsExceptiontest_catch_KeyboardInterrupt
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsExceptiontest_catch_incompatible_class
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsExceptiontest_exception_with_arg
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsExceptiontest_raise_and_catch_other
+ . . . . . . . . . . . . . . F  translator.js.test.test_rclass                     TestJsExceptiontest_raise_prebuilt_and_catch_other
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_access_in_try
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_access_in_try_set
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_clear
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_creation
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_del_simple
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_getitem_keyerror
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_getitem_setitem
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_inst_keys
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_iteration
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_keys
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_of_dict
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_or_none
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_dict_setdefault
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_empty_strings
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_id_instances_keys
+ . . . . . . . . . . . . . . F  translator.js.test.test_rdict                      TestJsDicttest_two_dicts_with_different_value_types
+ F F F F F F F F F F F F F F F  translator.js.test.test_rfloat                     TestJsFloattest_float2str
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_access_in_try_set
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_add
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_bound_list_method
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_charlist_extension_1
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_charlist_extension_2
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_charlist_extension_3
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_charlist_extension_4
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_charlist_extension_5
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_charlist_extension_6
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_getitem_exc
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_indexerror
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_inst_list
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_inst_pop
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_iterate
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_iterate_next
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_basic_ops
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_compare
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_compareinst
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_contains
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_equality
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_index
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_index_simple
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_inplace_multiply
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_multiply
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_or_None
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_list_remove
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_unicharlist_extension_1
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_unicharlist_extension_2
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_unicharlist_extension_3
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_unicharlist_extension_4
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_unicharlist_extension_5
+ . . . . . . . . . . . . . . F  translator.js.test.test_rlist                      TestJsListtest_valueerror
+ . . . . . . . . . . . . . . F  translator.js.test.test_rpbc                       TestJsPBCtest_always_raising_methods
+ . . . . . . . . . . . . . . F  translator.js.test.test_rpbc                       TestJsPBCtest_class_method_inherited
+ . . . . . . . . . . . . . . F  translator.js.test.test_rpbc                       TestJsPBCtest_multiple_ll_one_hl_op
+ . . . . . . . . . . . . . . F  translator.js.test.test_rpbc                       TestJsPBCtest_various_patterns_but_one_signature_method
+ . . . . . . . . . . . . . . F  translator.js.test.test_rsnippet                   test_closure
+ . . . . . . . . . . . . . . F  translator.js.test.test_rsnippet                   test_dict_creation
+ . . . . . . . . . . . . . . F  translator.js.test.test_rsnippet                   test_invoke_function_pointer
+ . . . . . . . . . . . . . . F  translator.js.test.test_rsnippet                   test_list_basic_ops
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        TestDicttest_const_dict
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        TestDicttest_dict_iter
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        TestDicttest_dict_iterator
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        TestDicttest_sophisticated_const
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        TestListtest_slice
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        TestListtest_var_list
+ . . . . . . . . . . . . . . F  translator.js.test.test_seq                        test_simple_seq
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_char_mul
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_getitem_exc
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_implicit_index_error
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_inplace_add
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_int
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_int_valueerror
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_iter
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_join
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_percentformat_list
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_replace
+ . . . . . . . . . . . . . . F  translator.js.test.test_str                        TestJsStringtest_split
+ . . . . . . . . . . . . . . F  translator.js.test.test_typed                      test_call_five
+ F F F F F F F F F F F F F F F  translator.jvm.test.test_string                    TestJvmStringtest_rfind_empty_string
+ F F F F F F F F F F F F F F F  translator.jvm.test.test_unicode                   TestJvmUnicodetest_prebuilt_ll_strings
+ F F F F F F F F F F F F F F F  translator.jvm.test.test_unicode                   TestJvmUnicodetest_rfind_empty_string
+ . . . . F F . . . . . F . . .  translator.jvm.test.test_weakref                   TestJvmExceptiontest_weakref_simple
+ F F F F F F F F F F F F F F F  translator.llvm.test.test_standalone               test__del__
+ F F F F F F F F F F F F F F F  translator.llvm.test.test_standalone               test_exception_leaking
+ F F F F F F F F F F F F F F F  translator.llvm.test.test_standalone               test_hello_world
+ F F F F F F F F F F F F F F F  translator.llvm.test.test_standalone               test_print
+ F F F F F F F F F F F F F F F  translator.llvm.test.test_standalone               test_strtod
+
+ | | | | | | | | | | | | | | |
+ | | | | | | | | | | | | | | 56260 (04 Jul)    170 failures, 1678 skips
+ | | | | | | | | | | | | | 56236 (03 Jul)       43 failures, 1681 skips
+ | | | | | | | | | | | | 56215 (02 Jul)         44 failures, 1681 skips
+ | | | | | | | | | | | 56194 (01 Jul)           45 failures, 1679 skips
+ | | | | | | | | | | 56176 (30 Jun)             53 failures, 1678 skips
+ | | | | | | | | | 56168 (29 Jun)               53 failures, 1678 skips
+ | | | | | | | | 56143 (28 Jun)                 54 failures, 1678 skips
+ | | | | | | | 56102 (27 Jun)                   45 failures, 1679 skips
+ | | | | | | 56058 (26 Jun)                     45 failures, 1679 skips
+ | | | | | 56048 (25 Jun)                       47 failures, 1679 skips
+ | | | | 56024 (23 Jun)                         45 failures, 1679 skips
+ | | | 56011 (22 Jun)                           53 failures, 1680 skips
+ | | 55999 (21 Jun)                             52 failures, 1680 skips
+ | 55972 (20 Jun)                               54 failures, 1680 skips
+ 55940 (19 Jun)                                 45 failures, 1680 skips
+
+

200 tests listed.

+ From hpk at codespeak.net Fri Jul 4 22:47:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 22:47:35 +0200 (CEST) Subject: [py-svn] r56315 - py/extradoc/talk/ep2008 Message-ID: <20080704204735.8DF1449800E@codespeak.net> Author: hpk Date: Fri Jul 4 22:47:35 2008 New Revision: 56315 Modified: py/extradoc/talk/ep2008/pytest.html (props changed) py/extradoc/talk/ep2008/summary.html (props changed) Log: set mime type From hpk at codespeak.net Fri Jul 4 22:49:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 22:49:03 +0200 (CEST) Subject: [py-svn] r56316 - py/extradoc/talk/ep2008 Message-ID: <20080704204903.EA3C749800E@codespeak.net> Author: hpk Date: Fri Jul 4 22:49:02 2008 New Revision: 56316 Removed: py/extradoc/talk/ep2008/ui Log: remove ui here Deleted: /py/extradoc/talk/ep2008/ui ============================================================================== --- /py/extradoc/talk/ep2008/ui Fri Jul 4 22:49:02 2008 +++ (empty file) @@ -1 +0,0 @@ -link ../ui \ No newline at end of file From hpk at codespeak.net Fri Jul 4 22:51:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 22:51:30 +0200 (CEST) Subject: [py-svn] r56317 - py/extradoc/talk/ep2008/ui Message-ID: <20080704205130.5F334698086@codespeak.net> Author: hpk Date: Fri Jul 4 22:51:29 2008 New Revision: 56317 Added: py/extradoc/talk/ep2008/ui/ - copied from r56316, pypy/extradoc/talk/ep2007/ui/ Log: adding it fresh From hpk at codespeak.net Fri Jul 4 22:57:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 4 Jul 2008 22:57:40 +0200 (CEST) Subject: [py-svn] r56319 - py/extradoc/talk/ep2008 Message-ID: <20080704205740.65CC52A8002@codespeak.net> Author: hpk Date: Fri Jul 4 22:57:40 2008 New Revision: 56319 Modified: py/extradoc/talk/ep2008/pytest.html py/extradoc/talk/ep2008/pytest.txt Log: add conftest info about running js tests and traditional unit tests Modified: py/extradoc/talk/ep2008/pytest.html ============================================================================== --- py/extradoc/talk/ep2008/pytest.html (original) +++ py/extradoc/talk/ep2008/pytest.html Fri Jul 4 22:57:40 2008 @@ -352,8 +352,10 @@

What is py.test?

@@ -581,6 +583,11 @@
  • collects items and runs them through Prolog interpreter
  • +
    +

    Example conftest.py: run javascript tests (in a browser)

    +

    http://johnnydebris.net/svn/projects/jsbase

    +

    XXX explore and summarize

    +

    Example conftest.py: cross-platform testing

    +
    +

    Example conftest.py: integrate traditional unittest's

    +

    http://johnnydebris.net/svn/projects/py_unittest

    +

    contains a conftest that scans all found files for +subclasses of unittest.TestCase and executes them

    +

    XXX explore and summarize

    +

    Summary extensions

    -

    Test Driven Development (at its best)

    -

    Setup and Teardown of test state

    @@ -510,7 +518,7 @@ if sys.platform == "win32": py.test.skip("posix platform required") -

    you can of course also place such skips inside test functions +

    you can also place such calls to "py.test.skip()" inside test functions or in other test setup functions.

    @@ -526,34 +534,50 @@

    Customizing py.test

    +

    write "confest.py" files at project or global level:

    +
    +

    Important Internal Objects

    +

    Session: iterates over (custom) collection tree, executes +tests and reports outcomes.

    +

    Collection Nodes ("py.test.collect.*"): +XXX insert links?

    +

    Directory: collect files

    +

    Module: collect test Classes and Functions

    +

    Class/Instance: collect test methods

    +

    DoctestFile: collect doctest of a .txt file

    +

    Generator: collect generative tests

    +

    *Function*: sets up and executes a python test function

    +

    The collection process

    -

    Example conftest.py: add ReST check support

    @@ -613,9 +637,11 @@
  • if you use the above conftest.py's or have some of your own please subscribe http://codespeak.net/mailman/listinfo/py-dev
  • -

    note, however: -- no easy customization of reporting (yet) -- some details will change for py lib 1.0 release

    +

    note, however:

    +

    XXX refactor / do extra slides see what could stay / below

    Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Fri Jul 4 23:28:24 2008 @@ -10,13 +10,13 @@ What is py.test? =================== -- cross-project **external** testing tool -- automatically collect and execute tests -- minimal boilerplate -- easy per-project customization +- **external** testing tool +- automatically collects and executes tests +- **minimal boilerplate** approach +- **per-project customization** - many mature features -Structure of Tutorial +Structure of Tutorial (I) ========================== writing and running tests: @@ -28,17 +28,19 @@ - skipping tests - doctests +Structure of Tutorial (II) +========================== + writing and using extensions: - - conftest.py files + - conftest.py mechanism - test collection hooks - test running hooks - existing extensions - First: Install py.test ============================ -- install "py.test" (see http://pylib.org) +install "py.test" (see http://pylib.org): - via "easy_install py" - via download and "python setup.py install" - via svn and adding py/bin to system $PATH @@ -61,9 +63,7 @@ assert py.test.raises(TypeError, "os.path.abspath()") XXX check why "os.path.abspath(3)" cannot display source lines correctly -then issue on the command line:: - - py.test test_ospath1.py +then issue on the command line: ``py.test test_ospath1.py`` Putting tests in Test Classes @@ -120,16 +120,16 @@ - ``-x | --exitfirst``: stop on first test failure -Test Driven Development (at its best) +Test Driven Development - at its best! ======================================= -- enter ``py.test --looponfailing``: +meet ``py.test --looponfailing``: - does a full test run - keeps a set of failing tests - checks for file changes and re-runs failing tests - if all failures are fixed, re-run the whole -- great for refactoring +great for refactoring! Setup and Teardown of test state @@ -167,7 +167,7 @@ if sys.platform == "win32": py.test.skip("posix platform required") -you can of course also place such skips inside test functions +you can also place such calls to "py.test.skip()" inside test functions or in other test setup functions. Doctests @@ -185,26 +185,50 @@ Customizing py.test =========================== -- write "confest.py" files at project or global level -- conftest.py allow to: - - integrate new test "items" or "collectors" - - add command line options - - influence the collection process +write "confest.py" files at project or global level: + +- integrate new test "items" or "collectors" +- add command line options +- influence the collection process - conftest.py's are picked up "upwards" -- debug aid:: +- to aid debugging use:: + py.test --collectonly py.test --traceconfig +Important Internal Objects +================================== + +**Session**: iterates over (custom) collection tree, executes +tests and reports outcomes. + +Collection Nodes ("py.test.collect.*"): +XXX insert links? + +**Directory**: collect files + +**Module**: collect test Classes and Functions + +**Class**/**Instance**: collect test methods + +**DoctestFile**: collect doctest of a .txt file + +**Generator**: collect generative tests + +***Function***: sets up and executes a python test function + The collection process =========================== -- test collection starts from directories or files -- collection process forms a tree: +collection process forms a tree: - leaves: so called "items", e.g. ``py.test.collect.Function`` - nodes: so called "collectors" contain further collectors or items -- lookup of Collector/Item class from conftest.py files + - collection tree is built iterativels +test collection usually starts from directories or files + +**dynamic lookup of Collector/Item class from conftest.py files** Example conftest.py: add ReST check support ============================================== @@ -275,6 +299,7 @@ please subscribe http://codespeak.net/mailman/listinfo/py-dev note, however: + - no easy customization of reporting (yet) - some details will change for py lib 1.0 release From hpk at codespeak.net Sat Jul 5 08:30:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 5 Jul 2008 08:30:21 +0200 (CEST) Subject: [py-svn] r56323 - py/extradoc/talk/ep2008 Message-ID: <20080705063021.8178616A0A0@codespeak.net> Author: hpk Date: Sat Jul 5 08:30:19 2008 New Revision: 56323 Modified: py/extradoc/talk/ep2008/pytest.html py/extradoc/talk/ep2008/pytest.txt Log: add some info for the js/py_unit conftests, and another conftest Modified: py/extradoc/talk/ep2008/pytest.html ============================================================================== --- py/extradoc/talk/ep2008/pytest.html (original) +++ py/extradoc/talk/ep2008/pytest.html Sat Jul 5 08:30:19 2008 @@ -556,8 +556,7 @@

    Important Internal Objects

    Session: iterates over (custom) collection tree, executes tests and reports outcomes.

    -

    Collection Nodes ("py.test.collect.*"): -XXX insert links?

    +

    Collection Nodes ("py.test.collect.*"):

    Directory: collect files

    Module: collect test Classes and Functions

    Class/Instance: collect test methods

    @@ -579,6 +578,19 @@

    test collection usually starts from directories or files

    dynamic lookup of Collector/Item class from conftest.py files

    +
    +

    Example conftest.py: control traversal

    +

    a conftest.py to influence collection:

    +
    +import py
    +mydir = py.magic.autopath().dirpath()
    +class Directory(py.test.collect.Directory):
    +    def run(self):
    +        if self.fspath == mydir:
    +            return ["x", "y", "z"]
    +        return super(Directory, self).run()
    +
    +

    Example conftest.py: add ReST check support

    look into py/doc/conftest.py:

    @@ -591,7 +603,8 @@

    Example conftest.py: html page generation

    -

    http://codespeak.net/svn/user/arigo/hack/misc/htmlconftest

    +

    http://codespeak.net/svn/user/arigo/hack/misc/htmlconftest +[courtesy Armin Rigo]

    Example conftest.py: run Prolog tests

    -

    http://codespeak.net/svn/user/cfbolz/jitpl/dist/test/conftest.py

    +

    http://codespeak.net/svn/user/cfbolz/jitpl/dist/test/conftest.py +[courtesy Armin Rigo, Carl Friedrich Bolz]

    -
    -

    Example conftest.py: run javascript tests (in a browser)

    -

    http://johnnydebris.net/svn/projects/jsbase

    -

    XXX explore and summarize

    +
    +

    Example conftest.py: run Javascript tests

    +

    http://johnnydebris.net/svn/projects/jsbase +[courtesy Guido Wesdorp]

    +
      +
    • collects Javascript tests
    • +
    • sends them for execution to Spidermonkey JS interpreter
    • +

    Example conftest.py: cross-platform testing

    +

    [courtesy Holger Krekel]

    • goal: remotely run tests on windows
    • check out py/misc/conftest-socketgatewayrun.py
    • -
    • requires one small script on the windows side
    • +
    • requires a small server script on the windows side
    • transfers your source code to windows, runs tests, reports on your terminal

    Example conftest.py: integrate traditional unittest's

    -

    http://johnnydebris.net/svn/projects/py_unittest

    -

    contains a conftest that scans all found files for -subclasses of unittest.TestCase and executes them

    -

    XXX explore and summarize

    +

    http://johnnydebris.net/svn/projects/py_unittest +[courtesy Guido Wesdorp]

    +
      +
    • additionally collect TestCase subclasses from +all test_*.py Module and execute them
    • +
    • allows to mix xUnit and py style tests
    • +

    Summary extensions

      -
    • conftest.py's can be re-used per-project
    • -
    • can add new (non-python) test items
    • +
    • conftest.py files are supposed to be "drop in" plugins
    • +
    • you can extend and modify the collection process
    • +
    • you can add new (non-python) test items
    • if you use the above conftest.py's or have some of your own please subscribe http://codespeak.net/mailman/listinfo/py-dev
    Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Sat Jul 5 08:30:19 2008 @@ -204,7 +204,6 @@ tests and reports outcomes. Collection Nodes ("py.test.collect.*"): -XXX insert links? **Directory**: collect files @@ -230,6 +229,20 @@ **dynamic lookup of Collector/Item class from conftest.py files** +Example conftest.py: control traversal +=================================================== + +a conftest.py to influence collection:: + + import py + mydir = py.magic.autopath().dirpath() + class Directory(py.test.collect.Directory): + def run(self): + if self.fspath == mydir: + return ["x", "y", "z"] + return super(Directory, self).run() + + Example conftest.py: add ReST check support ============================================== @@ -244,6 +257,7 @@ =========================================== http://codespeak.net/svn/user/arigo/hack/misc/htmlconftest +[courtesy Armin Rigo] - creates html output with "clickable" failures - creates a nice historic view on test failures @@ -255,25 +269,30 @@ ====================================== http://codespeak.net/svn/user/cfbolz/jitpl/dist/test/conftest.py +[courtesy Armin Rigo, Carl Friedrich Bolz] - creates PrologTest items - collects items and runs them through Prolog interpreter -Example conftest.py: run javascript tests (in a browser) +Example conftest.py: run Javascript tests ========================================================= http://johnnydebris.net/svn/projects/jsbase +[courtesy Guido Wesdorp] -XXX explore and summarize +- collects Javascript tests +- sends them for execution to Spidermonkey JS interpreter Example conftest.py: cross-platform testing ============================================= +[courtesy Holger Krekel] + - goal: remotely run tests on windows - check out py/misc/conftest-socketgatewayrun.py -- requires one small script on the windows side +- requires a small server script on the windows side - transfers your source code to windows, runs tests, reports on your terminal @@ -282,19 +301,20 @@ ========================================================= http://johnnydebris.net/svn/projects/py_unittest +[courtesy Guido Wesdorp] -contains a conftest that scans all found files for -subclasses of unittest.TestCase and executes them - -XXX explore and summarize +- additionally collect TestCase subclasses from + all test_*.py Module and execute them +- allows to mix xUnit and py style tests Summary extensions ======================== -- conftest.py's can be re-used per-project -- can add new (non-python) test items +- conftest.py files are supposed to be "drop in" plugins +- you can extend and modify the collection process +- you can add new (non-python) test items - if you use the above conftest.py's or have some of your own please subscribe http://codespeak.net/mailman/listinfo/py-dev From hpk at codespeak.net Sat Jul 5 17:07:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 5 Jul 2008 17:07:37 +0200 (CEST) Subject: [py-svn] r56328 - py/extradoc/talk/ep2008 Message-ID: <20080705150737.CBC7739B594@codespeak.net> Author: hpk Date: Sat Jul 5 17:07:34 2008 New Revision: 56328 Modified: py/extradoc/talk/ep2008/pytest.txt Log: mention script to convert xUnit style to py style tests Modified: py/extradoc/talk/ep2008/pytest.txt ============================================================================== --- py/extradoc/talk/ep2008/pytest.txt (original) +++ py/extradoc/talk/ep2008/pytest.txt Sat Jul 5 17:07:34 2008 @@ -308,20 +308,23 @@ - allows to mix xUnit and py style tests +- or: alternatively use ``py/tool/utestconvert.py`` to permanently + convert xUnit to py style tests Summary extensions ======================== -- conftest.py files are supposed to be "drop in" plugins - you can extend and modify the collection process - you can add new (non-python) test items -- if you use the above conftest.py's or have some of your own - please subscribe http://codespeak.net/mailman/listinfo/py-dev +- conftest.py files are usually "drop in" plugins note, however: - no easy customization of reporting (yet) -- some details will change for py lib 1.0 release +- details will change for py lib 1.0 release, thus + if you use special conftest's be sure to subscribe + http://codespeak.net/mailman/listinfo/py-dev + XXX refactor / do extra slides see what could stay / below From hpk at codespeak.net Sun Jul 13 10:28:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 13 Jul 2008 10:28:00 +0200 (CEST) Subject: [py-svn] r56509 - py/trunk/py/misc Message-ID: <20080713082800.24942169F7B@codespeak.net> Author: hpk Date: Sun Jul 13 10:27:58 2008 New Revision: 56509 Added: py/trunk/py/misc/_maketest2.py (contents, props changed) Log: small helper script for creating py/test2 hierarchy Added: py/trunk/py/misc/_maketest2.py ============================================================================== --- (empty file) +++ py/trunk/py/misc/_maketest2.py Sun Jul 13 10:27:58 2008 @@ -0,0 +1,53 @@ +""" create a py/test2 hierarchy copied from py/test. + useful for refactoring py.test itself and still + use py.test itself. +""" + +from _findpy import py + +def change_init(initfile): + l = [] + for line in initfile.readlines(): + newline = line + l.append(line) + newline = newline.replace("'test.", "'test2.") + newline = newline.replace("'./test/", "'./test2/") + if newline != line: + l.append(newline) + initfile.write("".join(l)) + +def perform_replace(directory): + for x in directory.visit("*.py", + rec=lambda x: x.check(dir=1, dotfile=0)): + s = n = x.read() + n = n.replace("py.test", "py.test2") + n = n.replace("py.__.test.", "py.__.test2.") + n = n.replace("py.__.test ", "py.__.test2 ") + if s != n: + print "writing modified", x + x.write(n) + +def cmd(command): + print "* executing:", command + return py.process.cmdexec(command) + +if __name__ == '__main__': + basedir = py.path.local(py.__file__).dirpath() + #st = py.path.svnwc(basedir).status() + #assert not st.modified + olddir = basedir.chdir() + try: + initfile = basedir.join("__init__.py") + cmd("svn revert %s" % initfile) + change_init(initfile) + + test2dir = basedir.join("test2") + cmd("svn revert -R test2") + cmd("rm -rf test2") + cmd("svn cp test test2") + perform_replace(test2dir) + + finally: + olddir.chdir() + + From hpk at codespeak.net Sun Jul 13 15:36:53 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 13 Jul 2008 15:36:53 +0200 (CEST) Subject: [py-svn] r56510 - py/trunk/py/path/svn/testing Message-ID: <20080713133653.461D3498019@codespeak.net> Author: hpk Date: Sun Jul 13 15:36:50 2008 New Revision: 56510 Modified: py/trunk/py/path/svn/testing/svntestbase.py Log: svn 1.3 is in common use these days and seems to pass the test. Modified: py/trunk/py/path/svn/testing/svntestbase.py ============================================================================== --- py/trunk/py/path/svn/testing/svntestbase.py (original) +++ py/trunk/py/path/svn/testing/svntestbase.py Sun Jul 13 15:36:50 2008 @@ -99,7 +99,6 @@ assert res.size > len("samplefile") and res.created_rev >= 0 def test_log_simple(self): - py.test.skip("XXX: does not work at least on svn below 1.3") url = self.root.join("samplefile") logentries = url.log() for logentry in logentries: From hpk at codespeak.net Sun Jul 13 16:02:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 13 Jul 2008 16:02:27 +0200 (CEST) Subject: [py-svn] r56512 - py/trunk/py/path/svn Message-ID: <20080713140227.78DD3169EED@codespeak.net> Author: hpk Date: Sun Jul 13 16:02:26 2008 New Revision: 56512 Modified: py/trunk/py/path/svn/urlcommand.py Log: avoid infinite recursion issue with svn 1.5, thanks chris lamb Modified: py/trunk/py/path/svn/urlcommand.py ============================================================================== --- py/trunk/py/path/svn/urlcommand.py (original) +++ py/trunk/py/path/svn/urlcommand.py Sun Jul 13 16:02:26 2008 @@ -239,7 +239,9 @@ for lsline in lines: if lsline: info = InfoSvnCommand(lsline) - nameinfo_seq.append((info._name, info)) + if info._name != '.': # svn 1.5 produces '.' dirs, + nameinfo_seq.append((info._name, info)) + return nameinfo_seq auth = self.auth and self.auth.makecmdoptions() or None if self.rev is not None: From hpk at codespeak.net Sun Jul 13 16:09:38 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 13 Jul 2008 16:09:38 +0200 (CEST) Subject: [py-svn] r56513 - py/trunk/py/path/svn Message-ID: <20080713140938.CF65A169EFB@codespeak.net> Author: hpk Date: Sun Jul 13 16:09:38 2008 New Revision: 56513 Modified: py/trunk/py/path/svn/urlcommand.py Log: extend doc string Modified: py/trunk/py/path/svn/urlcommand.py ============================================================================== --- py/trunk/py/path/svn/urlcommand.py (original) +++ py/trunk/py/path/svn/urlcommand.py Sun Jul 13 16:09:38 2008 @@ -1,7 +1,8 @@ """ module defining a subversion path object based on the external -command 'svn'. +command 'svn'. This modules aims to work with svn 1.3 and higher +but might also interact well with earlier versions. """ From hpk at codespeak.net Sun Jul 13 17:28:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 13 Jul 2008 17:28:11 +0200 (CEST) Subject: [py-svn] r56516 - py/branch/event/py/test2/testing Message-ID: <20080713152811.5C41B2A014A@codespeak.net> Author: hpk Date: Sun Jul 13 17:28:10 2008 New Revision: 56516 Modified: py/branch/event/py/test2/testing/test_remote.py Log: a remark about how "remote" tests here need to be done Modified: py/branch/event/py/test2/testing/test_remote.py ============================================================================== --- py/branch/event/py/test2/testing/test_remote.py (original) +++ py/branch/event/py/test2/testing/test_remote.py Sun Jul 13 17:28:10 2008 @@ -3,7 +3,11 @@ def setup_module(mod): mod.tmpdir = py.test.ensuretemp(mod.__name__) -class TestRemote: +class Test_Remote_Functional: + # XXX for testing features that implicitely start new + # we want to get access to the remote event log + # and be able to access and assert about it it from the + # tests below def test_exec(self): o = tmpdir.ensure('remote', dir=1) tfile = o.join('test_exec.py') @@ -16,15 +20,12 @@ ['--exec=' + py.std.sys.executable, o]) session = config.initsession() - #allevents = getevents_runmain(session) - #print allevents - #failures = [x for x in allevents - # if isinstance(x, repevent.ItemFinish)] failures = session.main() assert failures + py.test.skip("extend this test to test that --exec really honores its argument") def test_looponfailing(self): - py.test.skip("fix output checking tests to check for events") + py.test.skip("xxx check events") o = tmpdir.ensure('looponfailing', dir=1) tfile = o.join('test_looponfailing.py') tfile.write(py.code.Source(""" From hpk at codespeak.net Wed Jul 16 16:23:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 16 Jul 2008 16:23:26 +0200 (CEST) Subject: [py-svn] r56593 - py/branch/event/py/test2 Message-ID: <20080716142326.BF1E82A018E@codespeak.net> Author: hpk Date: Wed Jul 16 16:23:25 2008 New Revision: 56593 Modified: py/branch/event/py/test2/session.py Log: rename setup/teardown for Session objects to avoid confusion with Item/Collector setup/teardown's Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Jul 16 16:23:25 2008 @@ -41,17 +41,19 @@ for x in genitems(self.config, colitems, keyword): yield x - def setup(self): + def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ + self.config.bus.notify(repevent.SessionStart(self)) if not self.config.option.nomagic: py.magic.invoke(assertion=1) self._failurelist = self._initfailurelist() - def teardown(self): + def sessionfinishes(self): """ teardown any resources after a test run. """ self.config._setupstate.teardown_all() if not self.config.option.nomagic: py.magic.revoke(assertion=1) + self.config.bus.notify(repevent.SessionFinish(self)) return self._failurelist def _initfailurelist(self): @@ -67,8 +69,7 @@ def main(self): """ main loop for running tests. """ self.shouldstop = False - self.setup() - self.config.bus.notify(repevent.SessionStart(self)) + self.sessionstarts() try: for item in self.collect(): if self.shouldstop: @@ -76,8 +77,7 @@ if not self.config.option.collectonly: self.runtest(item) finally: - failures = self.teardown() - self.config.bus.notify(repevent.SessionFinish(self)) + failures = self.sessionfinishes() return failures def runtest(self, item): From hpk at codespeak.net Wed Jul 16 16:25:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 16 Jul 2008 16:25:27 +0200 (CEST) Subject: [py-svn] r56594 - py/branch/event/py/test2/rsession Message-ID: <20080716142527.14BC72A01B6@codespeak.net> Author: hpk Date: Wed Jul 16 16:25:26 2008 New Revision: 56594 Modified: py/branch/event/py/test2/rsession/rsession.py Log: first running tests and fixing things accordingly is known to be a good idea. Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Wed Jul 16 16:25:26 2008 @@ -32,14 +32,14 @@ print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" raise SystemExit - def setup(self): - super(RSession, self).setup() + def sessionstarts(self): + super(RSession, self).sessionstarts() self.hm = hm = HostManager(self.config) self.hm.setup_hosts() - def teardown(self): - super(RSession, self).teardown() + def sessionfinishes(self): self.hm.teardown_hosts() + super(RSession, self).sessionfinishes() def runtest(self, item): # dispatch tests to host manager From hpk at codespeak.net Wed Jul 16 16:49:53 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 16 Jul 2008 16:49:53 +0200 (CEST) Subject: [py-svn] r56595 - in py/branch/event/py/test2: . rsession Message-ID: <20080716144953.24C342A807B@codespeak.net> Author: hpk Date: Wed Jul 16 16:49:52 2008 New Revision: 56595 Modified: py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/session.py Log: reviewing a bit/docstrings Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Wed Jul 16 16:49:52 2008 @@ -137,9 +137,9 @@ host.gw.host = host def init_rsync(self): - ignores = self.config.getvalue_pathlist("dist_rsync_ignore") self.prepare_gateways() # send each rsync root + ignores = self.config.getvalue_pathlist("dist_rsync_ignore") for root in self.roots: rsync = HostRSync(root, ignores=ignores, verbose=self.config.option.verbose) Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Wed Jul 16 16:49:52 2008 @@ -9,7 +9,9 @@ from py.__.test2.rsession.hostmanage import HostManager class RSession(Session): - """ Distributing tests to remote places. + """ A session that sends tests to remote places. + It reuses the Session's main() loop and overrides + runtest() for implementing the sending. """ def fixoptions(self): super(RSession, self).fixoptions() @@ -21,9 +23,8 @@ try: config.getvalue('dist_hosts') except KeyError: - print "For running ad-hoc distributed tests you need to specify" + print "For running distributed tests you need to specify" print "dist_hosts in a local conftest.py file, for example:" - print "for example:" print print " dist_hosts = ['localhost'] * 4 # for 3 processors" print " dist_hosts = ['you at remote.com', '...'] # for testing on ssh accounts" @@ -34,7 +35,7 @@ def sessionstarts(self): super(RSession, self).sessionstarts() - self.hm = hm = HostManager(self.config) + self.hm = HostManager(self.config) self.hm.setup_hosts() def sessionfinishes(self): @@ -42,7 +43,7 @@ super(RSession, self).sessionfinishes() def runtest(self, item): - # dispatch tests to host manager + # let the host manager distribute tests sent = self.hm.trysendtest(item) if not sent and not self.shouldstop: self.sleepabit() Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Jul 16 16:49:52 2008 @@ -63,6 +63,7 @@ failurelist.append(event) if self.config.option.exitfirst: self.shouldstop = True + # XXX what about removing the processfailures subscription? self.config.bus.subscribe(processfailures) return failurelist From hpk at codespeak.net Wed Jul 16 16:56:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 16 Jul 2008 16:56:23 +0200 (CEST) Subject: [py-svn] r56596 - in py/branch/event/py/test2: . terminal Message-ID: <20080716145623.288972D8007@codespeak.net> Author: hpk Date: Wed Jul 16 16:56:22 2008 New Revision: 56596 Modified: py/branch/event/py/test2/session.py py/branch/event/py/test2/terminal/remote.py Log: move towards only using ItemTestReport instead of ItemFinish Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Jul 16 16:56:22 2008 @@ -59,7 +59,7 @@ def _initfailurelist(self): failurelist = [] def processfailures(event): - if isinstance(event, repevent.ItemFinish) and event.failed: + if isinstance(event, repevent.ItemTestReport) and event.failed: failurelist.append(event) if self.config.option.exitfirst: self.shouldstop = True Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Wed Jul 16 16:56:22 2008 @@ -136,5 +136,5 @@ session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" failures = session.main() - failures = [config.get_collector_trail(ev.item) for ev in failures] + failures = [ev.trail for ev in failures] channel.send(failures) From hpk at codespeak.net Thu Jul 17 10:36:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 10:36:26 +0200 (CEST) Subject: [py-svn] r56608 - in py/branch/event/py/test2: . rep Message-ID: <20080717083626.E628616A01D@codespeak.net> Author: hpk Date: Thu Jul 17 10:36:25 2008 New Revision: 56608 Added: py/branch/event/py/test2/rep/collectonly.py - copied unchanged from r56519, py/branch/event/py/test2/collectonly.py Removed: py/branch/event/py/test2/collectonly.py Log: moving outdated collectonly reporter away Deleted: /py/branch/event/py/test2/collectonly.py ============================================================================== --- /py/branch/event/py/test2/collectonly.py Thu Jul 17 10:36:25 2008 +++ (empty file) @@ -1,31 +0,0 @@ - -""" --collectonly session, not to spread logic all over the place -""" - -import py -from py.__.test2.session import Session -from py.__.test2.reporter import LocalReporter - -class CollectReporter(LocalReporter): - def __init__(self, *args, **kwds): - super(LocalReporter, self).__init__(*args, **kwds) - self.indent = 0 - - def report_ItemFinish(self, event): - pass - - def report_ItemStart(self, event): - self.out.line(" " * self.indent + str(event.item)) - self.indent += 2 - - def report_ItemFinish(self, event): - self.indent -= 2 - - def report_CollectionFailed(self, event): - self.out.line(" " * self.indent + "- FAILED TO LOAD MODULE -") - - def report_DeselectedTest(self, event): - self.out.line(" " * self.indent + "- skipped -") - - def summary(self): - self.out.sep("=", "Total time: %.1f" % (self.timeend - self.timestart)) From hpk at codespeak.net Thu Jul 17 13:44:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 13:44:56 +0200 (CEST) Subject: [py-svn] r56614 - in py/branch/event/py: . apigen/testing apigen/tracer bin doc misc misc/testing path/local path/svn path/svn/testing test test/testing Message-ID: <20080717114456.0FF7C2A017B@codespeak.net> Author: hpk Date: Thu Jul 17 13:44:54 2008 New Revision: 56614 Added: py/branch/event/py/bin/py.which - copied unchanged from r56613, py/trunk/py/bin/py.which py/branch/event/py/misc/_maketest2.py - copied unchanged from r56613, py/trunk/py/misc/_maketest2.py py/branch/event/py/path/svn/testing/test_auth.py - copied unchanged from r56613, py/trunk/py/path/svn/testing/test_auth.py Modified: py/branch/event/py/__init__.py py/branch/event/py/apigen/testing/test_apigen_functional.py py/branch/event/py/apigen/tracer/docstorage.py py/branch/event/py/bin/_docgen.py py/branch/event/py/bin/_makepyrelease.py py/branch/event/py/bin/_update_website.py py/branch/event/py/conftest.py py/branch/event/py/doc/TODO.txt py/branch/event/py/doc/conftest.py py/branch/event/py/doc/path.txt py/branch/event/py/doc/test.txt py/branch/event/py/initpkg.py py/branch/event/py/misc/_dist.py py/branch/event/py/misc/testing/test_initpkg.py py/branch/event/py/misc/testing/test_oskill.py py/branch/event/py/path/local/local.py py/branch/event/py/path/svn/svncommon.py py/branch/event/py/path/svn/testing/svntestbase.py py/branch/event/py/path/svn/testing/test_wccommand.py py/branch/event/py/path/svn/urlcommand.py py/branch/event/py/path/svn/wccommand.py py/branch/event/py/test/collect.py py/branch/event/py/test/doctest.py py/branch/event/py/test/reporter.py py/branch/event/py/test/representation.py py/branch/event/py/test/session.py py/branch/event/py/test/testing/setupdata.py py/branch/event/py/test/testing/test_doctest.py py/branch/event/py/test/testing/test_session.py Log: * svn merge -r 51120:56514 https://codespeak.net/svn/py/trunk * resolving issues Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Thu Jul 17 13:44:54 2008 @@ -84,6 +84,7 @@ 'path.svnwc' : ('./path/svn/wccommand.py', 'SvnWCCommandPath'), 'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'), 'path.local' : ('./path/local/local.py', 'LocalPath'), + 'path.SvnAuth' : ('./path/svn/svncommon.py', 'SvnAuth'), # some nice slightly magic APIs 'magic.__doc__' : ('./magic/__init__.py', '__doc__'), Modified: py/branch/event/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/branch/event/py/apigen/testing/test_apigen_functional.py (original) +++ py/branch/event/py/apigen/testing/test_apigen_functional.py Thu Jul 17 13:44:54 2008 @@ -117,7 +117,9 @@ pkgname, documentable = apigen.get_documentable_items_pkgdir( fs_root.join(package_name)) assert pkgname == 'pak' - assert sorted(documentable.keys()) == [ + keys = documentable.keys() + keys.sort() + assert keys == [ 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo'] Modified: py/branch/event/py/apigen/tracer/docstorage.py ============================================================================== --- py/branch/event/py/apigen/tracer/docstorage.py (original) +++ py/branch/event/py/apigen/tracer/docstorage.py Thu Jul 17 13:44:54 2008 @@ -16,7 +16,7 @@ sorted = py.builtin.sorted def pkg_to_dict(module): - defs = module.__package__.exportdefs + defs = module.__pkg__.exportdefs d = {} for key, value in defs.iteritems(): chain = key.split('.') @@ -33,7 +33,7 @@ """ deal with '*' entries in an initpkg situation """ ret = {} modpath = py.path.local(inspect.getsourcefile(module)) - pkgpath = module.__package__.getpath() + pkgpath = module.__pkg__.getpath() for objname in dir(module): if objname.startswith('_'): continue # also skip __*__ attributes @@ -281,8 +281,8 @@ return "Lack of module info" try: retval = module.__doc__ or "*undocumented*" - retval = module.__package__.description - retval = module.__package__.long_description + retval = module.__pkg__.description + retval = module.__pkg__.long_description except AttributeError: pass return retval Modified: py/branch/event/py/bin/_docgen.py ============================================================================== --- py/branch/event/py/bin/_docgen.py (original) +++ py/branch/event/py/bin/_docgen.py Thu Jul 17 13:44:54 2008 @@ -10,7 +10,7 @@ from _findpy import py import py -pypath = py.__package__.getpath() +pypath = py.__pkg__.getpath() def run_tests(path, envvars='', args=''): pytestpath = pypath.join('bin/py.test') @@ -28,7 +28,7 @@ def build_docs(targetpath, testargs): docpath = pypath.join('doc') run_tests(docpath, '', - testargs + ' --forcegen --apigenrelpath="apigen/"') + testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) docpath.copy(targetpath) def build_nav(targetpath, docs=True, api=True): Modified: py/branch/event/py/bin/_makepyrelease.py ============================================================================== --- py/branch/event/py/bin/_makepyrelease.py (original) +++ py/branch/event/py/bin/_makepyrelease.py Thu Jul 17 13:44:54 2008 @@ -147,9 +147,9 @@ if __name__ == '__main__': py.magic.invoke(assertion=True) version = py.std.sys.argv[1] - assert py.__package__.version == version, ( + assert py.__pkg__.version == version, ( "py package has version %s\nlocation: %s" % - (py.__package__.version, pydir)) + (py.__pkg__.version, pydir)) tmpdir = py.path.local.get_temproot().join('makepyrelease-%s' % version) if tmpdir.check(): @@ -171,14 +171,14 @@ py.process.cmdexec("rsync -avz %(source)s/ %(remotedir)s" % locals()) ddir = tmpdir.ensure('download', dir=1) - URL = py.__package__.download_url # 'http://codespeak.net/download/py/' + URL = py.__pkg__.download_url # 'http://codespeak.net/download/py/' unpacked = unpackremotetar(ddir, URL) assert unpacked == ddir.join("py-%s" % (version,)) #checksvnworks(unpacked) #pytest(unpacked) - pytest_remote('test at codespeak.net', py.__package__.download_url) + pytest_remote('test at codespeak.net', py.__pkg__.download_url) Modified: py/branch/event/py/bin/_update_website.py ============================================================================== --- py/branch/event/py/bin/_update_website.py (original) +++ py/branch/event/py/bin/_update_website.py Thu Jul 17 13:44:54 2008 @@ -24,7 +24,7 @@ def run_tests(pkgpath, apigenpath, args='', captureouterr=False): """ run the unit tests and build the docs """ - pypath = py.__package__.getpath() + pypath = py.__pkg__.getpath() pytestpath = pypath.join('bin/py.test') # XXX this would need a Windows specific version if we want to allow # running this script on that platform, but currently --apigen doesn't @@ -82,7 +82,7 @@ args.remove('--ignorefail') ignorefail = True args = ' '.join(sys.argv[1:]) - pkgpath = py.__package__.getpath() + pkgpath = py.__pkg__.getpath() apidocspath = pkgpath.dirpath().join('apigen') main(pkgpath, apidocspath, 'codespeak.net', '/home/guido/rsynctests', args, ignorefail) Modified: py/branch/event/py/conftest.py ============================================================================== --- py/branch/event/py/conftest.py (original) +++ py/branch/event/py/conftest.py Thu Jul 17 13:44:54 2008 @@ -33,6 +33,9 @@ action='store', dest='docpath', default="doc", type='string', help="relative path to doc output location (relative from py/)"), + Option('', '--runslowtests', + action="store_true", dest="runslowtests", default=False, + help="run slow tests)"), ) dist_rsync_roots = ['.'] Modified: py/branch/event/py/doc/TODO.txt ============================================================================== --- py/branch/event/py/doc/TODO.txt (original) +++ py/branch/event/py/doc/TODO.txt Thu Jul 17 13:44:54 2008 @@ -4,8 +4,11 @@ py.test -------------- +- BUG: write test/fix --showlocals (not showing anything) + - review and refactor architecture of py.test with particular respect to: + - allow custom reporting - writing (stacked) extensions / plugins (compared to Nose) - event naming and processing - porting existing extensions (htmlconftest / buildbot / PyPy's conftest's ...) @@ -22,6 +25,10 @@ - document py.test's conftest.py approach +- review and optimize skip-handling (it can be quite slow in + certain situations because e.g. setup/teardown is fully performed + although we have "skip by keyword" and could detect this early) + py.execnet -------------- @@ -35,10 +42,12 @@ - refactor to produce intermediate data/files capturing info of test runs - - refactor html renderer to work on intermediate data/files rather than on the live data +- check out CodeInvestigator + http://codeinvestigator.googlepages.com/main + ld (review and shift to above) ================================= Modified: py/branch/event/py/doc/conftest.py ============================================================================== --- py/branch/event/py/doc/conftest.py (original) +++ py/branch/event/py/doc/conftest.py Thu Jul 17 13:44:54 2008 @@ -311,7 +311,7 @@ 'to the py package') % (text,) relpath = '/'.join(text.split('/')[1:]) if check: - pkgroot = py.__package__.getpath() + pkgroot = py.__pkg__.getpath() abspath = pkgroot.join(relpath) assert pkgroot.join(relpath).check(), ( 'problem with linkrole :source:`%s`: ' Modified: py/branch/event/py/doc/path.txt ============================================================================== --- py/branch/event/py/doc/path.txt (original) +++ py/branch/event/py/doc/path.txt Thu Jul 17 13:44:54 2008 @@ -187,6 +187,23 @@ >>> len(wc.status().prop_modified) 0 +SVN authentication +++++++++++++++++++++++ + +Some uncommon functionality can also be provided as extensions, such as SVN +authentication:: + + >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False, + ... interactive=False) + >>> wc.auth = auth + >>> wc.update() # this should work + >>> path = wc.ensure('thisshouldnotexist.txt') + >>> try: + ... path.commit('testing') + ... except py.process.cmdexec.Error, e: + ... pass + >>> 'authorization failed' in str(e) + True Known problems / limitations =================================== Modified: py/branch/event/py/doc/test.txt ============================================================================== --- py/branch/event/py/doc/test.txt (original) +++ py/branch/event/py/doc/test.txt Thu Jul 17 13:44:54 2008 @@ -501,7 +501,7 @@ * ssh daemon running * ssh keys setup to allow login without a password -* python (2.3 or 2.4 should work) +* python * unix like machine (reliance on ``os.fork``) How to use it Modified: py/branch/event/py/initpkg.py ============================================================================== --- py/branch/event/py/initpkg.py (original) +++ py/branch/event/py/initpkg.py Thu Jul 17 13:44:54 2008 @@ -38,9 +38,9 @@ self.name = name self.exportdefs = exportdefs self.module = pkgmodule - assert not hasattr(pkgmodule, '__package__'), \ + assert not hasattr(pkgmodule, '__pkg__'), \ "unsupported reinitialization of %r" % pkgmodule - pkgmodule.__package__ = self + pkgmodule.__pkg__ = self # make available pkgname.__ implname = name + '.' + '__' @@ -134,7 +134,7 @@ from cStringIO import StringIO except ImportError: from StringIO import StringIO - base = py.__package__.getpath().dirpath() + base = py.__pkg__.getpath().dirpath() outf = StringIO() f = zipfile.ZipFile(outf, 'w', compression=zipfile.ZIP_DEFLATED) try: @@ -164,14 +164,14 @@ class Module(ModuleType): def __init__(self, pkg, name): - self.__package__ = pkg + self.__pkg__ = pkg self.__name__ = name self.__map__ = {} def __getattr__(self, name): if '*' in self.__map__: extpy = self.__map__['*'][0], name - result = self.__package__._resolve(extpy) + result = self.__pkg__._resolve(extpy) else: try: extpy = self.__map__[name] @@ -179,7 +179,7 @@ __tracebackhide__ = True raise AttributeError(name) else: - result = self.__package__._resolve(extpy) + result = self.__pkg__._resolve(extpy) del self.__map__[name] setattr(self, name, result) #self._fixinspection(result, name) @@ -216,7 +216,7 @@ assert not self.__map__, "%r not empty" % self.__map__ else: fsname = self.__map__['*'][0] - dict.update(self.__package__._loadimpl(fsname[:-3]).__dict__) + dict.update(self.__pkg__._loadimpl(fsname[:-3]).__dict__) return dict __dict__ = property(getdict) Modified: py/branch/event/py/misc/_dist.py ============================================================================== --- py/branch/event/py/misc/_dist.py (original) +++ py/branch/event/py/misc/_dist.py Thu Jul 17 13:44:54 2008 @@ -144,7 +144,7 @@ params = Params(pkg) #dump(params) - source = getattr(pkg, '__package__', pkg) + source = getattr(pkg, '__pkg__', pkg) namelist = list(core.setup_keywords) namelist.extend(['packages', 'scripts', 'data_files']) for name in namelist: Modified: py/branch/event/py/misc/testing/test_initpkg.py ============================================================================== --- py/branch/event/py/misc/testing/test_initpkg.py (original) +++ py/branch/event/py/misc/testing/test_initpkg.py Thu Jul 17 13:44:54 2008 @@ -33,7 +33,7 @@ def test_resolve_attrerror(): extpyish = "./initpkg.py", "hello" - excinfo = py.test.raises(AttributeError, "py.__package__._resolve(extpyish)") + excinfo = py.test.raises(AttributeError, "py.__pkg__._resolve(extpyish)") s = str(excinfo.value) assert s.find(extpyish[0]) != -1 assert s.find(extpyish[1]) != -1 @@ -83,16 +83,16 @@ assert __import__(modpath) def test_shahexdigest(): - hex = py.__package__.shahexdigest() + hex = py.__pkg__.shahexdigest() assert len(hex) == 40 def test_getzipdata(): - s = py.__package__.getzipdata() + s = py.__pkg__.getzipdata() def test_getrev(): if not py.path.local(py.__file__).dirpath('.svn').check(): py.test.skip("py package is not a svn checkout") - d = py.__package__.getrev() + d = py.__pkg__.getrev() svnversion = py.path.local.sysfind('svnversion') if svnversion is None: py.test.skip("cannot test svnversion, 'svnversion' binary not found") @@ -255,9 +255,9 @@ def test_url_of_version(): #py.test.skip("FAILING! - provide a proper URL or upload pylib tgz") from urllib import URLopener - url = py.__package__.download_url + url = py.__pkg__.download_url if url.lower() == "xxx": - assert py.__package__.version.find("alpha") != -1 + assert py.__pkg__.version.find("alpha") != -1 else: URLopener().open(url) Modified: py/branch/event/py/misc/testing/test_oskill.py ============================================================================== --- py/branch/event/py/misc/testing/test_oskill.py (original) +++ py/branch/event/py/misc/testing/test_oskill.py Thu Jul 17 13:44:54 2008 @@ -4,6 +4,10 @@ from py.__.misc.killproc import killproc def test_win_killsubprocess(): + if sys.platform == 'win32' and not py.path.local.sysfind('taskkill'): + py.test.skip("you\'re using an older version of windows, which " + "doesn\'t support 'taskkill' - py.misc.killproc is not " + "available") tmp = py.test.ensuretemp("test_win_killsubprocess") t = tmp.join("t.py") t.write("import time ; time.sleep(100)") Modified: py/branch/event/py/path/local/local.py ============================================================================== --- py/branch/event/py/path/local/local.py (original) +++ py/branch/event/py/path/local/local.py Thu Jul 17 13:44:54 2008 @@ -400,8 +400,8 @@ self._prependsyspath(pkgpath.dirpath()) pkg = __import__(pkgpath.basename, None, None, []) - if hasattr(pkg, '__package__'): - modname = pkg.__package__.getimportname(self) + if hasattr(pkg, '__pkg__'): + modname = pkg.__pkg__.getimportname(self) assert modname is not None, "package %s doesn't know %s" % ( pkg.__name__, self) Modified: py/branch/event/py/path/svn/svncommon.py ============================================================================== --- py/branch/event/py/path/svn/svncommon.py (original) +++ py/branch/event/py/path/svn/svncommon.py Thu Jul 17 13:44:54 2008 @@ -65,6 +65,7 @@ """ obj = object.__new__(self.__class__) obj.rev = kw.get('rev', self.rev) + obj.auth = kw.get('auth', self.auth) dirname, basename, purebasename, ext = self._getbyspec( "dirname,basename,purebasename,ext") if 'basename' in kw: @@ -138,7 +139,7 @@ args = tuple([arg.strip(self.sep) for arg in args]) parts = (self.strpath, ) + args - newpath = self.__class__(self.sep.join(parts), self.rev) + newpath = self.__class__(self.sep.join(parts), self.rev, self.auth) return newpath def propget(self, name): @@ -330,3 +331,27 @@ fspath = '%s at HEAD' % (fspath,) return 'file://%s' % (fspath,) +class SvnAuth(object): + """ container for auth information for Subversion """ + def __init__(self, username, password, cache_auth=True, interactive=True): + self.username = username + self.password = password + self.cache_auth = cache_auth + self.interactive = interactive + + def makecmdoptions(self): + uname = self.username.replace('"', '\\"') + passwd = self.password.replace('"', '\\"') + ret = [] + if uname: + ret.append('--username="%s"' % (uname,)) + if passwd: + ret.append('--password="%s"' % (passwd,)) + if not self.cache_auth: + ret.append('--no-auth-cache') + if not self.interactive: + ret.append('--non-interactive') + return ' '.join(ret) + + def __str__(self): + return "" %(self.username,) Modified: py/branch/event/py/path/svn/testing/svntestbase.py ============================================================================== --- py/branch/event/py/path/svn/testing/svntestbase.py (original) +++ py/branch/event/py/path/svn/testing/svntestbase.py Thu Jul 17 13:44:54 2008 @@ -99,7 +99,6 @@ assert res.size > len("samplefile") and res.created_rev >= 0 def test_log_simple(self): - py.test.skip("XXX: does not work at least on svn below 1.3") url = self.root.join("samplefile") logentries = url.log() for logentry in logentries: Modified: py/branch/event/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/branch/event/py/path/svn/testing/test_wccommand.py (original) +++ py/branch/event/py/path/svn/testing/test_wccommand.py Thu Jul 17 13:44:54 2008 @@ -1,13 +1,27 @@ import py +import sys from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc from py.__.path.svn.wccommand import InfoSvnWCCommand from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon - if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") +if sys.platform != 'win32': + def normpath(p): + return p +else: + try: + import win32api + except ImportError: + def normpath(p): + py.test.skip('this test requires win32api to run on windows') + else: + import os + def normpath(p): + p = win32api.GetShortPathName(p) + return os.path.normpath(os.path.normcase(p)) class TestWCSvnCommandPath(CommonSvnTests): @@ -253,7 +267,7 @@ try: locked = root.status().locked assert len(locked) == 1 - assert str(locked[0]) == str(somefile) + assert normpath(str(locked[0])) == normpath(str(somefile)) #assert somefile.locked() py.test.raises(Exception, 'somefile.lock()') finally: Modified: py/branch/event/py/path/svn/urlcommand.py ============================================================================== --- py/branch/event/py/path/svn/urlcommand.py (original) +++ py/branch/event/py/path/svn/urlcommand.py Thu Jul 17 13:44:54 2008 @@ -1,7 +1,8 @@ """ module defining a subversion path object based on the external -command 'svn'. +command 'svn'. This modules aims to work with svn 1.3 and higher +but might also interact well with earlier versions. """ @@ -21,10 +22,11 @@ _lsrevcache = BuildcostAccessCache(maxentries=128) _lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0) - def __new__(cls, path, rev=None): + def __new__(cls, path, rev=None, auth=None): self = object.__new__(cls) if isinstance(path, cls): rev = path.rev + auth = path.auth path = path.strpath proto, uri = path.split("://", 1) host, uripath = uri.split('/', 1) @@ -36,6 +38,7 @@ path = path.rstrip('/') self.strpath = path self.rev = rev + self.auth = auth return self def __repr__(self): @@ -44,7 +47,8 @@ else: return 'svnurl(%r, %r)' % (self.strpath, self.rev) - def _svn(self, cmd, *args): + def _svnwithrev(self, cmd, *args): + """ execute an svn command, append our own url and revision """ if self.rev is None: return self._svnwrite(cmd, *args) else: @@ -52,16 +56,28 @@ return self._svnwrite(cmd, *args) def _svnwrite(self, cmd, *args): + """ execute an svn command, append our own url """ l = ['svn %s' % cmd] args = ['"%s"' % self._escape(item) for item in args] l.extend(args) l.append('"%s"' % self._encodedurl()) # fixing the locale because we can't otherwise parse - string = svncommon.fixlocale() + " ".join(l) + string = " ".join(l) if DEBUG: print "execing", string + out = self._svncmdexecauth(string) + return out + + def _svncmdexecauth(self, cmd): + """ execute an svn command 'as is' """ + cmd = svncommon.fixlocale() + cmd + if self.auth is not None: + cmd += ' ' + self.auth.makecmdoptions() + return self._cmdexec(cmd) + + def _cmdexec(self, cmd): try: - out = process.cmdexec(string) + out = process.cmdexec(cmd) except py.process.cmdexec.Error, e: if (e.err.find('File Exists') != -1 or e.err.find('File already exists') != -1): @@ -69,21 +85,33 @@ raise return out + def _svnpopenauth(self, cmd): + """ execute an svn command, return a pipe for reading stdin """ + cmd = svncommon.fixlocale() + cmd + if self.auth is not None: + cmd += ' ' + self.auth.makecmdoptions() + return self._popen(cmd) + + def _popen(self, cmd): + return os.popen(cmd) + def _encodedurl(self): return self._escape(self.strpath) + def _norev_delentry(self, path): + auth = self.auth and self.auth.makecmdoptions() or None + self._lsnorevcache.delentry((str(path), auth)) + def open(self, mode='r'): """ return an opened file with the given mode. """ assert 'w' not in mode and 'a' not in mode, "XXX not implemented for svn cmdline" assert self.check(file=1) # svn cat returns an empty file otherwise - def popen(cmd): - return os.popen(cmd) if self.rev is None: - return popen(svncommon.fixlocale() + - 'svn cat "%s"' % (self._escape(self.strpath), )) + return self._svnpopenauth('svn cat "%s"' % ( + self._escape(self.strpath), )) else: - return popen(svncommon.fixlocale() + - 'svn cat -r %s "%s"' % (self.rev, self._escape(self.strpath))) + return self._svnpopenauth('svn cat -r %s "%s"' % ( + self.rev, self._escape(self.strpath))) def dirpath(self, *args, **kwargs): """ return the directory path of the current path joined @@ -104,33 +132,33 @@ commit_msg=kwargs.get('msg', "mkdir by py lib invocation") createpath = self.join(*args) createpath._svnwrite('mkdir', '-m', commit_msg) - self._lsnorevcache.delentry(createpath.dirpath().strpath) + self._norev_delentry(createpath.dirpath()) return createpath def copy(self, target, msg='copied by py lib invocation'): """ copy path to target with checkin message msg.""" if getattr(target, 'rev', None) is not None: raise py.error.EINVAL(target, "revisions are immutable") - process.cmdexec('svn copy -m "%s" "%s" "%s"' %(msg, - self._escape(self), self._escape(target))) - self._lsnorevcache.delentry(target.dirpath().strpath) + self._svncmdexecauth('svn copy -m "%s" "%s" "%s"' %(msg, + self._escape(self), self._escape(target))) + self._norev_delentry(target.dirpath()) def rename(self, target, msg="renamed by py lib invocation"): """ rename this path to target with checkin message msg. """ if getattr(self, 'rev', None) is not None: raise py.error.EINVAL(self, "revisions are immutable") - py.process.cmdexec('svn move -m "%s" --force "%s" "%s"' %( - msg, self._escape(self), self._escape(target))) - self._lsnorevcache.delentry(self.dirpath().strpath) - self._lsnorevcache.delentry(self.strpath) + self._svncmdexecauth('svn move -m "%s" --force "%s" "%s"' %( + msg, self._escape(self), self._escape(target))) + self._norev_delentry(self.dirpath()) + self._norev_delentry(self) def remove(self, rec=1, msg='removed by py lib invocation'): """ remove a file or directory (or a directory tree if rec=1) with checkin message msg.""" if self.rev is not None: raise py.error.EINVAL(self, "revisions are immutable") - process.cmdexec('svn rm -m "%s" "%s"' %(msg, self._escape(self))) - self._lsnorevcache.delentry(self.dirpath().strpath) + self._svncmdexecauth('svn rm -m "%s" "%s"' %(msg, self._escape(self))) + self._norev_delentry(self.dirpath()) def export(self, topath): """ export to a local path @@ -143,7 +171,7 @@ '"%s"' % (self._escape(topath),)] if self.rev is not None: args = ['-r', str(self.rev)] + args - process.cmdexec('svn export %s' % (' '.join(args),)) + self._svncmdexecauth('svn export %s' % (' '.join(args),)) return topath def ensure(self, *args, **kwargs): @@ -173,19 +201,19 @@ "ensure %s" % self._escape(tocreate), self._escape(tempdir.join(basename)), x.join(basename)._encodedurl()) - process.cmdexec(cmd) - self._lsnorevcache.delentry(x.strpath) # !!! + self._svncmdexecauth(cmd) + self._norev_delentry(x) finally: tempdir.remove() return target # end of modifying methods def _propget(self, name): - res = self._svn('propget', name) + res = self._svnwithrev('propget', name) return res[:-1] # strip trailing newline def _proplist(self): - res = self._svn('proplist') + res = self._svnwithrev('proplist') lines = res.split('\n') lines = map(str.strip, lines[1:]) return svncommon.PropListDict(self, lines) @@ -194,7 +222,7 @@ """ return sequence of name-info directory entries of self """ def builder(): try: - res = self._svn('ls', '-v') + res = self._svnwithrev('ls', '-v') except process.cmdexec.Error, e: if e.err.find('non-existent in that revision') != -1: raise py.error.ENOENT(self, e.err) @@ -212,12 +240,17 @@ for lsline in lines: if lsline: info = InfoSvnCommand(lsline) - nameinfo_seq.append((info._name, info)) + if info._name != '.': # svn 1.5 produces '.' dirs, + nameinfo_seq.append((info._name, info)) + return nameinfo_seq + auth = self.auth and self.auth.makecmdoptions() or None if self.rev is not None: - return self._lsrevcache.getorbuild((self.strpath, self.rev), builder) + return self._lsrevcache.getorbuild((self.strpath, self.rev, auth), + builder) else: - return self._lsnorevcache.getorbuild(self.strpath, builder) + return self._lsnorevcache.getorbuild((self.strpath, auth), + builder) def log(self, rev_start=None, rev_end=1, verbose=False): """ return a list of LogEntry instances for this path. @@ -234,9 +267,8 @@ else: rev_opt = "-r %s:%s" % (rev_start, rev_end) verbose_opt = verbose and "-v" or "" - xmlpipe = os.popen(svncommon.fixlocale() + - 'svn log --xml %s %s "%s"' % - (rev_opt, verbose_opt, self.strpath)) + xmlpipe = self._svnpopenauth('svn log --xml %s %s "%s"' % + (rev_opt, verbose_opt, self.strpath)) from xml.dom import minidom tree = minidom.parse(xmlpipe) result = [] @@ -254,7 +286,7 @@ # the '0?' part in the middle is an indication of whether the resource is # locked, see 'svn help ls' lspattern = re.compile( - r'^ *(?P\d+) +(?P\S+) +(0? *(?P\d+))? ' + r'^ *(?P\d+) +(?P.+?) +(0? *(?P\d+))? ' '*(?P\w+ +\d{2} +[\d:]+) +(?P.*)$') def __init__(self, line): # this is a typical line from 'svn ls http://...' Modified: py/branch/event/py/path/svn/wccommand.py ============================================================================== --- py/branch/event/py/path/svn/wccommand.py (original) +++ py/branch/event/py/path/svn/wccommand.py Thu Jul 17 13:44:54 2008 @@ -25,7 +25,7 @@ """ sep = os.sep - def __new__(cls, wcpath=None): + def __new__(cls, wcpath=None, auth=None): self = object.__new__(cls) if isinstance(wcpath, cls): if wcpath.__class__ == cls: @@ -35,6 +35,7 @@ svncommon.ALLOWED_CHARS): raise ValueError("bad char in wcpath %s" % (wcpath, )) self.localpath = py.path.local(wcpath) + self.auth = auth return self strpath = property(lambda x: str(x.localpath), None, None, "string path") @@ -63,13 +64,22 @@ info = self.info() return py.path.svnurl(info.url) - def __repr__(self): return "svnwc(%r)" % (self.strpath) # , self._url) def __str__(self): return str(self.localpath) + def _makeauthoptions(self): + if self.auth is None: + return '' + return self.auth.makecmdoptions() + + def _authsvn(self, cmd, args=None): + args = args and list(args) or [] + args.append(self._makeauthoptions()) + return self._svn(cmd, *args) + def _svn(self, cmd, *args): l = ['svn %s' % cmd] args = [self._escape(item) for item in args] @@ -101,9 +111,9 @@ raise return out - def switch(self, url): + def switch(self, url): """ switch to given URL. """ - self._svn('switch', url) + self._authsvn('switch', [url]) def checkout(self, url=None, rev=None): """ checkout from url to local wcpath. """ @@ -119,11 +129,12 @@ url += "@%d" % rev else: args.append('-r' + str(rev)) - self._svn('co', url, *args) + args.append(url) + self._authsvn('co', args) def update(self, rev = 'HEAD'): """ update working copy item to given revision. (None -> HEAD). """ - self._svn('up -r %s' % rev) + self._authsvn('up', ['-r', rev]) def write(self, content, mode='wb'): """ write content into local filesystem wc. """ @@ -131,7 +142,7 @@ def dirpath(self, *args): """ return the directory Path of the current Path. """ - return self.__class__(self.localpath.dirpath(*args)) + return self.__class__(self.localpath.dirpath(*args), auth=self.auth) def _ensuredirs(self): parent = self.dirpath() @@ -197,18 +208,21 @@ """ rename this path to target. """ py.process.cmdexec("svn move --force %s %s" %(str(self), str(target))) - _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(\S+)\s+(.*)') + # XXX a bit scary to assume there's always 2 spaces between username and + # path, however with win32 allowing spaces in user names there doesn't + # seem to be a more solid approach :( + _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') def lock(self): """ set a lock (exclusive) on the resource """ - out = self._svn('lock').strip() + out = self._authsvn('lock').strip() if not out: # warning or error, raise exception raise Exception(out[4:]) def unlock(self): """ unset a previously set lock """ - out = self._svn('unlock').strip() + out = self._authsvn('unlock').strip() if out.startswith('svn:'): # warning or error, raise exception raise Exception(out[4:]) @@ -248,7 +262,8 @@ update_rev = None - out = self._svn('status -v %s %s %s' % (updates, rec, externals)) + cmd = 'status -v %s %s %s' % (updates, rec, externals) + out = self._authsvn(cmd) rootstatus = WCStatus(self) for line in out.split('\n'): if not line.strip(): @@ -266,7 +281,8 @@ wcpath = self.join(fn, abs=1) rootstatus.unknown.append(wcpath) elif c0 == 'X': - wcpath = self.__class__(self.localpath.join(fn, abs=1)) + wcpath = self.__class__(self.localpath.join(fn, abs=1), + auth=self.auth) rootstatus.external.append(wcpath) elif c0 == 'I': wcpath = self.join(fn, abs=1) @@ -334,10 +350,10 @@ """ return a diff of the current path against revision rev (defaulting to the last one). """ - if rev is None: - out = self._svn('diff') - else: - out = self._svn('diff -r %d' % rev) + args = [] + if rev is not None: + args.append("-r %d" % rev) + out = self._authsvn('diff', args) return out def blame(self): @@ -365,7 +381,7 @@ cmd = 'commit -m "%s" --force-log' % (msg.replace('"', '\\"'),) if not rec: cmd += ' -N' - out = self._svn(cmd) + out = self._authsvn(cmd) try: del cache.info[self] except KeyError: @@ -431,7 +447,7 @@ localpath = self.localpath.new(**kw) else: localpath = self.localpath - return self.__class__(localpath) + return self.__class__(localpath, auth=self.auth) def join(self, *args, **kwargs): """ return a new Path (with the same revision) which is composed @@ -440,7 +456,7 @@ if not args: return self localpath = self.localpath.join(*args, **kwargs) - return self.__class__(localpath) + return self.__class__(localpath, auth=self.auth) def info(self, usecache=1): """ return an Info structure with svn-provided information. """ @@ -483,7 +499,7 @@ paths = [] for localpath in self.localpath.listdir(notsvn): - p = self.__class__(localpath) + p = self.__class__(localpath, auth=self.auth) paths.append(p) if fil or sort: @@ -534,11 +550,13 @@ else: rev_opt = "-r %s:%s" % (rev_start, rev_end) verbose_opt = verbose and "-v" or "" - s = svncommon.fixlocale() + locale_env = svncommon.fixlocale() # some blather on stderr - stdin, stdout, stderr = os.popen3(s + 'svn log --xml %s %s "%s"' % ( - rev_opt, verbose_opt, - self.strpath)) + auth_opt = self._makeauthoptions() + stdin, stdout, stderr = os.popen3(locale_env + + 'svn log --xml %s %s %s "%s"' % ( + rev_opt, verbose_opt, auth_opt, + self.strpath)) from xml.dom import minidom from xml.parsers.expat import ExpatError try: @@ -562,7 +580,7 @@ return self.info().mtime def __hash__(self): - return hash((self.strpath, self.__class__)) + return hash((self.strpath, self.__class__, self.auth)) class WCStatus: Modified: py/branch/event/py/test/collect.py ============================================================================== --- py/branch/event/py/test/collect.py (original) +++ py/branch/event/py/test/collect.py Thu Jul 17 13:44:54 2008 @@ -502,13 +502,17 @@ call, args = obj, () return call, args -class DoctestFile(PyCollectorMixin, FSCollector): +class DoctestFile(Module): + # XXX fix py.test reporting + # we subclass Module here in order to get py.test's reporting + # show the ".txt" filename in the test run much like a + # python module shows up. instead py.test needs to + # support more direct means of influencing reporting. def run(self): return [self.fspath.basename] def join(self, name): - from py.__.test.doctest import DoctestText - if name == self.fspath.basename: - item = DoctestText(self.fspath.basename, parent=self) - item._content = self.fspath.read() - return item + if name == self.fspath.basename: + from py.__.test.doctest import DoctestFileContent + return DoctestFileContent(name, self) + Modified: py/branch/event/py/test/doctest.py ============================================================================== --- py/branch/event/py/test/doctest.py (original) +++ py/branch/event/py/test/doctest.py Thu Jul 17 13:44:54 2008 @@ -1,9 +1,6 @@ import py -class DoctestText(py.test.collect.Item): - - def _setcontent(self, content): - self._content = content +class DoctestFileContent(py.test.collect.Item): #def buildname2items(self): # parser = py.compat.doctest.DoctestParser() @@ -16,18 +13,12 @@ # d[str(i)] = ex def run(self): - mod = py.std.types.ModuleType(self.name) - #for line in s.split('\n'): - # if line.startswith(prefix): - # exec py.code.Source(line[len(prefix):]).compile() in mod.__dict__ - # line = "" - # else: - # l.append(line) - self.execute(mod, self._content) + self.execute() - def execute(self, mod, docstring): - mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) + def execute(self): + failed, tot = py.compat.doctest.testfile(str(self.fspath), module_relative=False, verbose=1) + #mod.__file__ = str(self.fspath) + #failed, tot = py.compat.doctest.testmod(mod, verbose=1) if failed: py.test.fail("doctest %s: %s failed out of %s" %( self.fspath, failed, tot)) Modified: py/branch/event/py/test/reporter.py ============================================================================== --- py/branch/event/py/test/reporter.py (original) +++ py/branch/event/py/test/reporter.py Thu Jul 17 13:44:54 2008 @@ -49,12 +49,15 @@ return self.flag class AbstractReporter(object): - def __init__(self, config, hosts): + def __init__(self, config, hosts, out=None): self.config = config self.hosts = hosts self.failed_tests_outcome = [] self.skipped_tests_outcome = [] - self.out = getout(py.std.sys.stdout) + if out is None: + self.out = getout(py.std.sys.stdout) + else: + self.out = out self.presenter = Presenter(self.out, config) self.to_rsync = {} @@ -356,7 +359,7 @@ self.timestart = item.timestart self.out.line("executable: %s (%s)" % (py.std.sys.executable, repr_pythonversion())) - rev = py.__package__.getrev() + rev = py.__pkg__.getrev() self.out.line("using py lib: %s " % ( py.path.local(py.__file__).dirpath(), rev)) config = item.config Modified: py/branch/event/py/test/representation.py ============================================================================== --- py/branch/event/py/test/representation.py (original) +++ py/branch/event/py/test/representation.py Thu Jul 17 13:44:54 2008 @@ -71,8 +71,11 @@ s = str(source.getstatement(len(source)-1)) except KeyboardInterrupt: raise - except: - s = str(source[-1]) + except: + try: + s = str(source[-1]) + except IndexError: + s = "" indent = " " * (4 + (len(s) - len(s.lstrip()))) # get the real exception information out lines = excinfo.exconly(tryshort=True).split('\n') Modified: py/branch/event/py/test/session.py ============================================================================== --- py/branch/event/py/test/session.py (original) +++ py/branch/event/py/test/session.py Thu Jul 17 13:44:54 2008 @@ -33,10 +33,10 @@ if isinstance(next, stopitems): try: next._skipbykeyword(keyword) + if session and session.config.option.keyword_oneshot: + keyword = None yield next except Skipped: - if session.config.option.keyword_oneshot: - keyword = None excinfo = py.code.ExceptionInfo() reporter(repevent.SkippedTryiter(excinfo, next)) else: @@ -52,6 +52,8 @@ reporter(repevent.SkippedTryiter(excinfo, next)) else: reporter(repevent.FailedTryiter(excinfo, next)) + if session.config.option.usepdb: + py.__.test.custompdb.post_mortem(excinfo._excinfo[2]) if reporter: reporter(repevent.ItemFinish(next)) Modified: py/branch/event/py/test/testing/setupdata.py ============================================================================== --- py/branch/event/py/test/testing/setupdata.py (original) +++ py/branch/event/py/test/testing/setupdata.py Thu Jul 17 13:44:54 2008 @@ -91,6 +91,19 @@ def test_three(): assert 1 ''')), + ('testevenmore.py', py.code.Source(''' + def test_one(): + assert 1 + + def test_two(): + assert 1 + + def test_three(): + assert 1 + + def test_four(): + assert 1 + ''')), ('testspecial_importerror.py', py.code.Source(''' Modified: py/branch/event/py/test/testing/test_doctest.py ============================================================================== --- py/branch/event/py/test/testing/test_doctest.py (original) +++ py/branch/event/py/test/testing/test_doctest.py Thu Jul 17 13:44:54 2008 @@ -1,25 +1,30 @@ import py -from py.__.test.doctest import DoctestText from py.__.test.outcome import Skipped, Failed, Passed, Outcome + +def setup_module(mod): + mod.tmp = py.test.ensuretemp(__name__) + def test_simple_docteststring(): - testitem = DoctestText(name="dummy", parent=None) - testitem._setcontent(""" - >>> i = 0 - >>> i + 1 - 1 - """) + p = tmp.join("test_simple_docteststring") + p.write(py.code.Source(""" + >>> i = 0 + >>> i + 1 + 1 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) res = testitem.run() assert res is None def test_simple_docteststring_failing(): - testitem = DoctestText(name="dummy2", parent=None) - testitem._setcontent(""" - >>> i = 0 - >>> i + 1 - 2 - """) + p = tmp.join("test_simple_docteststring_failing") + p.write(py.code.Source(""" + >>> i = 0 + >>> i + 1 + 2 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) py.test.raises(Failed, "testitem.run()") @@ -39,6 +44,6 @@ col = config._getcollector(x) items = list(col._tryiter(py.test.collect.Item)) assert len(items) == 1 - assert isinstance(items[0], DoctestText) + assert isinstance(items[0].parent, py.test.collect.DoctestFile) Modified: py/branch/event/py/test/testing/test_session.py ============================================================================== --- py/branch/event/py/test/testing/test_session.py (original) +++ py/branch/event/py/test/testing/test_session.py Thu Jul 17 13:44:54 2008 @@ -118,13 +118,13 @@ assert l[0].item.name == 'test_1' def test_select_starton(self): - config = py.test.config._reparse([datadir/'testmore.py', - '-j', '-k', "test_two"]) + config = py.test.config._reparse([datadir/'testevenmore.py', + '-j', '-k', "test_three"]) all = [] session = config._getsessionclass()(config) session.main(all.append) assert len(getpassed(all)) == 2 - assert len(getskipped(all)) == 1 + assert len(getskipped(all)) == 2 class TestTerminalSession: From hpk at codespeak.net Thu Jul 17 14:27:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 14:27:47 +0200 (CEST) Subject: [py-svn] r56616 - in py/branch/event/py: misc test2 test2/rep test2/terminal Message-ID: <20080717122747.1D72C2A018B@codespeak.net> Author: hpk Date: Thu Jul 17 14:27:45 2008 New Revision: 56616 Removed: py/branch/event/py/test2/terminal/out.py Modified: py/branch/event/py/misc/terminal_helper.py py/branch/event/py/test2/present.py py/branch/event/py/test2/rep/reporter.py py/branch/event/py/test2/terminal/remote.py Log: factor the "Out" and "TerminalOut" classes into the misc terminal_helper.py module. Modified: py/branch/event/py/misc/terminal_helper.py ============================================================================== --- py/branch/event/py/misc/terminal_helper.py (original) +++ py/branch/event/py/misc/terminal_helper.py Thu Jul 17 14:27:45 2008 @@ -1,4 +1,12 @@ +""" + +Helper functions for writing to terminals and files. + +""" + + import sys, os +import py def get_terminal_width(): try: @@ -31,4 +39,94 @@ if flush: file.flush() +class Out(object): + tty = False + def __init__(self, file): + self.file = py.io.dupfile(file) + self.fullwidth = get_terminal_width() + + def sep(self, sepchar, title=None, fullwidth=None): + if not fullwidth: + fullwidth = self.fullwidth + # the goal is to have the line be as long as possible + # under the condition that len(line) <= fullwidth + if title is not None: + # we want 2 + 2*len(fill) + len(title) <= fullwidth + # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth + # 2*len(sepchar)*N <= fullwidth - len(title) - 2 + # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) + N = (fullwidth - len(title) - 2) // (2*len(sepchar)) + fill = sepchar * N + line = "%s %s %s" % (fill, title, fill) + else: + # we want len(sepchar)*N <= fullwidth + # i.e. N <= fullwidth // len(sepchar) + line = sepchar * (fullwidth // len(sepchar)) + # in some situations there is room for an extra sepchar at the right, + # in particular if we consider that with a sepchar like "_ " the + # trailing space is not important at the end of the line + if len(line) + len(sepchar.rstrip()) <= fullwidth: + line += sepchar.rstrip() + self.line(line) + +class TerminalOut(Out): + tty = True + def __init__(self, file): + super(TerminalOut, self).__init__(file) + + def sep(self, sepchar, title=None): + super(TerminalOut, self).sep(sepchar, title, + self.terminal_width) + + def write(self, s): + self.file.write(str(s)) + self.file.flush() + + def line(self, s=''): + if s: + self.file.write(s + '\n') + else: + self.file.write('\n') + self.file.flush() + + def xxxrewrite(self, s=''): + #self.write('\x1b[u%s' % s) - this escape sequence does + # strange things, or nothing at all, on various terminals. + # XXX what is it supposed to do in the first place?? + self.write(s) + +class FileOut(Out): + def write(self, s): + self.file.write(str(s)) + self.file.flush() + + def line(self, s=''): + if s: + self.file.write(str(s) + '\n') + else: + self.file.write('\n') + self.file.flush() + + def xxxrewrite(self, s=''): + self.write(s) + +def getout(file): + # XXX investigate further into terminal output, this is not enough + # + if file is None: + file = py.std.sys.stdout + elif hasattr(file, 'send'): + file = WriteFile(file.send) + elif callable(file): + file = WriteFile(file) + if hasattr(file, 'isatty') and file.isatty(): + return TerminalOut(file) + else: + return FileOut(file) + +class WriteFile(object): + def __init__(self, writemethod): + self.write = writemethod + def flush(self): + return Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Thu Jul 17 14:27:45 2008 @@ -7,7 +7,7 @@ import py from py.__.code import safe_repr -from py.__.test2.terminal.out import getout +from py.__.misc.terminal_helper import getout def getrelpath(source, dest): base = source.common(dest) Modified: py/branch/event/py/test2/rep/reporter.py ============================================================================== --- py/branch/event/py/test2/rep/reporter.py (original) +++ py/branch/event/py/test2/rep/reporter.py Thu Jul 17 14:27:45 2008 @@ -7,7 +7,7 @@ import py -from py.__.test2.terminal.out import getout +from py.__.misc.terminal_helper import getout from py.__.test2 import repevent from py.__.test2 import outcome from py.__.misc.terminal_helper import ansi_print, get_terminal_width Deleted: /py/branch/event/py/test2/terminal/out.py ============================================================================== --- /py/branch/event/py/test2/terminal/out.py Thu Jul 17 14:27:45 2008 +++ (empty file) @@ -1,97 +0,0 @@ -from __future__ import generators -import sys -import os -import py -from py.__.misc import terminal_helper - -class Out(object): - tty = False - fullwidth = terminal_helper.terminal_width - def __init__(self, file): - self.file = py.io.dupfile(file) - - def sep(self, sepchar, title=None, fullwidth=None): - if not fullwidth: - fullwidth = self.fullwidth - # the goal is to have the line be as long as possible - # under the condition that len(line) <= fullwidth - if title is not None: - # we want 2 + 2*len(fill) + len(title) <= fullwidth - # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth - # 2*len(sepchar)*N <= fullwidth - len(title) - 2 - # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) - N = (fullwidth - len(title) - 2) // (2*len(sepchar)) - fill = sepchar * N - line = "%s %s %s" % (fill, title, fill) - else: - # we want len(sepchar)*N <= fullwidth - # i.e. N <= fullwidth // len(sepchar) - line = sepchar * (fullwidth // len(sepchar)) - # in some situations there is room for an extra sepchar at the right, - # in particular if we consider that with a sepchar like "_ " the - # trailing space is not important at the end of the line - if len(line) + len(sepchar.rstrip()) <= fullwidth: - line += sepchar.rstrip() - self.line(line) - -class TerminalOut(Out): - tty = True - def __init__(self, file): - super(TerminalOut, self).__init__(file) - - def sep(self, sepchar, title=None): - super(TerminalOut, self).sep(sepchar, title, - terminal_helper.get_terminal_width()) - - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(s + '\n') - else: - self.file.write('\n') - self.file.flush() - - def rewrite(self, s=''): - #self.write('\x1b[u%s' % s) - this escape sequence does - # strange things, or nothing at all, on various terminals. - # XXX what is it supposed to do in the first place?? - self.write(s) - -class FileOut(Out): - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(str(s) + '\n') - else: - self.file.write('\n') - self.file.flush() - - def rewrite(self, s=''): - self.write(s) - -def getout(file): - # XXX investigate further into terminal output, this is not enough - # - if file is None: - file = py.std.sys.stdout - elif hasattr(file, 'send'): - file = WriteFile(file.send) - elif callable(file): - file = WriteFile(file) - if hasattr(file, 'isatty') and file.isatty(): - return TerminalOut(file) - else: - return FileOut(file) - -class WriteFile(object): - def __init__(self, writemethod): - self.write = writemethod - def flush(self): - return - Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Thu Jul 17 14:27:45 2008 @@ -1,7 +1,7 @@ from __future__ import generators import py from py.__.test2.session import Session -from py.__.test2.terminal.out import getout +from py.__.misc.terminal_helper import getout from py.__.test2.outcome import Failed, Passed, Skipped def checkpyfilechange(rootdir, statcache={}): From hpk at codespeak.net Thu Jul 17 14:48:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 14:48:27 +0200 (CEST) Subject: [py-svn] r56617 - in py/branch/event/py/test2: . rep testing Message-ID: <20080717124827.33E8B168563@codespeak.net> Author: hpk Date: Thu Jul 17 14:48:26 2008 New Revision: 56617 Modified: py/branch/event/py/test2/present.py py/branch/event/py/test2/rep/reporter.py py/branch/event/py/test2/testing/test_present.py Log: move some reporting helpers to the reporter module Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Thu Jul 17 14:48:26 2008 @@ -8,40 +8,7 @@ import py from py.__.code import safe_repr from py.__.misc.terminal_helper import getout - -def getrelpath(source, dest): - base = source.common(dest) - if not base: - return None - # with posix local paths '/' is always a common base - relsource = source.relto(base) - reldest = dest.relto(base) - n = relsource.count(source.sep) - target = dest.sep.join(('..', )*n + (reldest, )) - return target - -def getmodpath(pycolitem): - """ return dotted module path for the given colitem. """ - # XXX what about non-functions? - colitems = pycolitem.listchain() - while colitems: - colitem = colitems.pop(0) - if isinstance(colitem, colitem.Module): - parts = [colitem.obj.__name__] - for colitem in colitems: - if colitem.name[0] in '([': - parts[-1] += colitem.name - else: - parts.append(colitem.name) - return ".".join(parts) - return colitem.name - -def repr_pythonversion(): - v = py.std.sys.version_info - try: - return "%s.%s.%s-%s-%s" % v - except (TypeError, ValueError): - return str(v) +from py.__.test2.rep.reporter import getmodpath class FuncPresenter(object): """ presenting information about failing Functions and Generators. """ Modified: py/branch/event/py/test2/rep/reporter.py ============================================================================== --- py/branch/event/py/test2/rep/reporter.py (original) +++ py/branch/event/py/test2/rep/reporter.py Thu Jul 17 14:48:26 2008 @@ -11,13 +11,45 @@ from py.__.test2 import repevent from py.__.test2 import outcome from py.__.misc.terminal_helper import ansi_print, get_terminal_width -from py.__.test2.present import Presenter, repr_pythonversion,\ - getrelpath import sys from time import time as now +def getrelpath(source, dest): + base = source.common(dest) + if not base: + return None + # with posix local paths '/' is always a common base + relsource = source.relto(base) + reldest = dest.relto(base) + n = relsource.count(source.sep) + target = dest.sep.join(('..', )*n + (reldest, )) + return target + +def getmodpath(pycolitem): + """ return dotted module path for the given colitem. """ + # XXX what about non-functions? + colitems = pycolitem.listchain() + while colitems: + colitem = colitems.pop(0) + if isinstance(colitem, colitem.Module): + parts = [colitem.obj.__name__] + for colitem in colitems: + if colitem.name[0] in '([': + parts[-1] += colitem.name + else: + parts.append(colitem.name) + return ".".join(parts) + return colitem.name + +def repr_pythonversion(): + v = py.std.sys.version_info + try: + return "%s.%s.%s-%s-%s" % v + except (TypeError, ValueError): + return str(v) + def choose_reporter(reporterclass, config): option = config.option if option.startserver or option.runbrowser: Modified: py/branch/event/py/test2/testing/test_present.py ============================================================================== --- py/branch/event/py/test2/testing/test_present.py (original) +++ py/branch/event/py/test2/testing/test_present.py Thu Jul 17 14:48:26 2008 @@ -4,37 +4,6 @@ import suptest, setupdata import re, sys -def test_getmodpath_cases(): - tmpdir = py.test.ensuretemp("test_getmodpath_cases") - pkgdir = tmpdir.join("pkg") - pkgdir.ensure("__init__.py") - nopkgdir = tmpdir.ensure("nopkg", dir=1) - def checkpkg(names, expected): - fcol = setupdata.getexamplecollector(names, tmpdir=pkgdir) - assert present.getmodpath(fcol) == pkgdir.basename + "." + expected - def checknopkg(names, expected): - fcol = setupdata.getexamplecollector(names, tmpdir=nopkgdir) - assert present.getmodpath(fcol) == expected - - for names in ( - 'mod.py test_f1 mod.test_f1', - 'mod.py TestA () test_m1 mod.TestA().test_m1', - 'mod.py test_g1 mod.test_g1', - 'mod.py test_g1 [0] mod.test_g1[0]', - ): - names = names.split() - expected = names.pop() - yield checkpkg, names, expected - yield checknopkg, names, expected - -def test_repr_python_version(): - py.magic.patch(py.std.sys, 'version_info', (2, 5, 1, 'final', 0)) - try: - assert present.repr_pythonversion() == "2.5.1-final-0" - py.std.sys.version_info = x = (2,3) - assert present.repr_pythonversion() == str(x) - finally: - py.magic.revert(py.std.sys, 'version_info') class TestPresenter: def setup_class(cls): From hpk at codespeak.net Thu Jul 17 15:21:25 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 15:21:25 +0200 (CEST) Subject: [py-svn] r56618 - py/trunk/py/code/testing Message-ID: <20080717132125.4A8892A018B@codespeak.net> Author: hpk Date: Thu Jul 17 15:21:23 2008 New Revision: 56618 Modified: py/trunk/py/code/testing/test_source.py Log: skip this test for a more explicit reason Modified: py/trunk/py/code/testing/test_source.py ============================================================================== --- py/trunk/py/code/testing/test_source.py (original) +++ py/trunk/py/code/testing/test_source.py Thu Jul 17 15:21:23 2008 @@ -282,14 +282,17 @@ lines = deindent(source.splitlines()) assert lines == ['', 'def f():', ' def g():', ' pass', ' '] -def test_write_read(): - py.test.skip("Failing") +def test_source_of_class_at_eof_without_newline(): + py.test.skip("circumvent CPython's buggy inspect.getsource?") + # this test fails because the implicit inspect.getsource(A) below + # does not return the "x = 1" last line. tmpdir = py.test.ensuretemp("source_write_read") source = py.code.Source(''' - class A(object): - def method(self): - x = 1 + class A(object): + def method(self): + x = 1 ''') - tmpdir.ensure("a.py").write(source) + path = tmpdir.join("a.py") + path.write(source) s2 = py.code.Source(tmpdir.join("a.py").pyimport().A) - assert source == s2 + assert str(source).strip() == str(s2).strip() From hpk at codespeak.net Thu Jul 17 15:22:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 15:22:37 +0200 (CEST) Subject: [py-svn] r56619 - py/trunk/py/code/testing Message-ID: <20080717132237.4386D2A018B@codespeak.net> Author: hpk Date: Thu Jul 17 15:22:36 2008 New Revision: 56619 Modified: py/trunk/py/code/testing/test_source.py Log: sic Modified: py/trunk/py/code/testing/test_source.py ============================================================================== --- py/trunk/py/code/testing/test_source.py (original) +++ py/trunk/py/code/testing/test_source.py Thu Jul 17 15:22:36 2008 @@ -283,7 +283,7 @@ assert lines == ['', 'def f():', ' def g():', ' pass', ' '] def test_source_of_class_at_eof_without_newline(): - py.test.skip("circumvent CPython's buggy inspect.getsource?") + py.test.skip("CPython's inspect.getsource is buggy") # this test fails because the implicit inspect.getsource(A) below # does not return the "x = 1" last line. tmpdir = py.test.ensuretemp("source_write_read") From hpk at codespeak.net Thu Jul 17 19:07:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 19:07:13 +0200 (CEST) Subject: [py-svn] r56620 - in py/branch/event/py/code: . testing Message-ID: <20080717170713.A5971169F6E@codespeak.net> Author: hpk Date: Thu Jul 17 19:07:10 2008 New Revision: 56620 Added: py/branch/event/py/code/tbpresent.py - copied, changed from r56617, py/branch/event/py/test2/present.py py/branch/event/py/code/testing/test_tbpresent.py - copied, changed from r56617, py/branch/event/py/test2/testing/test_present.py Log: factor out traceback representation logic to py.code, also refactor it and write cleaner tests. Copied: py/branch/event/py/code/tbpresent.py (from r56617, py/branch/event/py/test2/present.py) ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/code/tbpresent.py Thu Jul 17 19:07:10 2008 @@ -1,26 +1,26 @@ - -""" This file intends to gather all methods of representing -failures/tracebacks etc. which should be used among -all terminal-based reporters. This methods should be general, -to allow further use outside the pylib +""" + Present Python tracebacks in a nice way. """ - import py from py.__.code import safe_repr from py.__.misc.terminal_helper import getout -from py.__.test2.rep.reporter import getmodpath -class FuncPresenter(object): +class TBPresenter(object): """ presenting information about failing Functions and Generators. """ - def __init__(self, config, out=None): - self.config = config + # for traceback entries + flow_marker = ">" + fail_marker = "E" + + def __init__(self, out=None, showlocals=False, style="long"): if out is None: self.stringio = py.std.StringIO.StringIO() out = getout(self.stringio) assert hasattr(out, 'write'), out self.out = out + self.showlocals = showlocals + self.style = style - def repr_source(self, source, marker=">", marker_location=-1): + def repr_source(self, source, marker_location=-1): """ This one represents piece of source with possible marker at requested position """ @@ -37,18 +37,11 @@ marker_location = len(source) - 1 for i in range(len(source)): if i == marker_location: - prefix = marker + " " + prefix = self.flow_marker + " " else: prefix = " " self.out.line(prefix + source[i]) - def repr_failure_headline(self, item): - """ This method represents py.test2.collect.Item info (path and module) - """ - # XXX do something for non-python test items - modpath = getmodpath(item) - self.out.sep("_", "entrypoint: %s" %(modpath)) - def repr_failure_explanation(self, excinfo, source): try: s = str(source.getstatement(len(source)-1)) @@ -63,7 +56,7 @@ indent = " " * indent # get the real exception information out lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line('E' + indent[:-1] + lines.pop(0)) + self.out.line(self.fail_marker + indent[:-1] + lines.pop(0)) for x in lines: self.out.line(indent + x) @@ -71,11 +64,11 @@ try: source = entry.getsource() except py.error.ENOENT: - source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) + source = "???" return source.deindent() def repr_locals(self, f_locals): - if self.config.option.showlocals: + if self.showlocals: self.out.sep('- ', 'locals') for name, value in f_locals.items(): if name == '__builtins__': @@ -91,85 +84,44 @@ self.out.line("%-10s =\\" % (name,)) py.std.pprint.pprint(value, stream=self.out) - def repr_failure(self, item, excinfo): - self.repr_failure_headline(item) - self.repr_tb(item, excinfo) - - def repr_out_err(self, colitem): - for parent in colitem.listchain(): - if hasattr(parent, '_run_capture'): - name, obj = parent._run_capture - if obj: - self.out.sep("- ", "%s: recorded std%s" % (parent.name, name)) - self.out.line(obj) - def repr_tb_entry(self, entry, excinfo=None): - # excinfo is None if this is the last tb entry + # excinfo is not None if this is the last tb entry source = self.getentrysource(entry) firstsourceline = entry.getfirstlinesource() marker_location = entry.lineno - firstsourceline - self.repr_source(source, '>', marker_location) - if excinfo: - self.repr_failure_explanation(excinfo, source) - self.out.line("") - self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) - self.repr_locals(entry.locals) - - def repr_tb_entry_last(self, item, entry, excinfo): - self.repr_tb_entry(entry, excinfo) - self.repr_out_err(item) - def repr_tb(self, item, excinfo): - traceback = item.prunetraceback(excinfo.traceback) - recursionindex = traceback.recursionindex() - repr_tb = getattr(self, "repr_tb_" + self.config.option.tbstyle) - repr_tb(item, excinfo, traceback, recursionindex) + if self.style == "long": + self.repr_source(source, marker_location) + if excinfo: + self.repr_failure_explanation(excinfo, source) + self.out.line("") + self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) + self.repr_locals(entry.locals) + else: + if self.style == "short": + line = source[marker_location].lstrip() + self.out.line(' File "%s", line %d, in %s' % ( + entry.path.basename, entry.lineno+1, entry.name)) + self.out.line(" " + line) + if excinfo: + self.repr_exconly(excinfo, indent=4) + + def repr_sep(self, sep): + if self.style == "long": + self.out.sep(sep) - def repr_tb_long(self, item, excinfo, traceback, recursionindex): + def repr_tb(self, excinfo): + traceback = excinfo.traceback + recursionindex = traceback.recursionindex() last = traceback[-1] for index, entry in py.builtin.enumerate(traceback): if last != entry: self.repr_tb_entry(entry) - self.out.sep("_ ") + self.repr_sep("_ ") else: - self.repr_tb_entry_last(item, entry, excinfo) - self.out.sep("_") + self.repr_tb_entry(entry, excinfo) + self.repr_sep("_") if index == recursionindex: self.out.line("Recursion detected (same locals & position)") self.out.sep("!") break - - def repr_tb_short(self, item, excinfo, traceback, recursionindex): - # XXX refactor - self.out.line() - last = traceback[-1] - for index, entry in py.builtin.enumerate(traceback): - path = entry.path.basename - firstsourceline = entry.getfirstlinesource() - relline = entry.lineno - firstsourceline - self.out.line(' File "%s", line %d, in %s' % ( - path, entry.lineno+1, entry.name)) - try: - source = entry.getsource().lines - source = source[relline].lstrip() - except (IndexError, py.error.ENOENT): - source = None - else: - self.out.line(" " + source) - if entry == last: - self.repr_exconly(excinfo, indent=4) - self.repr_locals(entry.locals) - - # trailing info - if entry == last: - self.repr_out_err(item) - self.out.sep("_") - else: - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break - - # the following is only used by the combination '--pdb --tb=no' - repr_tb_no = repr_tb_short - Copied: py/branch/event/py/code/testing/test_tbpresent.py (from r56617, py/branch/event/py/test2/testing/test_present.py) ============================================================================== --- py/branch/event/py/test2/testing/test_present.py (original) +++ py/branch/event/py/code/testing/test_tbpresent.py Thu Jul 17 19:07:10 2008 @@ -1,36 +1,27 @@ import py -from py.__.test2 import present, repevent -import suptest, setupdata -import re, sys +import re +from py.__.code.tbpresent import TBPresenter - -class TestPresenter: +class TestTracebackPresenter: def setup_class(cls): - cls.tmpdir = py.test2.ensuretemp(cls.__name__) + cls.clstmpdir = py.test2.ensuretemp(cls.__name__) - def getpresenter(self, cmdlinearg=None): - args = [self.tmpdir] - if cmdlinearg: - args.append(cmdlinearg) - config = py.test2.config._reparse(args) - return present.FuncPresenter(config) + def setup_method(self, method): + self.tmpdir = self.clstmpdir.ensure(method.__name__, dir=1) - def gentest(self, check, **options): - print "using config options", options - p = self.getpresenter() - for name, value in options.items(): - setattr(p.config.option, name, value) - check(p) + def getpresenter(self, **kwargs): + return TBPresenter(**kwargs) def test_repr_source(self): + pr = self.getpresenter() source = py.code.Source(""" def f(x): pass """).strip() - p = self.getpresenter() - p.repr_source(source, "|", 0) - lines = p.stringio.getvalue().split("\n") + pr.flow_marker = "|" + pr.repr_source(source, 0) + lines = pr.stringio.getvalue().split("\n") assert len(lines) == 3 assert lines[0].startswith("|") assert lines[0].find("def f(x)") != -1 @@ -47,134 +38,152 @@ e = py.code.ExceptionInfo() return e - p = self.getpresenter() + pr = self.getpresenter() source = py.code.Source(f) e = f() - p.repr_failure_explanation(e, source) - assert p.stringio.getvalue().startswith("E ") + pr.repr_failure_explanation(e, source) + assert pr.stringio.getvalue().startswith("E ") def test_repr_local(self): - p = self.getpresenter("--showlocals") + p = self.getpresenter(showlocals=True) loc = locals() p.repr_locals(loc) result = p.stringio.getvalue() for key in loc.keys(): assert result.find(key) != -1 - def test_repr_failure_simple(self): - item, excinfo = suptest.getfailing(""" - def test_one(): - # failingsourcemarker - assert 42 == 43 + def importasmod(self, source): + source = py.code.Source(source) + modpath = self.tmpdir.join("mod.py") + self.tmpdir.ensure("__init__.py") + modpath.write(source) + return modpath.pyimport() + + def test_repr_tbentry(self): + mod = self.importasmod(""" + def func1(): + raise ValueError("hello") + def entry(): + func1() """) + excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter() - p.repr_failure(item, excinfo) + p.repr_tb_entry(excinfo.traceback[-2]) s = p.stringio.getvalue() - print s - assert re.search("entrypoint:.*\.test_one", s) - assert s.find("# failingsourcemarker") != -1 - assert re.search(r"E.*assert 42 == 43", s) - assert re.search(r">.*assert 42 == 43", s) + lines = s.split("\n") - def test_repr_failure_recursive_funcs(self): - item, excinfo = suptest.getfailing(""" - def rec2(x): - return rec1(x+1) - def rec1(x): - return rec2(x-1) - def test_one(): - rec1(42) - """) - def check(p): - p.repr_failure(item, excinfo) - s = p.stringio.getvalue() - print s - assert re.search(".*return rec1.*x\+1", s) - assert re.search(".*return rec2.*x-1.*", s) - assert re.search("Recursion detected", s) + # test intermittent entries - self.gentest(check, fulltrace=True) - self.gentest(check, fulltrace=False) - self.gentest(check, tbstyle='short') - self.gentest(check, showlocals=True) - - def test_repr_failing_setup(self): - item, excinfo = suptest.getfailing(""" - def setup_module(mod): - xyz - def test_one(): - pass - """) - def check(p): - p.repr_failure(item, excinfo) - s = p.stringio.getvalue() - print s - assert re.search(" xyz", s) - assert re.search("NameError:.*xyz", s) - - self.gentest(check, tbstyle="short") - self.gentest(check, fulltrace=True) - self.gentest(check, showlocals=True) - - def test_repr_failing_generator_itself(self): - sorter = suptest.events_from_runsource(""" - def test_gen(): - def check(x): - assert x - xyz - yield check, 0 - """) - l = sorter.getfailedcollections() - assert len(l) == 1 - ev = l[0] - print ev - s = ev.collector.repr_failure(ev.excinfo) + assert lines[0] == " def entry():" + assert lines[1] == "> func1()" + assert not lines[2] + assert lines[3] == "[%s:5]" %(mod.__file__,) + + # test last entry + p = self.getpresenter() + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + s = p.stringio.getvalue() + lines = s.split("\n") print s - i = s.find("def ") - assert i != -1 - assert s[i:].startswith("def test_gen():") - assert s.find("NameError: ") != -1 - - def test_repr_failing_generated_test(self): - item, excinfo = suptest.getfailing(""" - def test_gen(): - def check(x): - assert x - yield check, 0 - """) - s = item.repr_failure(excinfo) + assert lines[0] == " def func1():" + assert lines[1] == '> raise ValueError("hello")' + assert lines[2] == "E ValueError: hello" + assert not lines[3] + assert lines[4] == "[%s:3]" %(mod.__file__,) + + + def test_repr_tbentry_short(self): + mod = self.importasmod(""" + def func1(): + raise ValueError("hello") + def entry(): + func1() + """) + excinfo = py.test.raises(ValueError, mod.entry) + p = self.getpresenter(style="short") + p.repr_tb_entry(excinfo.traceback[-2]) + s = p.stringio.getvalue() + lines = s.split("\n") + basename = py.path.local(mod.__file__).basename + assert lines[0] == ' File "%s", line 5, in entry' % basename + assert lines[1] == ' func1()' + assert not lines[2] + + # test last entry print s + p = self.getpresenter(style="short") + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + s = p.stringio.getvalue() lines = s.split("\n") - assert lines[2].find("test_gen[0] -> check(0,)") != -1 - assert lines[3].find("def check(x):") != -1 - item._config.option.fulltrace = True - s = item.repr_failure(excinfo) + assert lines[0] == ' File "%s", line 3, in func1' % basename + assert lines[1] == ' raise ValueError("hello")' + assert lines[2] == 'E ValueError: hello' + assert not lines[3] + + def test_repr_tbentry_no(self): + mod = self.importasmod(""" + def func1(): + raise ValueError("hello") + def entry(): + func1() + """) + excinfo = py.test.raises(ValueError, mod.entry) + p = self.getpresenter(style="no") + p.repr_tb_entry(excinfo.traceback[-2]) + s = p.stringio.getvalue() + assert not s + p = self.getpresenter(style="no") + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + s = p.stringio.getvalue() lines = s.split("\n") - assert lines[2].find("test_gen[0] -> check(0,)") == -1 + assert lines[0] == 'E ValueError: hello' - def test_repr_tb_short(self): - item, excinfo = suptest.getfailing(""" + + def test_repr_tb(self): + mod = self.importasmod(""" def f(x): - assert x - def test_f(): + raise ValueError(x) + def entry(): f(0) """) - item._config.option.tbstyle = "short" - s = item.repr_failure(excinfo) - print s - index = -1 - basename = item.fspath.basename - lines = s.split("\n")[3:] - for line in ( - ' File "%s", line 5, in test_f' % basename, - ' f(0)', - ' File "%s", line 3, in f' % basename, - ' assert x', - 'E assert 0' - ): - actual = lines.pop(0) - actual = actual.rstrip() - if line != actual: - print "expected:", repr(line) - print "got :", repr(actual) - assert 0 + p = self.getpresenter(style="short") + excinfo = py.test.raises(ValueError, mod.entry) + l = [] + p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) + p.repr_sep = lambda sep: l.append(sep) + s = p.repr_tb(excinfo) + print l + assert l[-1] == "_" + entry, excinfo2 = l[-2] + assert excinfo == excinfo2 + assert entry == excinfo.traceback[-1] + assert l[-3] == "_ " + entry, excinfo2 = l[-4] + assert excinfo2 is None + assert entry == excinfo.traceback[-2] + + def test_repr_tb_recursion(self): + mod = self.importasmod(""" + def rec2(x): + return rec1(x+1) + def rec1(x): + return rec2(x-1) + def entry(): + rec1(42) + """) + excinfo = py.test.raises(RuntimeError, mod.entry) + + for style in ("short", "long"): + p = self.getpresenter(style="short") + l = [] + p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) + p.repr_sep = lambda sep: l.append(sep) + p.repr_tb(excinfo) + s = p.stringio.getvalue() + print l + #assert re.search(".*return rec1.*x\+1", s) + #assert re.search(".*return rec2.*x-1.*", s) + assert len(l) < 20 + assert re.search("Recursion detected", s) + assert l[-1] == "_ " + assert l[-2][0].lineno == 4 From hpk at codespeak.net Thu Jul 17 20:31:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 20:31:03 +0200 (CEST) Subject: [py-svn] r56622 - py/branch/event/py/test2/rep/testing Message-ID: <20080717183103.83AC669804E@codespeak.net> Author: hpk Date: Thu Jul 17 20:31:01 2008 New Revision: 56622 Added: py/branch/event/py/test2/rep/testing/test_reporter_helper.py Log: adding some previously factored out tests Added: py/branch/event/py/test2/rep/testing/test_reporter_helper.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rep/testing/test_reporter_helper.py Thu Jul 17 20:31:01 2008 @@ -0,0 +1,37 @@ + +import py + +from py.__.test2.rep.reporter import getmodpath, repr_pythonversion +from py.__.test2.testing import setupdata + +def test_getmodpath_cases(): + tmpdir = py.test.ensuretemp("test_getmodpath_cases") + pkgdir = tmpdir.join("pkg") + pkgdir.ensure("__init__.py") + nopkgdir = tmpdir.ensure("nopkg", dir=1) + def checkpkg(names, expected): + fcol = setupdata.getexamplecollector(names, tmpdir=pkgdir) + assert getmodpath(fcol) == pkgdir.basename + "." + expected + def checknopkg(names, expected): + fcol = setupdata.getexamplecollector(names, tmpdir=nopkgdir) + assert getmodpath(fcol) == expected + + for names in ( + 'mod.py test_f1 mod.test_f1', + 'mod.py TestA () test_m1 mod.TestA().test_m1', + 'mod.py test_g1 mod.test_g1', + 'mod.py test_g1 [0] mod.test_g1[0]', + ): + names = names.split() + expected = names.pop() + yield checkpkg, names, expected + yield checknopkg, names, expected + +def test_repr_python_version(): + py.magic.patch(py.std.sys, 'version_info', (2, 5, 1, 'final', 0)) + try: + assert repr_pythonversion() == "2.5.1-final-0" + py.std.sys.version_info = x = (2,3) + assert repr_pythonversion() == str(x) + finally: + py.magic.revert(py.std.sys, 'version_info') From hpk at codespeak.net Thu Jul 17 20:35:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 20:35:00 +0200 (CEST) Subject: [py-svn] r56623 - py/branch/event/py/test2/testing Message-ID: <20080717183500.78E42168495@codespeak.net> Author: hpk Date: Thu Jul 17 20:34:59 2008 New Revision: 56623 Removed: py/branch/event/py/test2/testing/test_remote.py Modified: py/branch/event/py/test2/testing/test_session.py Log: merge test files, clean up Deleted: /py/branch/event/py/test2/testing/test_remote.py ============================================================================== --- /py/branch/event/py/test2/testing/test_remote.py Thu Jul 17 20:34:59 2008 +++ (empty file) @@ -1,58 +0,0 @@ -import py - -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - -class Test_Remote_Functional: - # XXX for testing features that implicitely start new - # we want to get access to the remote event log - # and be able to access and assert about it it from the - # tests below - def test_exec(self): - o = tmpdir.ensure('remote', dir=1) - tfile = o.join('test_exec.py') - tfile.write(py.code.Source(""" - def test_1(): - assert 1 == 0 - """)) - print py.std.sys.executable - config = py.test2.config._reparse( - ['--exec=' + py.std.sys.executable, - o]) - session = config.initsession() - failures = session.main() - assert failures - py.test.skip("extend this test to test that --exec really honores its argument") - - def test_looponfailing(self): - py.test.skip("xxx check events") - o = tmpdir.ensure('looponfailing', dir=1) - tfile = o.join('test_looponfailing.py') - tfile.write(py.code.Source(""" - def test_1(): - assert 1 == 0 - """)) - print py.std.sys.executable - config = py.test2.config._reparse(['--looponfailing', str(o)]) - session = config.initsession() - failures = session.main() - - cls = config._getsessionclass() - out = py.std.Queue.Queue() - session = cls(config, out.put) - pool = py._thread.WorkerPool() - reply = pool.dispatch(session.main) - while 1: - s = out.get(timeout=5.0) - if s.find('1 failed') != -1: - break - print s - else: - py.test2.fail("did not see test_1 failure") - # XXX we would like to have a cleaner way to finish - try: - reply.get(timeout=5.0) - except IOError, e: - assert str(e).lower().find('timeout') != -1 - - Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Thu Jul 17 20:34:59 2008 @@ -275,3 +275,53 @@ assert rep.repr_failure == "custom_repr test_one" rep = sorter.getreport("test_method_one") assert rep.repr_failure == "custom_repr test_method_one" + +from py.__.test2.terminal.remote import RemoteTerminalSession + +class Test_TerminalRemoteSession: + def test_exec_leads_to_RemoteTerminalSession(self): + executable = py.std.sys.executable + config = py.test2.config._reparse(['--exec=' + executable]) + print config + session = config.initsession() + assert isinstance(session, RemoteTerminalSession) + assert session.executable == executable + + def test_looponfailing_leads_to_RemoteTerminalSession(self): + config = py.test2.config._reparse(['--looponfailing']) + session = config.initsession() + assert isinstance(session, RemoteTerminalSession) + + def test_looponfailing_loops(self): + py.test.skip("thought needed to nicely test --looponfailing") + py.test.skip("xxx check events") + o = tmpdir.ensure('looponfailing', dir=1) + tfile = o.join('test_looponfailing.py') + tfile.write(py.code.Source(""" + def test_1(): + assert 1 == 0 + """)) + print py.std.sys.executable + config = py.test2.config._reparse(['--looponfailing', str(o)]) + session = config.initsession() + failures = session.main() + + cls = config._getsessionclass() + out = py.std.Queue.Queue() + session = cls(config, out.put) + pool = py._thread.WorkerPool() + reply = pool.dispatch(session.main) + while 1: + s = out.get(timeout=5.0) + if s.find('1 failed') != -1: + break + print s + else: + py.test2.fail("did not see test_1 failure") + # XXX we would like to have a cleaner way to finish + try: + reply.get(timeout=5.0) + except IOError, e: + assert str(e).lower().find('timeout') != -1 + + From hpk at codespeak.net Thu Jul 17 21:22:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 17 Jul 2008 21:22:10 +0200 (CEST) Subject: [py-svn] r56624 - in py/branch/event/py: code code/testing test2 test2/testing Message-ID: <20080717192210.A5FB8169EC2@codespeak.net> Author: hpk Date: Thu Jul 17 21:22:08 2008 New Revision: 56624 Modified: py/branch/event/py/code/tbpresent.py py/branch/event/py/code/testing/test_tbpresent.py py/branch/event/py/test2/item.py py/branch/event/py/test2/present.py py/branch/event/py/test2/testing/test_present.py Log: strip down and separate presentation logic for python functions which now is a thin wrapper/uses py/code/tbpresent functionality. Modified: py/branch/event/py/code/tbpresent.py ============================================================================== --- py/branch/event/py/code/tbpresent.py (original) +++ py/branch/event/py/code/tbpresent.py Thu Jul 17 21:22:08 2008 @@ -67,6 +67,9 @@ source = "???" return source.deindent() + def _saferepr(self, obj): + return safe_repr._repr(obj) + def repr_locals(self, f_locals): if self.showlocals: self.out.sep('- ', 'locals') Modified: py/branch/event/py/code/testing/test_tbpresent.py ============================================================================== --- py/branch/event/py/code/testing/test_tbpresent.py (original) +++ py/branch/event/py/code/testing/test_tbpresent.py Thu Jul 17 21:22:08 2008 @@ -187,3 +187,26 @@ assert re.search("Recursion detected", s) assert l[-1] == "_ " assert l[-2][0].lineno == 4 + + def test_tb_entry_AssertionError(self): + # probably this test is a bit redundant + # as py/magic/testing/test_assertion.py + # already tests correctness of + # assertion-reinterpretation logic + mod = self.importasmod(""" + def somefunc(): + x = 1 + assert x == 2 + """) + py.magic.invoke(assertion=True) + try: + excinfo = py.test.raises(AssertionError, mod.somefunc) + finally: + py.magic.revoke(assertion=True) + + p = self.getpresenter() + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + s = p.stringio.getvalue() + print s + lines = s.split("\n") + assert lines[-4] == "E assert 1 == 2" Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Thu Jul 17 21:22:08 2008 @@ -56,14 +56,6 @@ def repr_failure(self, excinfo): """ return a textual failure representation for this item. """ - p = present.FuncPresenter(self._config) - p.repr_failure_headline(self) - p.out.line("") - if self.name[-1] == "]": # print extra info for generated tests - if not self._config.option.fulltrace: - args = present.safe_repr._repr(self._args) - line = "%s%s -> %s%s" %(self.parent.name, self.name, - self.obj.__name__, args) - p.out.line(line) - p.repr_tb(self, excinfo) + p = present.PythonFunctionPresenter(self._config, self) + p.repr_failure(excinfo) return p.stringio.getvalue() Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Thu Jul 17 21:22:08 2008 @@ -1,101 +1,38 @@ -""" This file intends to gather all methods of representing -failures/tracebacks etc. which should be used among -all terminal-based reporters. This methods should be general, -to allow further use outside the pylib +""" XXX this file serves as a reminder about some remaining + reporting issues that are not handled by the new py/code/tbpresenter """ import py -from py.__.code import safe_repr -from py.__.misc.terminal_helper import getout + +from py.__.code.tbpresent import TBPresenter + from py.__.test2.rep.reporter import getmodpath -class FuncPresenter(object): +class PythonFunctionPresenter(TBPresenter): """ presenting information about failing Functions and Generators. """ - def __init__(self, config, out=None): - self.config = config - if out is None: - self.stringio = py.std.StringIO.StringIO() - out = getout(self.stringio) - assert hasattr(out, 'write'), out - self.out = out - - def repr_source(self, source, marker=">", marker_location=-1): - """ This one represents piece of source with possible - marker at requested position - """ - if source is None: - self.out.line("???") - return - if isinstance(source, str): - source = source.split("\n") - if marker_location < 0: - marker_location += len(source) - if marker_location < 0: - marker_location = 0 - if marker_location >= len(source): - marker_location = len(source) - 1 - for i in range(len(source)): - if i == marker_location: - prefix = marker + " " - else: - prefix = " " - self.out.line(prefix + source[i]) - - def repr_failure_headline(self, item): - """ This method represents py.test2.collect.Item info (path and module) - """ - # XXX do something for non-python test items + def __init__(self, config, item): + self._config = config + self._item = item + super(PythonFunctionPresenter, self).__init__( + showlocals=config.option.showlocals, + style=config.option.tbstyle, + ) + + def repr_header(self): + item = self._item modpath = getmodpath(item) self.out.sep("_", "entrypoint: %s" %(modpath)) + self.out.line("") + if item.name[-1] == "]": # print extra info for generated tests + if not self._config.option.fulltrace: + args = self._saferepr(item._args) + line = "%s%s -> %s%s" %(item.parent.name, item.name, + item.obj.__name__, args) + self.out.line(line) - def repr_failure_explanation(self, excinfo, source): - try: - s = str(source.getstatement(len(source)-1)) - except KeyboardInterrupt: - raise - except: - s = str(source[-1]) - indent = 4 + (len(s) - len(s.lstrip())) - self.repr_exconly(excinfo, indent=indent) - - def repr_exconly(self, excinfo, indent): - indent = " " * indent - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line('E' + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) - - def getentrysource(self, entry): - try: - source = entry.getsource() - except py.error.ENOENT: - source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) - return source.deindent() - - def repr_locals(self, f_locals): - if self.config.option.showlocals: - self.out.sep('- ', 'locals') - for name, value in f_locals.items(): - if name == '__builtins__': - self.out.line("__builtins__ = ") - else: - # This formatting could all be handled by the _repr() function, which is - # only repr.Repr in disguise, so is very configurable. - str_repr = safe_repr._repr(value) - if len(str_repr) < 70 or not isinstance(value, - (list, tuple, dict)): - self.out.line("%-10s = %s" %(name, str_repr)) - else: - self.out.line("%-10s =\\" % (name,)) - py.std.pprint.pprint(value, stream=self.out) - - def repr_failure(self, item, excinfo): - self.repr_failure_headline(item) - self.repr_tb(item, excinfo) - - def repr_out_err(self, colitem): + def repr_footer(self): + colitem = self._item for parent in colitem.listchain(): if hasattr(parent, '_run_capture'): name, obj = parent._run_capture @@ -103,73 +40,8 @@ self.out.sep("- ", "%s: recorded std%s" % (parent.name, name)) self.out.line(obj) - def repr_tb_entry(self, entry, excinfo=None): - # excinfo is None if this is the last tb entry - source = self.getentrysource(entry) - firstsourceline = entry.getfirstlinesource() - marker_location = entry.lineno - firstsourceline - self.repr_source(source, '>', marker_location) - if excinfo: - self.repr_failure_explanation(excinfo, source) - self.out.line("") - self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) - self.repr_locals(entry.locals) - - def repr_tb_entry_last(self, item, entry, excinfo): - self.repr_tb_entry(entry, excinfo) - self.repr_out_err(item) - - def repr_tb(self, item, excinfo): - traceback = item.prunetraceback(excinfo.traceback) - recursionindex = traceback.recursionindex() - repr_tb = getattr(self, "repr_tb_" + self.config.option.tbstyle) - repr_tb(item, excinfo, traceback, recursionindex) - - def repr_tb_long(self, item, excinfo, traceback, recursionindex): - last = traceback[-1] - for index, entry in py.builtin.enumerate(traceback): - if last != entry: - self.repr_tb_entry(entry) - self.out.sep("_ ") - else: - self.repr_tb_entry_last(item, entry, excinfo) - self.out.sep("_") - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break - - def repr_tb_short(self, item, excinfo, traceback, recursionindex): - # XXX refactor - self.out.line() - last = traceback[-1] - for index, entry in py.builtin.enumerate(traceback): - path = entry.path.basename - firstsourceline = entry.getfirstlinesource() - relline = entry.lineno - firstsourceline - self.out.line(' File "%s", line %d, in %s' % ( - path, entry.lineno+1, entry.name)) - try: - source = entry.getsource().lines - source = source[relline].lstrip() - except (IndexError, py.error.ENOENT): - source = None - else: - self.out.line(" " + source) - if entry == last: - self.repr_exconly(excinfo, indent=4) - self.repr_locals(entry.locals) - - # trailing info - if entry == last: - self.repr_out_err(item) - self.out.sep("_") - else: - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break - - # the following is only used by the combination '--pdb --tb=no' - repr_tb_no = repr_tb_short - + def repr_failure(self, excinfo): + self.repr_header() + excinfo.traceback = self._item.prunetraceback(excinfo.traceback) + self.repr_tb(excinfo) + self.repr_footer() Modified: py/branch/event/py/test2/testing/test_present.py ============================================================================== --- py/branch/event/py/test2/testing/test_present.py (original) +++ py/branch/event/py/test2/testing/test_present.py Thu Jul 17 21:22:08 2008 @@ -1,10 +1,8 @@ - import py from py.__.test2 import present, repevent import suptest, setupdata import re, sys - class TestPresenter: def setup_class(cls): cls.tmpdir = py.test2.ensuretemp(cls.__name__) @@ -16,126 +14,7 @@ config = py.test2.config._reparse(args) return present.FuncPresenter(config) - def gentest(self, check, **options): - print "using config options", options - p = self.getpresenter() - for name, value in options.items(): - setattr(p.config.option, name, value) - check(p) - - def test_repr_source(self): - source = py.code.Source(""" - def f(x): - pass - """).strip() - p = self.getpresenter() - p.repr_source(source, "|", 0) - lines = p.stringio.getvalue().split("\n") - assert len(lines) == 3 - assert lines[0].startswith("|") - assert lines[0].find("def f(x)") != -1 - assert lines[1].find("pass") != -1 - - def test_repr_failure_explanation(self): - """ check if indentation is right """ - def f(): - def g(): - 1/0 - try: - g() - except: - e = py.code.ExceptionInfo() - return e - - p = self.getpresenter() - source = py.code.Source(f) - e = f() - p.repr_failure_explanation(e, source) - assert p.stringio.getvalue().startswith("E ") - - def test_repr_local(self): - p = self.getpresenter("--showlocals") - loc = locals() - p.repr_locals(loc) - result = p.stringio.getvalue() - for key in loc.keys(): - assert result.find(key) != -1 - - def test_repr_failure_simple(self): - item, excinfo = suptest.getfailing(""" - def test_one(): - # failingsourcemarker - assert 42 == 43 - """) - p = self.getpresenter() - p.repr_failure(item, excinfo) - s = p.stringio.getvalue() - print s - assert re.search("entrypoint:.*\.test_one", s) - assert s.find("# failingsourcemarker") != -1 - assert re.search(r"E.*assert 42 == 43", s) - assert re.search(r">.*assert 42 == 43", s) - - def test_repr_failure_recursive_funcs(self): - item, excinfo = suptest.getfailing(""" - def rec2(x): - return rec1(x+1) - def rec1(x): - return rec2(x-1) - def test_one(): - rec1(42) - """) - def check(p): - p.repr_failure(item, excinfo) - s = p.stringio.getvalue() - print s - assert re.search(".*return rec1.*x\+1", s) - assert re.search(".*return rec2.*x-1.*", s) - assert re.search("Recursion detected", s) - - self.gentest(check, fulltrace=True) - self.gentest(check, fulltrace=False) - self.gentest(check, tbstyle='short') - self.gentest(check, showlocals=True) - - def test_repr_failing_setup(self): - item, excinfo = suptest.getfailing(""" - def setup_module(mod): - xyz - def test_one(): - pass - """) - def check(p): - p.repr_failure(item, excinfo) - s = p.stringio.getvalue() - print s - assert re.search(" xyz", s) - assert re.search("NameError:.*xyz", s) - - self.gentest(check, tbstyle="short") - self.gentest(check, fulltrace=True) - self.gentest(check, showlocals=True) - - def test_repr_failing_generator_itself(self): - sorter = suptest.events_from_runsource(""" - def test_gen(): - def check(x): - assert x - xyz - yield check, 0 - """) - l = sorter.getfailedcollections() - assert len(l) == 1 - ev = l[0] - print ev - s = ev.collector.repr_failure(ev.excinfo) - print s - i = s.find("def ") - assert i != -1 - assert s[i:].startswith("def test_gen():") - assert s.find("NameError: ") != -1 - - def test_repr_failing_generated_test(self): + def test_repr_pruning_tb_generated_test(self): item, excinfo = suptest.getfailing(""" def test_gen(): def check(x): @@ -145,6 +24,7 @@ s = item.repr_failure(excinfo) print s lines = s.split("\n") + assert lines[0].find("test_0.test_0.test_gen[0]") != -1 assert lines[2].find("test_gen[0] -> check(0,)") != -1 assert lines[3].find("def check(x):") != -1 item._config.option.fulltrace = True @@ -153,6 +33,7 @@ assert lines[2].find("test_gen[0] -> check(0,)") == -1 def test_repr_tb_short(self): + # XXX probably a redundant test item, excinfo = suptest.getfailing(""" def f(x): assert x @@ -164,7 +45,7 @@ print s index = -1 basename = item.fspath.basename - lines = s.split("\n")[3:] + lines = s.split("\n")[2:] for line in ( ' File "%s", line 5, in test_f' % basename, ' f(0)', From hpk at codespeak.net Fri Jul 18 00:19:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 00:19:32 +0200 (CEST) Subject: [py-svn] r56628 - in py/branch/event/py/test2: . testing Message-ID: <20080717221932.7F7F5169F14@codespeak.net> Author: hpk Date: Fri Jul 18 00:19:31 2008 New Revision: 56628 Modified: py/branch/event/py/test2/executor.py py/branch/event/py/test2/present.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_present.py py/branch/event/py/test2/testing/test_session.py Log: * remove redundant ItemFinish event, * now there is only the fully serializable ItemTestReport which gets filled directly at test execution time. Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Fri Jul 18 00:19:31 2008 @@ -31,8 +31,6 @@ finally: outerr = capture.reset() self.testrep.iocapture_run = outerr - # XXX - self.item._run_capture = outerr def _fillreport(self, excinfo): testrep = self.testrep @@ -52,7 +50,6 @@ excinfo = py.code.ExceptionInfo() if excinfo.errisinstance(Failed) and excinfo.value.excinfo: excinfo = e.excinfo - self.config.bus.notify(repevent.ItemFinish(self.item, excinfo)) self._fillreport(excinfo) if self.config.option.usepdb and not excinfo.errisinstance(Skipped): py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Fri Jul 18 00:19:31 2008 @@ -33,6 +33,7 @@ def repr_footer(self): colitem = self._item + # XXX io capturing is stored with the itemtestreport for parent in colitem.listchain(): if hasattr(parent, '_run_capture'): name, obj = parent._run_capture Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Fri Jul 18 00:19:31 2008 @@ -47,16 +47,6 @@ self.item = item self.time = timestamp() -from outcome import Skipped -class ItemFinish(BaseEvent): - def __init__(self, item, excinfo=None): - self.item = item - self.time = timestamp() - self.excinfo = excinfo - self.passed = not excinfo - self.skipped = excinfo and excinfo.errisinstance(Skipped) - self.failed = not (self.passed or self.skipped) - # ---------------------------------------------------------------------- # Report of the run of a single test (might come from a disttributed run) # ---------------------------------------------------------------------- Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Fri Jul 18 00:19:31 2008 @@ -118,13 +118,11 @@ p.dirpath("__init__.py").ensure() return p -def getfailing(source): +def getItemTestReport(source, tb="long"): tfile = makeuniquepyfile(source) - sorter = events_from_cmdline([tfile]) + sorter = events_from_cmdline([tfile, "--tb=%s" %tb]) # get failure base info - failevents = sorter.get(repevent.ItemFinish) + failevents = sorter.get(repevent.ItemTestReport) assert len(failevents) == 1 - item = failevents[0].item - excinfo = failevents[0].excinfo - return item, excinfo + return failevents[0],tfile Modified: py/branch/event/py/test2/testing/test_present.py ============================================================================== --- py/branch/event/py/test2/testing/test_present.py (original) +++ py/branch/event/py/test2/testing/test_present.py Fri Jul 18 00:19:31 2008 @@ -15,36 +15,31 @@ return present.FuncPresenter(config) def test_repr_pruning_tb_generated_test(self): - item, excinfo = suptest.getfailing(""" + itemtestreport,fn = suptest.getItemTestReport(""" def test_gen(): def check(x): assert x yield check, 0 """) - s = item.repr_failure(excinfo) + s = itemtestreport.repr_failure print s lines = s.split("\n") assert lines[0].find("test_0.test_0.test_gen[0]") != -1 assert lines[2].find("test_gen[0] -> check(0,)") != -1 assert lines[3].find("def check(x):") != -1 - item._config.option.fulltrace = True - s = item.repr_failure(excinfo) - lines = s.split("\n") - assert lines[2].find("test_gen[0] -> check(0,)") == -1 def test_repr_tb_short(self): # XXX probably a redundant test - item, excinfo = suptest.getfailing(""" + itemtestreport,fn = suptest.getItemTestReport(""" def f(x): assert x def test_f(): f(0) - """) - item._config.option.tbstyle = "short" - s = item.repr_failure(excinfo) + """, tb="short") + s = itemtestreport.repr_failure print s index = -1 - basename = item.fspath.basename + basename = fn.basename lines = s.split("\n")[2:] for line in ( ' File "%s", line 5, in test_f' % basename, Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Fri Jul 18 00:19:31 2008 @@ -194,7 +194,6 @@ sorter = suptest.events_from_cmdline([p.dirpath(), '--collectonly']) assert not sorter.get(repevent.ItemStart) - assert not sorter.get(repevent.ItemFinish) assert not sorter.get(repevent.ItemTestReport) started = sorter.get(repevent.CollectionStart) finished = sorter.get(repevent.CollectionFinish) From hpk at codespeak.net Fri Jul 18 09:42:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 09:42:04 +0200 (CEST) Subject: [py-svn] r56631 - py/branch/event/py/test2 Message-ID: <20080718074204.995A6169F33@codespeak.net> Author: hpk Date: Fri Jul 18 09:41:53 2008 New Revision: 56631 Modified: py/branch/event/py/test2/present.py Log: small renaming Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Fri Jul 18 09:41:53 2008 @@ -19,7 +19,7 @@ style=config.option.tbstyle, ) - def repr_header(self): + def header(self): item = self._item modpath = getmodpath(item) self.out.sep("_", "entrypoint: %s" %(modpath)) @@ -31,7 +31,7 @@ item.obj.__name__, args) self.out.line(line) - def repr_footer(self): + def footer(self): colitem = self._item # XXX io capturing is stored with the itemtestreport for parent in colitem.listchain(): @@ -42,7 +42,7 @@ self.out.line(obj) def repr_failure(self, excinfo): - self.repr_header() + self.header() excinfo.traceback = self._item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) - self.repr_footer() + self.footer() From hpk at codespeak.net Fri Jul 18 11:18:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 11:18:31 +0200 (CEST) Subject: [py-svn] r56634 - in py/branch/event/py/test2: . rep rep/testing Message-ID: <20080718091831.D9553168023@codespeak.net> Author: hpk Date: Fri Jul 18 11:18:19 2008 New Revision: 56634 Added: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_terminal.py Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/session.py Log: beginning of a new Terminal Reporter that works from events. Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Fri Jul 18 11:18:19 2008 @@ -142,8 +142,14 @@ cls = self._getsessionclass() session = cls(self) session.fixoptions() + reporter = self._getreporterclass() + session.reporter = reporter(self) return session + def _getreporterclass(self): + from py.__.test2.rep.terminal import TerminalReporter + return TerminalReporter + def _getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. Added: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 18 11:18:19 2008 @@ -0,0 +1,10 @@ +from py.__.test2 import repevent + +class TerminalReporter(object): + def __init__(self, config, out=None): + self.config = config + self.config.bus.subscribe(self.consume) + + def consume(self, ev): + if isinstance(ev, repevent.ItemTestReport): + print ev.dottedpath(self.config), ev.passed Added: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Fri Jul 18 11:18:19 2008 @@ -0,0 +1,35 @@ +import py +from py.__.test2.rep.terminal import TerminalReporter +from py.__.test2 import repevent + +class TestInit: + def test_is_default(self): + config = py.test2.config._reparse(['xxx']) + assert config._getreporterclass() is TerminalReporter + + def test_session_has_reporter(self): + config = py.test2.config._reparse(['xxx']) + session = config.initsession() + assert isinstance(session.reporter, TerminalReporter) + assert session.reporter.config is config + + def test_reporter_is_subscribed(self): + config = py.test2.config._reparse(['xxx']) + session = config.initsession() + rep = session.reporter + for subscriber in config.bus._subscribers: + if subscriber == rep.consume: + break + else: + assert 0, "not subscribed" + +class TestEvents: + def test_session_starts(self): + config = py.test2.config._reparse(['xxx']) + session = config.initsession() + rep = session.reporter + rep.consume(repevent.SessionStart(session)) + + py.test.skip("xxx") + + Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Jul 18 11:18:19 2008 @@ -63,7 +63,8 @@ failurelist.append(event) if self.config.option.exitfirst: self.shouldstop = True - # XXX what about removing the processfailures subscription? + # XXX how to cleanly remove the processfailures + # subscription after it's done its work? self.config.bus.subscribe(processfailures) return failurelist From hpk at codespeak.net Fri Jul 18 11:38:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 11:38:31 +0200 (CEST) Subject: [py-svn] r56635 - py/trunk/py/doc Message-ID: <20080718093831.CC5E2169F04@codespeak.net> Author: hpk Date: Fri Jul 18 11:38:21 2008 New Revision: 56635 Modified: py/trunk/py/doc/TODO.txt Log: a few notes for release planning Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Fri Jul 18 11:38:21 2008 @@ -6,19 +6,34 @@ - BUG: write test/fix --showlocals (not showing anything) -- review and refactor architecture of py.test with particular +- extend conftest mechanism to allow to check for: + + conftest_*.py + + and maybe also for + + conftest/ + + directories. + +- twisted support: checkout and integrate Samuele's twisted support files + also look at Ralf Schmitt's way of going over greenlets + +- (ongoing) review and refactor architecture of py.test with particular respect to: - allow custom reporting - writing (stacked) extensions / plugins (compared to Nose) - event naming and processing - - porting existing extensions (htmlconftest / buildbot / PyPy's conftest's ...) - fast and stable distributed testing - reliable cross-platform testing + - porting existing extensions (htmlconftest / PyPy's conftest's ...) + + - fix reporting/usage degradation after reporter-merge merge: - collapse skips with same reason and lineno into one line -- fix and investigate win32 failures +- fix and investigate win32 failures - (needs review) adjust py.test documentation to reflect new collector/session architecture @@ -32,6 +47,8 @@ py.execnet -------------- +- thoroughly test on win32 (also in conjunction with py.test) + - cross-python version (2.2/2.3-2.5/6) and cross-platform testing of setup/teardown semantics From hpk at codespeak.net Fri Jul 18 11:50:59 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 11:50:59 +0200 (CEST) Subject: [py-svn] r56636 - py/trunk/py/doc Message-ID: <20080718095059.C9CEB16A0C7@codespeak.net> Author: hpk Date: Fri Jul 18 11:50:49 2008 New Revision: 56636 Modified: py/trunk/py/doc/TODO.txt Log: note regarding collection/runtest ambiguity Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Fri Jul 18 11:50:49 2008 @@ -44,6 +44,15 @@ certain situations because e.g. setup/teardown is fully performed although we have "skip by keyword" and could detect this early) +- reduce ambiguity/confusion of collection/running of tests: + - introduce "gentest" naming for generative tests and + deprecate test_* generators? + - collectors have collect() method + items have runtest() method + deprecate run() + - review source code and rename some internal methods to + help with un-confusing things + py.execnet -------------- From hpk at codespeak.net Fri Jul 18 12:06:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 12:06:50 +0200 (CEST) Subject: [py-svn] r56638 - py/branch/event/py/test2 Message-ID: <20080718100650.45FD416A0E2@codespeak.net> Author: hpk Date: Fri Jul 18 12:06:49 2008 New Revision: 56638 Modified: py/branch/event/py/test2/executor.py Log: some more explicit naming Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Fri Jul 18 12:06:49 2008 @@ -60,12 +60,12 @@ class BoxExecutor(RunExecutor): """ Same as RunExecutor, but boxes test instead """ - def fun(self): + def execute_forked(self): testrep = RunExecutor.execute(self) return testrep.dumps() - + def execute(self): - b = Box(self.fun, config=self.config) + b = Box(self.execute_forked, config=self.config) pid = b.run() assert pid return self.maketestreport(b) @@ -88,7 +88,7 @@ computations (more async mode) """ def execute(self): - b = Box(self.fun, config=self.config) + b = Box(self.execute_forked, config=self.config) parent, pid = b.run(continuation=True) def cont(waiter=os.waitpid): parent(pid, waiter=waiter) From hpk at codespeak.net Fri Jul 18 17:09:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 17:09:22 +0200 (CEST) Subject: [py-svn] r56642 - in py/branch/event/py: bin test2 test2/rep test2/rsession test2/testing Message-ID: <20080718150922.3CEE7169EC9@codespeak.net> Author: hpk Date: Fri Jul 18 17:09:20 2008 New Revision: 56642 Added: py/branch/event/py/bin/py.test2 (contents, props changed) Modified: py/branch/event/py/test2/executor.py py/branch/event/py/test2/item.py py/branch/event/py/test2/present.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/master.py py/branch/event/py/test2/rsession/slave.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_executor.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_session.py py/branch/event/py/test2/testing/test_setup_nested.py Log: * refactor ItemTestReport further. it now is very explicitely constructed so that there is clarity about its attributes everwhere. * an item is now responsible for representing failure and also its path information. * the new terminal-reporter uses this * py.test2 begings to work Added: py/branch/event/py/bin/py.test2 ============================================================================== --- (empty file) +++ py/branch/event/py/bin/py.test2 Fri Jul 18 17:09:20 2008 @@ -0,0 +1,4 @@ +#!/usr/bin/env python + +from _findpy import py +py.test2.cmdline.main() Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Fri Jul 18 17:09:20 2008 @@ -17,7 +17,7 @@ def __init__(self, item, config): self.item = item self.config = config - self.testrep = repevent.ItemTestReport(item._get_collector_trail()) + self.outerr = None def setup_and_run(self): testrep = self.execute() @@ -29,17 +29,8 @@ try: self.item.run() finally: - outerr = capture.reset() - self.testrep.iocapture_run = outerr - - def _fillreport(self, excinfo): - testrep = self.testrep - if excinfo.errisinstance(Skipped): - testrep.skipped = True - else: - testrep.failed = True - testrep.exconly = excinfo.exconly() - testrep.repr_failure = self.item.repr_failure(excinfo) + out, err = capture.reset() + self.outerr = (("recorded stdout", out), ("recorded stderr", err)) def execute(self): try: @@ -48,14 +39,11 @@ raise except: excinfo = py.code.ExceptionInfo() - if excinfo.errisinstance(Failed) and excinfo.value.excinfo: - excinfo = e.excinfo - self._fillreport(excinfo) if self.config.option.usepdb and not excinfo.errisinstance(Skipped): py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) else: - self.testrep.passed = True - return self.testrep + excinfo = None + return repevent.ItemTestReport.fromitem(self.item, excinfo, self.outerr) class BoxExecutor(RunExecutor): """ Same as RunExecutor, but boxes test instead @@ -72,12 +60,12 @@ def maketestreport(self, b): if b.retval is not None: - testrep = repevent.ItemTestReport.loads(b.retval) + testrep = repevent.ItemTestReport.fromdumps(b.retval) testrep.stdout = b.stdoutrepr testrep.stderr = b.stderrrepr else: - testrep = repevent.ItemTestReport(self.item._get_collector_trail()) - testrep.failed = True + testrep = repevent.ItemTestReport.fromitem(self.item, None, None) + testrep.outcome = "failed" testrep.stdout = b.stdoutrepr testrep.stderr = b.stderrrepr testrep.signal = b.signal Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Fri Jul 18 17:09:20 2008 @@ -30,6 +30,20 @@ self.stack.append(col) class Item(Node): + def repr_path(self): + """ return (filepath, testpath) tuple with + filepath: a fspath representation (None if not applicable) + testpath: a path representation of the test path. + + this is used by reporters. + """ + xxx + + def repr_failure(self, excinfo): + """ return string failure represenation for this item. + """ + xxx + def runtest(self): from py.__.test2.executor import getexecutor executor = getexecutor(self, self._config) @@ -56,6 +70,13 @@ def repr_failure(self, excinfo): """ return a textual failure representation for this item. """ + #xxx outerr p = present.PythonFunctionPresenter(self._config, self) p.repr_failure(excinfo) return p.stringio.getvalue() + + def repr_path(self): + from py.__.test2.rep.reporter import getrelpath, getmodpath + fspath = getrelpath(self._config.topdir, self.fspath) + modpath = getmodpath(self) + return (fspath, modpath) Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Fri Jul 18 17:09:20 2008 @@ -31,18 +31,9 @@ item.obj.__name__, args) self.out.line(line) - def footer(self): - colitem = self._item - # XXX io capturing is stored with the itemtestreport - for parent in colitem.listchain(): - if hasattr(parent, '_run_capture'): - name, obj = parent._run_capture - if obj: - self.out.sep("- ", "%s: recorded std%s" % (parent.name, name)) - self.out.line(obj) def repr_failure(self, excinfo): self.header() excinfo.traceback = self._item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) - self.footer() + # xxx outerr self.footer() Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 18 17:09:20 2008 @@ -1,10 +1,18 @@ +import sys from py.__.test2 import repevent class TerminalReporter(object): def __init__(self, config, out=None): self.config = config self.config.bus.subscribe(self.consume) + self.currentfspath = None def consume(self, ev): if isinstance(ev, repevent.ItemTestReport): - print ev.dottedpath(self.config), ev.passed + fspath, modpath = ev.repr_path + if fspath != self.currentfspath: + print + sys.stdout.write(fspath) + self.currentfspath = fspath + sys.stdout.write(".") + sys.stdout.flush() Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Fri Jul 18 17:09:20 2008 @@ -3,6 +3,7 @@ import py import time +from py.__.test2.outcome import Skipped class BaseEvent(object): @@ -54,21 +55,45 @@ import marshal class ItemTestReport(BaseEvent): - passed = failed = skipped = False - def __init__(self, trail, outcome=None): + passed = property(lambda self: self.outcome == "passed") + failed = property(lambda self: self.outcome == "failed") + skipped = property(lambda self: self.outcome == "skipped") + + @classmethod + def fromitem(cls, item, excinfo, outerr): + trail = item._get_collector_trail() + repr_failure = None + if excinfo: + if excinfo.errisinstance(Skipped): + outcome = "skipped" + else: + outcome = "failed" + repr_failure = item.repr_failure(excinfo) + else: + outcome = "passed" + repr_path = item.repr_path() + return ItemTestReport(trail, outcome, repr_failure, repr_path, outerr) + + @classmethod + def fromdumps(cls, string): + d = marshal.loads(string) + assert isinstance(d, dict) + return cls(**d) + + def __init__(self, trail, outcome, repr_failure, repr_path, outerr): self.trail = trail - if outcome is not None: - assert outcome in ("passed", "failed", "skipped") - setattr(self, outcome, True) - - def dottedpath(self, config): - """ return dotted path for underlying test item. """ - from py.__.test2.present import getmodpath - col = config._getcollector(self.trail) - return getmodpath(col) + assert outcome in ("passed", "failed", "skipped") + self.outcome = outcome + self.repr_failure = repr_failure + self.repr_path = repr_path + self.outerr = outerr def dumps(self): """ marshal all possible attr to a string. """ + # as this might run in some remote process + # where we might not see exceptions we try + # to make this code here robust. + # XXX just dump __dict__, ensure debuggability otherwise d = {} for name, value in self.__dict__.items(): try: @@ -79,14 +104,6 @@ d[name] = value return marshal.dumps(d) - def loads(cls, string): - testrep = object.__new__(ItemTestReport) - d = marshal.loads(string) - assert isinstance(d, dict) - testrep.__dict__.update(d) - return testrep - loads = classmethod(loads) - # ---------------------------------------------------------------------- # Distributed Testing Events # ---------------------------------------------------------------------- Modified: py/branch/event/py/test2/rsession/master.py ============================================================================== --- py/branch/event/py/test2/rsession/master.py (original) +++ py/branch/event/py/test2/rsession/master.py Fri Jul 18 17:09:20 2008 @@ -18,7 +18,7 @@ self.notify(repevent.HostDown(self.host)) return item = self.pending.pop() - ev = repevent.ItemTestReport.loads(outcomestring) + ev = repevent.ItemTestReport.fromdumps(outcomestring) self.notify(ev) def send(self, item): Modified: py/branch/event/py/test2/rsession/slave.py ============================================================================== --- py/branch/event/py/test2/rsession/slave.py (original) +++ py/branch/event/py/test2/rsession/slave.py Fri Jul 18 17:09:20 2008 @@ -5,6 +5,7 @@ import py from py.__.test2 import repevent + def slave_main(receive, send, config): while 1: itemspec = receive() @@ -33,9 +34,7 @@ py.magic.invoke(assertion=1) from py.__.test2.rsession.slave import slave_main - #debug = open("/tmp/debug-slave.log", 'wa') def send(msg): - #print >>debug, "sending", msg channel.send(msg) def receive(): msg = channel.receive() Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Fri Jul 18 17:09:20 2008 @@ -98,8 +98,8 @@ """ return a testreport whose dotted import path matches """ l = [] for rep in self.get(repevent.ItemTestReport): - dp = rep.dottedpath(self.config) - if dp.find(inamepart) != -1: + fspath, modpath = rep.repr_path + if modpath.find(inamepart) != -1: l.append(rep) if len(l) != 1: raise ValueError("did not find exactly one testreport" Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Fri Jul 18 17:09:20 2008 @@ -32,26 +32,25 @@ def test_run_executor_capture_stdout(self): testrep = self.exrun("print") - assert testrep.iocapture_run[0] == "samfing\n" - assert not testrep.iocapture_run[1] + assert testrep.outerr[0][1] == "samfing\n" + assert not testrep.outerr[1][1] def test_run_executor_capture_stderr(self): testrep = self.exrun("printerr") - assert testrep.iocapture_run[1] == "samfing\n" - assert not testrep.iocapture_run[0] + assert testrep.outerr[1][1] == "samfing\n" + assert not testrep.outerr[0][1] def test_box_executor_printfailing(self): testrep = self.exrun("printfail") assert not testrep.passed assert testrep.failed - assert testrep.iocapture_run[0].find("samfing elz") != -1 - assert not testrep.iocapture_run[1] + assert testrep.outerr[0][1].find("samfing elz") != -1 + assert not testrep.outerr[1][1] def test_executor_explicit_Failed(self): testrep = self.exrun("explicitfail") assert not testrep.passed assert testrep.failed - #assert testrep._excinfo == "3" def test_executor_raises_fails(self): testrep = self.exrun("raisesfails") Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Fri Jul 18 17:09:20 2008 @@ -3,36 +3,24 @@ import setupdata, suptest class TestItemTestReport(object): - def _makevent(self, names, **kwargs): - fcol = setupdata.getexamplecollector(names) - trail = fcol._get_collector_trail() - ev = repevent.ItemTestReport(trail, **kwargs) - ev._col = fcol - return ev - def test_dumps_loads(self): - ev = self._makevent(["filetest.py", 'test_one'], outcome="skipped") - ev.whatever = 42 - class A: pass - ev.a = A() - assert ev.skipped - s = ev.dumps() - ev2 = repevent.ItemTestReport.loads(s) - assert ev2.skipped - assert ev2.trail == ev.trail - assert ev2.dottedpath(ev._col._config).endswith("filetest.test_one") - assert ev2.whatever == 42 - assert not hasattr(ev2, 'a') - - def test_ItemTestReport(self): - ev = self._makevent(["filetest.py", 'test_one'], outcome="passed") - assert ev.dottedpath(ev._col._config).endswith("filetest.test_one") + for example in "filetest.py", "test_threepass.py": + sorter = suptest.events_run_example(example) + reports = sorter.get(repevent.ItemTestReport) + ev1 = reports[0] + ev2 = repevent.ItemTestReport.fromdumps(ev1.dumps()) + assert ev1.repr_path == ev2.repr_path + assert ev1.repr_failure == ev2.repr_failure + assert ev1.outcome == ev2.outcome def test_failing(self): sorter = suptest.events_run_example("filetest.py") reports = sorter.get(repevent.ItemTestReport) ev = reports[0] assert ev.failed - assert ev.exconly.find("AssertionError") != -1 + #assert ev.repr_failure.find("AssertionError") != -1 + filepath, modpath = ev.repr_path + assert filepath.endswith("filetest.py") + assert modpath.endswith("filetest.test_one") Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Fri Jul 18 17:09:20 2008 @@ -12,7 +12,7 @@ '-s', '-k', keyword) passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 - assert failed[0].dottedpath(sorter.config).endswith(name) + assert failed[0].repr_path[1].endswith(name) des = sorter.get(repevent.DeselectedTest) assert len(des) == 1 @@ -41,7 +41,7 @@ print "keyword", repr(keyword) passed, skipped, failed = sorter.listoutcomes() assert len(passed) == 1 - assert passed[0].dottedpath(sorter.config).endswith('test_2') + assert passed[0].repr_path[1].endswith('test_2') assert not skipped deslist = sorter.get(repevent.DeselectedTest) assert len(deslist) == 1 @@ -107,8 +107,11 @@ ev_list = sorter.get(repevent.ItemTestReport) ev1, ev2 = ev_list - assert ev1.iocapture_run == ("42\n", "23\n") - assert ev2.iocapture_run == ("1\n", "2\n") + assert ev1.outerr[0][1] == "42\n" + assert ev1.outerr[1][1] == "23\n" + + assert ev2.outerr[0][1] == "1\n" + assert ev2.outerr[1][1] == "2\n" def test_raises_output(self): sorter = suptest.events_from_runsource(''' @@ -118,7 +121,7 @@ ''') passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 - out = failed[0].exconly + out = failed[0].repr_failure # exconly if not out.find("DID NOT RAISE") != -1: print out py.test2.fail("incorrect raises() output") @@ -168,9 +171,9 @@ sorter = suptest.events_run_example("brokenrepr.py") passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 2 - out = failed[0].exconly + out = failed[0].repr_failure # exconly assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' - out = failed[1].exconly + out = failed[1].repr_failure # exconly assert out.find("[unknown exception raised in repr()]") != -1 def test_collect_only_with_various_situations(self): @@ -238,9 +241,9 @@ assert len(skipped) == 0 assert len(passed) == 1 assert len(failed) == 3 - assert failed[0].dottedpath(sorter.config).endswith("test_one_one") - assert failed[1].dottedpath(sorter.config).endswith("test_other") - assert failed[2].dottedpath(sorter.config).endswith("test_two") + assert failed[0].repr_path[1].endswith("test_one_one") + assert failed[1].repr_path[1].endswith("test_other") + assert failed[2].repr_path[1].endswith("test_two") def test_capture_on_function(self): sorter = suptest.events_from_runsource(""" @@ -251,12 +254,12 @@ assert 0 """) testrep = sorter.getreport("test_one") - assert testrep.iocapture_run[0].startswith("passed") - assert not testrep.iocapture_run[1] + assert testrep.outerr[0][1].startswith("passed") + assert not testrep.outerr[1][1] testrep = sorter.getreport("test_two") - assert testrep.iocapture_run[0].startswith("failed") - assert not testrep.iocapture_run[1] + assert testrep.outerr[0][1].startswith("failed") + assert not testrep.outerr[1][1] def test_function_repr_failure(self): o = setupdata.getexamplefile("filetest.py") Modified: py/branch/event/py/test2/testing/test_setup_nested.py ============================================================================== --- py/branch/event/py/test2/testing/test_setup_nested.py (original) +++ py/branch/event/py/test2/testing/test_setup_nested.py Fri Jul 18 17:09:20 2008 @@ -123,6 +123,6 @@ """) rep = sorter.getreport("test_one") assert rep.passed - assert rep.iocapture_run[0] == "check\n" - assert rep.iocapture_run[1] == "e\n" + assert rep.outerr[0][1] == "check\n" + assert rep.outerr[1][1] == "e\n" From hpk at codespeak.net Fri Jul 18 17:30:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 17:30:47 +0200 (CEST) Subject: [py-svn] r56643 - py/branch/event/py/test2 Message-ID: <20080718153047.E36E62A807E@codespeak.net> Author: hpk Date: Fri Jul 18 17:30:47 2008 New Revision: 56643 Modified: py/branch/event/py/test2/executor.py py/branch/event/py/test2/item.py py/branch/event/py/test2/session.py Log: emit ItemTestReport events from the main loop. simplify executor. Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Fri Jul 18 17:30:47 2008 @@ -19,11 +19,6 @@ self.config = config self.outerr = None - def setup_and_run(self): - testrep = self.execute() - self.config.bus.notify(testrep) - return testrep - def runitem(self): capture = self.config._getcapture(path=self.item.fspath) try: @@ -81,6 +76,9 @@ def cont(waiter=os.waitpid): parent(pid, waiter=waiter) return self.maketestreport(b) + + # XXX does not follow normal execute() signature! + # thus cannot be used like this anyway return cont, pid class ApigenExecutor(RunExecutor): Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Fri Jul 18 17:30:47 2008 @@ -47,7 +47,7 @@ def runtest(self): from py.__.test2.executor import getexecutor executor = getexecutor(self, self._config) - return executor.setup_and_run() + return executor.execute() class Function(FunctionMixin, Item): """ a Function Item is responsible for setting up Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Jul 18 17:30:47 2008 @@ -77,7 +77,8 @@ if self.shouldstop: break if not self.config.option.collectonly: - self.runtest(item) + testrep = self.runtest(item) + self.config.bus.notify(testrep) finally: failures = self.sessionfinishes() return failures From hpk at codespeak.net Fri Jul 18 19:28:15 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 19:28:15 +0200 (CEST) Subject: [py-svn] r56644 - py/dist/py/test Message-ID: <20080718172815.DF14E16A055@codespeak.net> Author: hpk Date: Fri Jul 18 19:28:14 2008 New Revision: 56644 Modified: py/dist/py/test/session.py Log: porting a fix from trunk that gives exit codes != 0 if there were test failures. Modified: py/dist/py/test/session.py ============================================================================== --- py/dist/py/test/session.py (original) +++ py/dist/py/test/session.py Fri Jul 18 19:28:14 2008 @@ -67,6 +67,7 @@ self.footer(colitems) except Exit, ex: pass + return self.getitemoutcomepairs(Failed) def runtraced(self, colitem): if self.shouldclose(): From hpk at codespeak.net Fri Jul 18 20:41:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 20:41:31 +0200 (CEST) Subject: [py-svn] r56645 - in py/branch/event: . py py/code py/io py/io/test py/test2 py/test2/rep py/test2/terminal Message-ID: <20080718184131.6855669801F@codespeak.net> Author: hpk Date: Fri Jul 18 20:41:30 2008 New Revision: 56645 Added: py/branch/event/NOTES_FOR_MERGE py/branch/event/py/io/terminalwriter.py - copied, changed from r56624, py/branch/event/py/misc/terminal_helper.py py/branch/event/py/io/test/test_terminalwriter.py Modified: py/branch/event/py/__init__.py py/branch/event/py/code/tbpresent.py py/branch/event/py/test2/collect.py py/branch/event/py/test2/item.py py/branch/event/py/test2/rep/reporter.py py/branch/event/py/test2/terminal/remote.py Log: * add relatively well tested py.io.TerminalWriter and phase out py/misc/terminal_helper.py * make a note to remove the latter helper (still used by the legacy py.test) at merge time Added: py/branch/event/NOTES_FOR_MERGE ============================================================================== --- (empty file) +++ py/branch/event/NOTES_FOR_MERGE Fri Jul 18 20:41:30 2008 @@ -0,0 +1 @@ +remove misc/terminal_helper.py (now py.io.TerminalWriter) Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Fri Jul 18 20:41:30 2008 @@ -130,6 +130,7 @@ 'io.FDCapture' : ('./io/fdcapture.py', 'FDCapture'), 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), 'io.StdCaptureFD' : ('./io/stdcapture.py', 'StdCaptureFD'), + 'io.TerminalWriter' : ('./io/terminalwriter.py', 'TerminalWriter'), # error module, defining all errno's as Classes 'error' : ('./misc/error.py', 'error'), Modified: py/branch/event/py/code/tbpresent.py ============================================================================== --- py/branch/event/py/code/tbpresent.py (original) +++ py/branch/event/py/code/tbpresent.py Fri Jul 18 20:41:30 2008 @@ -3,7 +3,6 @@ """ import py from py.__.code import safe_repr -from py.__.misc.terminal_helper import getout class TBPresenter(object): """ presenting information about failing Functions and Generators. """ @@ -11,14 +10,10 @@ flow_marker = ">" fail_marker = "E" - def __init__(self, out=None, showlocals=False, style="long"): - if out is None: - self.stringio = py.std.StringIO.StringIO() - out = getout(self.stringio) - assert hasattr(out, 'write'), out - self.out = out + def __init__(self, showlocals=False, style="long"): self.showlocals = showlocals self.style = style + self.out = py.io.TerminalWriter() def repr_source(self, source, marker_location=-1): """ This one represents piece of source with possible Copied: py/branch/event/py/io/terminalwriter.py (from r56624, py/branch/event/py/misc/terminal_helper.py) ============================================================================== --- py/branch/event/py/misc/terminal_helper.py (original) +++ py/branch/event/py/io/terminalwriter.py Fri Jul 18 20:41:30 2008 @@ -8,18 +8,21 @@ import sys, os import py +def _getdimensions(): + import termios,fcntl,struct + call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) + height,width = struct.unpack( "hhhh", call ) [:2] + return height, width + def get_terminal_width(): try: - import termios,fcntl,struct - call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) - height,width = struct.unpack( "hhhh", call ) [:2] - terminal_width = width + height, width = _getdimensions() except (SystemExit, KeyboardInterrupt), e: raise except: # FALLBACK - terminal_width = int(os.environ.get('COLUMNS', 80))-1 - return terminal_width + width = int(os.environ.get('COLUMNS', 80))-1 + return width terminal_width = get_terminal_width() @@ -39,10 +42,17 @@ if flush: file.flush() -class Out(object): - tty = False - def __init__(self, file): - self.file = py.io.dupfile(file) +class TerminalWriter(object): + def __init__(self, file=None): + if file is None: + self.stringio = file = py.std.cStringIO.StringIO() + elif hasattr(file, 'send'): + file = WriteFile(file.send) + elif callable(file): + file = WriteFile(file) + else: + file = py.io.dupfile(file) + self._file = file self.fullwidth = get_terminal_width() def sep(self, sepchar, title=None, fullwidth=None): @@ -69,60 +79,16 @@ line += sepchar.rstrip() self.line(line) -class TerminalOut(Out): - tty = True - def __init__(self, file): - super(TerminalOut, self).__init__(file) - - def sep(self, sepchar, title=None): - super(TerminalOut, self).sep(sepchar, title, - self.terminal_width) - def write(self, s): - self.file.write(str(s)) - self.file.flush() + self._file.write(str(s)) + self._file.flush() def line(self, s=''): if s: - self.file.write(s + '\n') + self._file.write(s + '\n') else: - self.file.write('\n') - self.file.flush() - - def xxxrewrite(self, s=''): - #self.write('\x1b[u%s' % s) - this escape sequence does - # strange things, or nothing at all, on various terminals. - # XXX what is it supposed to do in the first place?? - self.write(s) - -class FileOut(Out): - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(str(s) + '\n') - else: - self.file.write('\n') - self.file.flush() - - def xxxrewrite(self, s=''): - self.write(s) - -def getout(file): - # XXX investigate further into terminal output, this is not enough - # - if file is None: - file = py.std.sys.stdout - elif hasattr(file, 'send'): - file = WriteFile(file.send) - elif callable(file): - file = WriteFile(file) - if hasattr(file, 'isatty') and file.isatty(): - return TerminalOut(file) - else: - return FileOut(file) + self._file.write('\n') + self._file.flush() class WriteFile(object): def __init__(self, writemethod): @@ -130,3 +96,4 @@ def flush(self): return + Added: py/branch/event/py/io/test/test_terminalwriter.py ============================================================================== --- (empty file) +++ py/branch/event/py/io/test/test_terminalwriter.py Fri Jul 18 20:41:30 2008 @@ -0,0 +1,75 @@ +import py +import os +from py.__.io import terminalwriter + +def test_terminalwriter_computes_width(): + py.magic.patch(terminalwriter, 'get_terminal_width', lambda: 42) + try: + tw = py.io.TerminalWriter() + assert tw.fullwidth == 42 + finally: + py.magic.revert(terminalwriter, 'get_terminal_width') + +def test_terminalwriter_defaultwidth_80(): + py.magic.patch(terminalwriter, '_getdimensions', lambda: 0/0) + try: + tw = py.io.TerminalWriter() + assert tw.fullwidth == os.environ.get('COLUMNS', 80)-1 + finally: + py.magic.revert(terminalwriter, '_getdimensions') + + +def test_terminalwriter_default_instantiation(): + tw = py.io.TerminalWriter() + assert hasattr(tw, 'stringio') + +class BaseTests: + def test_line(self): + tw = self.getwriter() + tw.line("hello") + l = self.getlines() + assert len(l) == 1 + assert l[0] == "hello\n" + + def test_sep_no_title(self): + tw = self.getwriter() + tw.sep("-", fullwidth=60) + l = self.getlines() + assert len(l) == 1 + assert l[0] == "-" * 60 + "\n" + + def test_sep_with_title(self): + tw = self.getwriter() + tw.sep("-", "hello", fullwidth=60) + l = self.getlines() + assert len(l) == 1 + assert l[0] == "-" * 26 + " hello " + "-" * 27 + "\n" + +class TestStringIO(BaseTests): + def getwriter(self): + self.tw = py.io.TerminalWriter() + return self.tw + def getlines(self): + io = self.tw.stringio + io.seek(0) + return io.readlines() + +class TestChannelLikeFile(BaseTests): + def getwriter(self): + self.writes = l = [] + class A: + def send(self, msg): + l.append(msg) + return py.io.TerminalWriter(A()) + def getlines(self): + io = py.std.cStringIO.StringIO() + io.write("".join(self.writes)) + io.seek(0) + return io.readlines() + +class TestCallableFile(TestChannelLikeFile): + def getwriter(self): + self.writes = [] + return py.io.TerminalWriter(self.writes.append) + + Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Jul 18 20:41:30 2008 @@ -155,6 +155,7 @@ return self._config.get_collector_trail(self) def repr_failure(self, excinfo): + xxx # needs test and sensible output p = present.FuncPresenter(self._config) p.repr_failure_headline(self) p.out.line("") Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Fri Jul 18 20:41:30 2008 @@ -73,7 +73,7 @@ #xxx outerr p = present.PythonFunctionPresenter(self._config, self) p.repr_failure(excinfo) - return p.stringio.getvalue() + return p.out.stringio.getvalue() def repr_path(self): from py.__.test2.rep.reporter import getrelpath, getmodpath Modified: py/branch/event/py/test2/rep/reporter.py ============================================================================== --- py/branch/event/py/test2/rep/reporter.py (original) +++ py/branch/event/py/test2/rep/reporter.py Fri Jul 18 20:41:30 2008 @@ -7,10 +7,8 @@ import py -from py.__.misc.terminal_helper import getout from py.__.test2 import repevent from py.__.test2 import outcome -from py.__.misc.terminal_helper import ansi_print, get_terminal_width import sys @@ -61,6 +59,17 @@ else: return reporterclass +# +# +# +# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted +# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted +# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted +# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted +# +# + + class TestReporter(object): """ Simple test reporter which tracks failures and also calls arbitrary provided function, Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Fri Jul 18 20:41:30 2008 @@ -1,7 +1,6 @@ from __future__ import generators import py from py.__.test2.session import Session -from py.__.misc.terminal_helper import getout from py.__.test2.outcome import Failed, Passed, Skipped def checkpyfilechange(rootdir, statcache={}): @@ -59,7 +58,7 @@ if file is None: file = py.std.sys.stdout self._file = file - self.out = getout(file) + self.out = py.io.TerminalWriter(file) def _setexecutable(self): name = self.config.option.executable From hpk at codespeak.net Fri Jul 18 23:08:06 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 23:08:06 +0200 (CEST) Subject: [py-svn] r56648 - py/branch/event/py/code/testing Message-ID: <20080718210806.F0DC4169EBD@codespeak.net> Author: hpk Date: Fri Jul 18 23:08:05 2008 New Revision: 56648 Modified: py/branch/event/py/code/testing/test_tbpresent.py Log: fix getting at stringio Modified: py/branch/event/py/code/testing/test_tbpresent.py ============================================================================== --- py/branch/event/py/code/testing/test_tbpresent.py (original) +++ py/branch/event/py/code/testing/test_tbpresent.py Fri Jul 18 23:08:05 2008 @@ -21,7 +21,7 @@ """).strip() pr.flow_marker = "|" pr.repr_source(source, 0) - lines = pr.stringio.getvalue().split("\n") + lines = pr.out.stringio.getvalue().split("\n") assert len(lines) == 3 assert lines[0].startswith("|") assert lines[0].find("def f(x)") != -1 @@ -42,13 +42,13 @@ source = py.code.Source(f) e = f() pr.repr_failure_explanation(e, source) - assert pr.stringio.getvalue().startswith("E ") + assert pr.out.stringio.getvalue().startswith("E ") def test_repr_local(self): p = self.getpresenter(showlocals=True) loc = locals() p.repr_locals(loc) - result = p.stringio.getvalue() + result = p.out.stringio.getvalue() for key in loc.keys(): assert result.find(key) != -1 @@ -69,7 +69,7 @@ excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter() p.repr_tb_entry(excinfo.traceback[-2]) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() lines = s.split("\n") # test intermittent entries @@ -82,7 +82,7 @@ # test last entry p = self.getpresenter() p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() lines = s.split("\n") print s assert lines[0] == " def func1():" @@ -102,7 +102,7 @@ excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter(style="short") p.repr_tb_entry(excinfo.traceback[-2]) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() lines = s.split("\n") basename = py.path.local(mod.__file__).basename assert lines[0] == ' File "%s", line 5, in entry' % basename @@ -113,7 +113,8 @@ print s p = self.getpresenter(style="short") p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() + lines = s.split("\n") assert lines[0] == ' File "%s", line 3, in func1' % basename assert lines[1] == ' raise ValueError("hello")' @@ -130,11 +131,11 @@ excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter(style="no") p.repr_tb_entry(excinfo.traceback[-2]) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() assert not s p = self.getpresenter(style="no") p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() lines = s.split("\n") assert lines[0] == 'E ValueError: hello' @@ -179,7 +180,7 @@ p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) p.repr_sep = lambda sep: l.append(sep) p.repr_tb(excinfo) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() print l #assert re.search(".*return rec1.*x\+1", s) #assert re.search(".*return rec2.*x-1.*", s) @@ -206,7 +207,7 @@ p = self.getpresenter() p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.stringio.getvalue() + s = p.out.stringio.getvalue() print s lines = s.split("\n") assert lines[-4] == "E assert 1 == 2" From hpk at codespeak.net Fri Jul 18 23:10:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 23:10:44 +0200 (CEST) Subject: [py-svn] r56649 - in py/branch/event: . py py/io py/io/test py/test2 py/test2/testing Message-ID: <20080718211044.7DCB4169EBF@codespeak.net> Author: hpk Date: Fri Jul 18 23:10:43 2008 New Revision: 56649 Added: py/branch/event/py/io/box.py - copied, changed from r56637, py/branch/event/py/test2/box.py py/branch/event/py/io/test/test_boxing.py - copied, changed from r56637, py/branch/event/py/test2/testing/test_boxing.py Removed: py/branch/event/py/test2/box.py py/branch/event/py/test2/testing/test_boxing.py Modified: py/branch/event/NOTES_FOR_MERGE py/branch/event/py/__init__.py py/branch/event/py/test2/executor.py py/branch/event/py/test2/testing/test_executor.py Log: factor out boxing to test-independent py.io.ForkedFunc add tests, cleanup. can still use some work but is basically fine (and already more robust than the previous version) Modified: py/branch/event/NOTES_FOR_MERGE ============================================================================== --- py/branch/event/NOTES_FOR_MERGE (original) +++ py/branch/event/NOTES_FOR_MERGE Fri Jul 18 23:10:43 2008 @@ -1 +1,3 @@ remove misc/terminal_helper.py (now py.io.TerminalWriter) +review py.io.ForkedFunc and see if to provide a function that throws ExecutionFailed aka py.process.cmdexec +merge py.process.cmdexec to py.io Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Fri Jul 18 23:10:43 2008 @@ -131,6 +131,7 @@ 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), 'io.StdCaptureFD' : ('./io/stdcapture.py', 'StdCaptureFD'), 'io.TerminalWriter' : ('./io/terminalwriter.py', 'TerminalWriter'), + 'io.ForkedFunc' : ('./io/box.py', 'ForkedFunc'), # error module, defining all errno's as Classes 'error' : ('./misc/error.py', 'error'), Copied: py/branch/event/py/io/box.py (from r56637, py/branch/event/py/test2/box.py) ============================================================================== --- py/branch/event/py/test2/box.py (original) +++ py/branch/event/py/io/box.py Fri Jul 18 23:10:43 2008 @@ -1,120 +1,107 @@ -""" boxing - wrapping process with another process so we can run -a process inside and see if it crashes +""" + ForkedFunc provides a way to run a function in a forked process + and get at its return value, stdout and stderr output as well + as signals and exitstatusus. + + XXX see if tempdir handling is sane """ import py import os import sys import marshal -from py.__.test2 import config as pytestconfig - -PYTESTSTDOUT = "pyteststdout" -PYTESTSTDERR = "pyteststderr" -PYTESTRETVAL = "pytestretval" - -import tempfile -import itertools -from StringIO import StringIO -counter = itertools.count().next - -class FileBox(object): - def __init__(self, fun, args=None, kwargs=None, config=None): +class ForkedFunc(object): + def __init__(self, fun, args=None, kwargs=None, nice_level=0): if args is None: args = [] if kwargs is None: kwargs = {} self.fun = fun - self.config = config - assert self.config self.args = args self.kwargs = kwargs - - def run(self, continuation=False): - # XXX we should not use py.test2.ensuretemp here - count = counter() - tempdir = py.test2.ensuretemp("box%d" % count) - self.tempdir = tempdir - self.PYTESTRETVAL = tempdir.join('retval') - self.PYTESTSTDOUT = tempdir.join('stdout') - self.PYTESTSTDERR = tempdir.join('stderr') + self.tempdir = tempdir = py.path.local.mkdtemp() + self.RETVAL = tempdir.join('retval') + self.STDOUT = tempdir.join('stdout') + self.STDERR = tempdir.join('stderr') - nice_level = self.config.getvalue('dist_nicelevel') pid = os.fork() - if pid: - if not continuation: - self.parent(pid) - else: - return self.parent, pid - else: - try: - outcome = self.children(nice_level) - except: - excinfo = py.code.ExceptionInfo() - x = open("/tmp/traceback", "w") - print >>x, "Internal box error" - for i in excinfo.traceback: - print >>x, str(i)[2:-1] - print >>x, excinfo - x.close() - os._exit(1) - os.close(1) - os.close(2) - os._exit(0) - return pid - - def children(self, nice_level): + if pid: # in parent process + self.pid = pid + else: # in child process + self._child(nice_level) + + def _child(self, nice_level): # right now we need to call a function, but first we need to # map all IO that might happen # make sure sys.stdout points to file descriptor one - sys.stdout = stdout = self.PYTESTSTDOUT.open('w') + sys.stdout = stdout = self.STDOUT.open('w') sys.stdout.flush() fdstdout = stdout.fileno() if fdstdout != 1: os.dup2(fdstdout, 1) - sys.stderr = stderr = self.PYTESTSTDERR.open('w') + sys.stderr = stderr = self.STDERR.open('w') fdstderr = stderr.fileno() if fdstderr != 2: os.dup2(fdstderr, 2) - retvalf = self.PYTESTRETVAL.open("w") + retvalf = self.RETVAL.open("w") + EXITSTATUS = 0 try: if nice_level: os.nice(nice_level) - # with fork() we have duplicated py.test's basetemp - # directory so we want to set it manually here. - # this may be expensive for some test setups, - # but that is what you get with boxing. - # XXX but we are called in more than strict boxing - # mode ("AsyncExecutor") so we can't do the following without - # inflicting on --dist speed, hum: - # pytestconfig.basetemp = self.tempdir.join("childbasetemp") - retval = self.fun(*self.args, **self.kwargs) - retvalf.write(marshal.dumps(retval)) + try: + retval = self.fun(*self.args, **self.kwargs) + retvalf.write(marshal.dumps(retval)) + except: + excinfo = py.code.ExceptionInfo() + stderr.write(excinfo.exconly()) + EXITSTATUS = 3 finally: stdout.close() stderr.close() retvalf.close() - os._exit(0) + os.close(1) + os.close(2) + os._exit(EXITSTATUS) - def parent(self, pid, waiter=os.waitpid): - pid, exitstat = waiter(pid, 0) - self.signal = exitstat & 0x7f - self.exitstat = exitstat & 0xff00 - - - if not exitstat: - retval = self.PYTESTRETVAL.open() + def waitfinish(self, waiter=os.waitpid): + pid, systemstatus = waiter(self.pid, 0) + if systemstatus: + if os.WIFSIGNALED(systemstatus): + exitstatus = os.WTERMSIG(systemstatus) + 128 + else: + exitstatus = os.WEXITSTATUS(systemstatus) + #raise ExecutionFailed(status, systemstatus, cmd, + # ''.join(out), ''.join(err)) + else: + exitstatus = 0 + signal = systemstatus & 0x7f + if not exitstatus and not signal: + retval = self.RETVAL.open() try: retval_data = retval.read() finally: retval.close() - self.retval = marshal.loads(retval_data) + retval = marshal.loads(retval_data) else: - self.retval = None - - self.stdoutrepr = self.PYTESTSTDOUT.read() - self.stderrrepr = self.PYTESTSTDERR.read() - return self.stdoutrepr, self.stderrrepr - -Box = FileBox + retval = None + stdout = self.STDOUT.read() + stderr = self.STDERR.read() + self._removetemp() + return Result(exitstatus, signal, retval, stdout, stderr) + + def _removetemp(self): + if self.tempdir.check(): + self.tempdir.remove() + + def __del__(self): + self._removetemp() + +class Result(object): + def __init__(self, exitstatus, signal, retval, stdout, stderr): + self.exitstatus = exitstatus + self.signal = signal + self.retval = retval + self.out = stdout + self.err = stderr Copied: py/branch/event/py/io/test/test_boxing.py (from r56637, py/branch/event/py/test2/testing/test_boxing.py) ============================================================================== --- py/branch/event/py/test2/testing/test_boxing.py (original) +++ py/branch/event/py/io/test/test_boxing.py Fri Jul 18 23:10:43 2008 @@ -1,82 +1,95 @@ - -""" test boxing functionality -""" - import py, sys, os -if sys.platform == 'win32': - py.test.skip("rsession is unsupported on Windows.") - -from py.__.test2.box import Box - def setup_module(mod): - tmpdir = py.test2.ensuretemp("boxtests") - mod.config = py.test2.config._reparse([tmpdir]) - -def test_basic_boxing(): - # XXX: because we do not have option transfer -## if not hasattr(option, 'nocapture') or not option.nocapture: -## py.test.skip("Interacts with pylib i/o skipping which is bad actually") - b = Box(boxf1, config=config) - b.run() - assert b.stdoutrepr == "some out\n" - assert b.stderrrepr == "some err\n" - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 1 - -def test_boxing_on_fds(): - b = Box(boxf2, config=config) - b.run() - assert b.stdoutrepr == "someout" - assert b.stderrrepr == "someerr" - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 2 - -def test_boxing_signal(): - b = Box(boxseg, config=config) - b.run() - assert b.retval is None + if not hasattr(os, 'fork'): + py.test.skip("forkedfunc requires os.fork") + mod.tmpdir = py.test2.ensuretemp(mod.__file__) + +def test_waitfinish_removes_tempdir(): + ff = py.io.ForkedFunc(boxf1) + assert ff.tempdir.check() + ff.waitfinish() + assert not ff.tempdir.check() + +def test_tempdir_gets_gc_collected(): + ff = py.io.ForkedFunc(boxf1) + assert ff.tempdir.check() + ff.__del__() + assert not ff.tempdir.check() + os.waitpid(ff.pid, 0) + +def test_basic_forkedfunc(): + result = py.io.ForkedFunc(boxf1).waitfinish() + assert result.out == "some out\n" + assert result.err == "some err\n" + assert result.exitstatus == 0 + assert result.signal == 0 + assert result.retval == 1 + +def test_exitstatus(): + def func(): + os._exit(3) + result = py.io.ForkedFunc(func).waitfinish() + assert result.exitstatus == 3 + assert result.signal == 0 + assert not result.out + assert not result.err + +def test_execption_in_func(): + def fun(): + raise ValueError(42) + result = py.io.ForkedFunc(fun).waitfinish() + assert result.exitstatus == 3 + assert result.err.find("ValueError: 42") != -1 + assert result.signal == 0 + assert not result.retval + +def test_forkedfunc_on_fds(): + result = py.io.ForkedFunc(boxf2).waitfinish() + assert result.out == "someout" + assert result.err == "someerr" + assert result.exitstatus == 0 + assert result.signal == 0 + assert result.retval == 2 + +def test_forkedfunc_signal(): + result = py.io.ForkedFunc(boxseg).waitfinish() + assert result.retval is None if py.std.sys.version_info < (2,4): py.test.skip("signal detection does not work with python prior 2.4") - assert b.signal == 11 + assert result.signal == 11 -def test_boxing_huge_data(): - b = Box(boxhuge, config=config) - b.run() - assert b.stdoutrepr - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 3 +def test_forkedfunc_huge_data(): + result = py.io.ForkedFunc(boxhuge).waitfinish() + assert result.out + assert result.exitstatus == 0 + assert result.signal == 0 + assert result.retval == 3 def test_box_seq(): # we run many boxes with huge data, just one after another - for i in xrange(100): - b = Box(boxhuge, config=config) - b.run() - assert b.stdoutrepr - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 3 + for i in xrange(50): + result = py.io.ForkedFunc(boxhuge).waitfinish() + assert result.out + assert result.exitstatus == 0 + assert result.signal == 0 + assert result.retval == 3 def test_box_in_a_box(): def boxfun(): - b = Box(boxf2, config=config) - b.run() - print b.stdoutrepr - print >>sys.stderr, b.stderrrepr - return b.retval + result = py.io.ForkedFunc(boxf2).waitfinish() + print result.out + print >>sys.stderr, result.err + return result.retval - b = Box(boxfun, config=config) - b.run() - assert b.stdoutrepr == "someout\n" - assert b.stderrrepr == "someerr\n" - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 2 + result = py.io.ForkedFunc(boxfun).waitfinish() + assert result.out == "someout\n" + assert result.err == "someerr\n" + assert result.exitstatus == 0 + assert result.signal == 0 + assert result.retval == 2 -def test_box_killer(): +def test_kill_func_forked(): class A: pass info = A() @@ -85,13 +98,13 @@ def box_fun(): time.sleep(10) # we don't want to last forever here - b = Box(box_fun, config=config) - par, pid = b.run(continuation=True) - os.kill(pid, 15) - par(pid) + ff = py.io.ForkedFunc(box_fun) + os.kill(ff.pid, 15) + result = ff.waitfinish() if py.std.sys.version_info < (2,4): py.test.skip("signal detection does not work with python prior 2.4") - assert b.signal == 15 + assert result.signal == 15 + # ====================================================================== Deleted: /py/branch/event/py/test2/box.py ============================================================================== --- /py/branch/event/py/test2/box.py Fri Jul 18 23:10:43 2008 +++ (empty file) @@ -1,120 +0,0 @@ - -""" boxing - wrapping process with another process so we can run -a process inside and see if it crashes -""" - -import py -import os -import sys -import marshal -from py.__.test2 import config as pytestconfig - -PYTESTSTDOUT = "pyteststdout" -PYTESTSTDERR = "pyteststderr" -PYTESTRETVAL = "pytestretval" - -import tempfile -import itertools -from StringIO import StringIO - -counter = itertools.count().next - -class FileBox(object): - def __init__(self, fun, args=None, kwargs=None, config=None): - if args is None: - args = [] - if kwargs is None: - kwargs = {} - self.fun = fun - self.config = config - assert self.config - self.args = args - self.kwargs = kwargs - - def run(self, continuation=False): - # XXX we should not use py.test2.ensuretemp here - count = counter() - tempdir = py.test2.ensuretemp("box%d" % count) - self.tempdir = tempdir - self.PYTESTRETVAL = tempdir.join('retval') - self.PYTESTSTDOUT = tempdir.join('stdout') - self.PYTESTSTDERR = tempdir.join('stderr') - - nice_level = self.config.getvalue('dist_nicelevel') - pid = os.fork() - if pid: - if not continuation: - self.parent(pid) - else: - return self.parent, pid - else: - try: - outcome = self.children(nice_level) - except: - excinfo = py.code.ExceptionInfo() - x = open("/tmp/traceback", "w") - print >>x, "Internal box error" - for i in excinfo.traceback: - print >>x, str(i)[2:-1] - print >>x, excinfo - x.close() - os._exit(1) - os.close(1) - os.close(2) - os._exit(0) - return pid - - def children(self, nice_level): - # right now we need to call a function, but first we need to - # map all IO that might happen - # make sure sys.stdout points to file descriptor one - sys.stdout = stdout = self.PYTESTSTDOUT.open('w') - sys.stdout.flush() - fdstdout = stdout.fileno() - if fdstdout != 1: - os.dup2(fdstdout, 1) - sys.stderr = stderr = self.PYTESTSTDERR.open('w') - fdstderr = stderr.fileno() - if fdstderr != 2: - os.dup2(fdstderr, 2) - retvalf = self.PYTESTRETVAL.open("w") - try: - if nice_level: - os.nice(nice_level) - # with fork() we have duplicated py.test's basetemp - # directory so we want to set it manually here. - # this may be expensive for some test setups, - # but that is what you get with boxing. - # XXX but we are called in more than strict boxing - # mode ("AsyncExecutor") so we can't do the following without - # inflicting on --dist speed, hum: - # pytestconfig.basetemp = self.tempdir.join("childbasetemp") - retval = self.fun(*self.args, **self.kwargs) - retvalf.write(marshal.dumps(retval)) - finally: - stdout.close() - stderr.close() - retvalf.close() - os._exit(0) - - def parent(self, pid, waiter=os.waitpid): - pid, exitstat = waiter(pid, 0) - self.signal = exitstat & 0x7f - self.exitstat = exitstat & 0xff00 - - - if not exitstat: - retval = self.PYTESTRETVAL.open() - try: - retval_data = retval.read() - finally: - retval.close() - self.retval = marshal.loads(retval_data) - else: - self.retval = None - - self.stdoutrepr = self.PYTESTSTDOUT.read() - self.stderrrepr = self.PYTESTSTDERR.read() - return self.stdoutrepr, self.stderrrepr - -Box = FileBox Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Fri Jul 18 23:10:43 2008 @@ -3,7 +3,6 @@ import py, os, sys -from py.__.test2.box import Box from py.__.test2 import repevent from py.__.test2.outcome import Skipped, Failed from py.__.test2 import repevent, present @@ -40,7 +39,7 @@ excinfo = None return repevent.ItemTestReport.fromitem(self.item, excinfo, self.outerr) -class BoxExecutor(RunExecutor): +class ForkExecutor(RunExecutor): """ Same as RunExecutor, but boxes test instead """ def execute_forked(self): @@ -48,39 +47,23 @@ return testrep.dumps() def execute(self): - b = Box(self.execute_forked, config=self.config) - pid = b.run() - assert pid - return self.maketestreport(b) - - def maketestreport(self, b): - if b.retval is not None: - testrep = repevent.ItemTestReport.fromdumps(b.retval) - testrep.stdout = b.stdoutrepr - testrep.stderr = b.stderrrepr + ff = py.io.ForkedFunc(self.execute_forked) + result = ff.waitfinish() + return self.maketestreport(result) + + def maketestreport(self, result): + if result.retval is not None: + testrep = repevent.ItemTestReport.fromdumps(result.retval) + testrep.stdout = result.out + testrep.stderr = result.err else: testrep = repevent.ItemTestReport.fromitem(self.item, None, None) testrep.outcome = "failed" - testrep.stdout = b.stdoutrepr - testrep.stderr = b.stderrrepr - testrep.signal = b.signal + testrep.stdout = result.out + testrep.stderr = result.err + testrep.signal = result.err return testrep -class AsyncExecutor(BoxExecutor): - """ same as box executor, but instead it returns function to continue - computations (more async mode) - """ - def execute(self): - b = Box(self.execute_forked, config=self.config) - parent, pid = b.run(continuation=True) - def cont(waiter=os.waitpid): - parent(pid, waiter=waiter) - return self.maketestreport(b) - - # XXX does not follow normal execute() signature! - # thus cannot be used like this anyway - return cont, pid - class ApigenExecutor(RunExecutor): """ Same as RunExecutor, but takes tracer to trace calls as an argument to execute @@ -108,6 +91,6 @@ def getexecutor(item, config): cls = RunExecutor if config.option.boxed: - cls = BoxExecutor + cls = ForkExecutor return cls(item, config) Deleted: /py/branch/event/py/test2/testing/test_boxing.py ============================================================================== --- /py/branch/event/py/test2/testing/test_boxing.py Fri Jul 18 23:10:43 2008 +++ (empty file) @@ -1,124 +0,0 @@ - -""" test boxing functionality -""" - -import py, sys, os - -if sys.platform == 'win32': - py.test.skip("rsession is unsupported on Windows.") - -from py.__.test2.box import Box - -def setup_module(mod): - tmpdir = py.test2.ensuretemp("boxtests") - mod.config = py.test2.config._reparse([tmpdir]) - -def test_basic_boxing(): - # XXX: because we do not have option transfer -## if not hasattr(option, 'nocapture') or not option.nocapture: -## py.test.skip("Interacts with pylib i/o skipping which is bad actually") - b = Box(boxf1, config=config) - b.run() - assert b.stdoutrepr == "some out\n" - assert b.stderrrepr == "some err\n" - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 1 - -def test_boxing_on_fds(): - b = Box(boxf2, config=config) - b.run() - assert b.stdoutrepr == "someout" - assert b.stderrrepr == "someerr" - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 2 - -def test_boxing_signal(): - b = Box(boxseg, config=config) - b.run() - assert b.retval is None - if py.std.sys.version_info < (2,4): - py.test.skip("signal detection does not work with python prior 2.4") - assert b.signal == 11 - -def test_boxing_huge_data(): - b = Box(boxhuge, config=config) - b.run() - assert b.stdoutrepr - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 3 - -def test_box_seq(): - # we run many boxes with huge data, just one after another - for i in xrange(100): - b = Box(boxhuge, config=config) - b.run() - assert b.stdoutrepr - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 3 - -def test_box_in_a_box(): - def boxfun(): - b = Box(boxf2, config=config) - b.run() - print b.stdoutrepr - print >>sys.stderr, b.stderrrepr - return b.retval - - b = Box(boxfun, config=config) - b.run() - assert b.stdoutrepr == "someout\n" - assert b.stderrrepr == "someerr\n" - assert b.exitstat == 0 - assert b.signal == 0 - assert b.retval == 2 - -def test_box_killer(): - class A: - pass - info = A() - import time - - def box_fun(): - time.sleep(10) # we don't want to last forever here - - b = Box(box_fun, config=config) - par, pid = b.run(continuation=True) - os.kill(pid, 15) - par(pid) - if py.std.sys.version_info < (2,4): - py.test.skip("signal detection does not work with python prior 2.4") - assert b.signal == 15 - - -# ====================================================================== -# examples -# ====================================================================== -# - -def boxf1(): - print "some out" - print >>sys.stderr, "some err" - return 1 - -def boxf2(): - os.write(1, "someout") - os.write(2, "someerr") - return 2 - -def boxseg(): - os.kill(os.getpid(), 11) - -def boxhuge(): - os.write(1, " " * 10000) - os.write(2, " " * 10000) - os.write(1, " " * 10000) - - os.write(1, " " * 10000) - os.write(2, " " * 10000) - os.write(2, " " * 10000) - os.write(1, " " * 10000) - return 3 Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Fri Jul 18 23:10:43 2008 @@ -1,8 +1,7 @@ import py -from py.__.test2.executor import RunExecutor, BoxExecutor -from py.__.test2.executor import AsyncExecutor, ApigenExecutor +from py.__.test2.executor import RunExecutor, ForkExecutor, ApigenExecutor from py.__.test2.rsession.testing.basetest import BasicRsessionTest def setup_module(mod): @@ -56,16 +55,8 @@ testrep = self.exrun("raisesfails") assert testrep.failed -class TestBoxExecutor(TestExecutor): - Executor = BoxExecutor - -class TestAsyncExecutor(TestExecutor): - Executor = AsyncExecutor - def exrun(self, examplename): - ex = self.getexecutor(examplename) - cont, pid = ex.execute() - testrep = cont() - return testrep +class TestForkExecutor(TestExecutor): + Executor = ForkExecutor class TestApigenExecutor(TestExecutor): Executor = ApigenExecutor From hpk at codespeak.net Fri Jul 18 23:31:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 23:31:35 +0200 (CEST) Subject: [py-svn] r56652 - py/branch/event/py/test2/rep Message-ID: <20080718213135.C657A168020@codespeak.net> Author: hpk Date: Fri Jul 18 23:31:34 2008 New Revision: 56652 Modified: py/branch/event/py/test2/rep/terminal.py Log: nicefy py.test2 output a bit Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 18 23:31:34 2008 @@ -1,3 +1,4 @@ +import py import sys from py.__.test2 import repevent @@ -6,13 +7,19 @@ self.config = config self.config.bus.subscribe(self.consume) self.currentfspath = None + self.out = py.io.TerminalWriter(sys.stdout) def consume(self, ev): if isinstance(ev, repevent.ItemTestReport): fspath, modpath = ev.repr_path if fspath != self.currentfspath: - print - sys.stdout.write(fspath) + self.out.line() + self.out.write(fspath) self.currentfspath = fspath - sys.stdout.write(".") - sys.stdout.flush() + self.out.write(".") + elif isinstance(ev, repevent.SessionFinish): + self.out.line() + self.out.sep("=", "finish") + elif isinstance(ev, repevent.SessionStart): + self.out.sep("=", "session starts") + From hpk at codespeak.net Fri Jul 18 23:49:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 18 Jul 2008 23:49:20 +0200 (CEST) Subject: [py-svn] r56653 - py/branch/event/py/test2/rep Message-ID: <20080718214920.090DE16A043@codespeak.net> Author: hpk Date: Fri Jul 18 23:49:19 2008 New Revision: 56653 Modified: py/branch/event/py/test2/rep/terminal.py Log: extending py.test2 for the fun of it (but this all needs to be done slightly differently and with tests, but i'd like to see how things can look like after all this dry TDD refactoring work ...) Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 18 23:49:19 2008 @@ -8,18 +8,47 @@ self.config.bus.subscribe(self.consume) self.currentfspath = None self.out = py.io.TerminalWriter(sys.stdout) + self._failed = [] + self._numskipped = 0 + self._numpassed = 0 def consume(self, ev): if isinstance(ev, repevent.ItemTestReport): fspath, modpath = ev.repr_path if fspath != self.currentfspath: self.out.line() - self.out.write(fspath) + self.out.write(fspath + " ") self.currentfspath = fspath - self.out.write(".") + self.out.write(self._getletter(ev)) + self.record(ev) elif isinstance(ev, repevent.SessionFinish): self.out.line() - self.out.sep("=", "finish") + self.print_failures() + self.out.sep("=", "%d tests of which %d failed, %d skipped" %( + len(self._failed) + self._numskipped + self._numpassed, + len(self._failed), self._numskipped)) elif isinstance(ev, repevent.SessionStart): self.out.sep("=", "session starts") - + + def record(self, ev): + if ev.failed: + self._failed.append(ev) + elif ev.skipped: + self._numskipped += 1 + else: + self._numpassed += 1 + + def print_failures(self): + if not self._failed: + return + for ev in self._failed: + #self.out.sep("_", "entrypoint: %s" %(ev.repr_path[1],)) + self.out.line(ev.repr_failure) + + def _getletter(self, ev): + if ev.passed: + return "." + elif ev.skipped: + return "s" + elif ev.failed: + return "F" From hpk at codespeak.net Sat Jul 19 12:24:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 12:24:10 +0200 (CEST) Subject: [py-svn] r56663 - py/trunk/py/doc Message-ID: <20080719102410.E55662A00E2@codespeak.net> Author: hpk Date: Sat Jul 19 12:24:09 2008 New Revision: 56663 Added: py/trunk/py/doc/release-notes-1.0.0 Modified: py/trunk/py/doc/TODO.txt Log: some more planning and notes for 1.0 Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Jul 19 12:24:09 2008 @@ -1,6 +1,24 @@ Things to do for 1.0.0 ========================= +packaging +------------------------------------- + +* setup automatical dist snapshots from trunk + when tests pass (at best for all of windows, + linux and osx). + +* ensure compatibility with Python 2.3 - 2.6 + +* package c-extensions (greenlet) for windows + +* support installation from svn, setup.py and via easy_install + +* debian and TAR/zip packages for py lib, + particularly look into C module issues (greenlet most importantly) + +* (partly DONE) refine and implement releasescheme/download + py.test -------------- @@ -30,7 +48,7 @@ - porting existing extensions (htmlconftest / PyPy's conftest's ...) -- fix reporting/usage degradation after reporter-merge merge: +- fix reporting/usage regression after reporter-merge merge: - collapse skips with same reason and lineno into one line - fix and investigate win32 failures @@ -53,6 +71,7 @@ - review source code and rename some internal methods to help with un-confusing things + py.execnet -------------- @@ -75,15 +94,20 @@ http://codeinvestigator.googlepages.com/main +py.io +-------------------- + +- write documentation about it +- deprecate py.process.cmdexec, move it to py.io.cmdexec() + merge exceptions/handling of py.io.cmdexec, py.io.forkedfunc + ld (review and shift to above) ================================= refactorings ------------------ -- session / collection unification (particularly tryiter and buildname2items) - -- reporting unification, i.e. use dist-testing Reporter class +- (ongoing) reporting unification, i.e. use dist-testing Reporter class also for "normal" session, consider introduction of tkinter session (M978) @@ -138,25 +162,6 @@ svn-bindings (M634) -packaging -------------------------------------- - -* debian and TAR/zip packages for py lib, - particularly look into C module issues (greenlet most importantly) - -* do something about c-extensions both on unix-ish - versus win32 systems - -* ensure compatibility with Python 2.3 - 2.5, - see what is missing for 2.2 - -* optional: support setuptools (eggs?) installs, and instally - from pypi (and register pylib there) - -* (DONE/c-modules don't) see if things work on Win32 (partially done) - -* (partly DONE) refine and implement releasescheme/download - APIGEN / source viewer ------------------------------------- Added: py/trunk/py/doc/release-notes-1.0.0 ============================================================================== --- (empty file) +++ py/trunk/py/doc/release-notes-1.0.0 Sat Jul 19 12:24:09 2008 @@ -0,0 +1,9 @@ + +changes from 0.9.1 to 1.0 + +py.test: + +- collector.run() deprecated, implement/use listdir() instead + + + From hpk at codespeak.net Sat Jul 19 12:32:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 12:32:02 +0200 (CEST) Subject: [py-svn] r56664 - py/branch/event/py/test2 Message-ID: <20080719103202.DEC772D8001@codespeak.net> Author: hpk Date: Sat Jul 19 12:32:02 2008 New Revision: 56664 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/executor.py py/branch/event/py/test2/item.py Log: review, add some comments, shift methods Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sat Jul 19 12:32:02 2008 @@ -27,7 +27,7 @@ import py from py.__.test2 import present -sysex = (KeyboardInterrupt, SystemExit) +sysex = (KeyboardInterrupt, SystemExit) # XXX GeneratorExit? def configproperty(name): def fget(self): @@ -38,8 +38,7 @@ class Node(object): """ base class for Nodes in the collection tree. Nodes with Children are "Collectors" and - leaves (without children) are runnable - Test Items. + leaves are runnable Test Items. """ def __init__(self, name, parent=None, config=None): self.name = name @@ -52,16 +51,18 @@ def __repr__(self): return "<%s %r>" %(self.__class__.__name__, self.name) + # methods for ordering nodes + def __eq__(self, other): if not isinstance(other, Node): return False return self.name == other.name and self.parent == other.parent + + def __ne__(self, other): + return not self == other def __hash__(self): return hash((self.name, self.parent)) - - def __ne__(self, other): - return not self == other def __cmp__(self, other): if not isinstance(other, Node): @@ -69,16 +70,12 @@ s1 = self._getsortvalue() s2 = other._getsortvalue() return cmp(s1, s2) - - def multijoin(self, namelist): - """ return a list of colitems for the given namelist. """ - return [self.join(name) for name in namelist] - + def setup(self): - pass + pass def teardown(self): - pass + pass def listchain(self): """ return list of all parent collectors up to self. """ @@ -189,6 +186,10 @@ py.std.warnings.warn("deprecated: use listdir()", category=DeprecationWarning) return self.listdir() + def multijoin(self, namelist): + """ return a list of colitems for the given namelist. """ + return [self.join(name) for name in namelist] + def listdir(self): """ returns a list of names available from this collector. You can return an empty list. Callers of this method Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Sat Jul 19 12:32:02 2008 @@ -4,8 +4,7 @@ import py, os, sys from py.__.test2 import repevent -from py.__.test2.outcome import Skipped, Failed -from py.__.test2 import repevent, present +from py.__.test2.outcome import Skipped import py.__.test2.custompdb sysex = (KeyboardInterrupt, SystemExit) Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sat Jul 19 12:32:02 2008 @@ -45,6 +45,9 @@ xxx def runtest(self): + """ Return an ItemTestReport event which has all the + information about running the underlying test item. + """ from py.__.test2.executor import getexecutor executor = getexecutor(self, self._config) return executor.execute() From hpk at codespeak.net Sat Jul 19 12:41:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 12:41:17 +0200 (CEST) Subject: [py-svn] r56665 - in py/branch/event/py: . builtin builtin/testing Message-ID: <20080719104117.51B15318001@codespeak.net> Author: hpk Date: Sat Jul 19 12:41:16 2008 New Revision: 56665 Modified: py/branch/event/py/__init__.py py/branch/event/py/builtin/exception.py py/branch/event/py/builtin/testing/test_exception.py Log: add py.builtin.sysex allowing to write: try: ... except py.builtin.sysex: # KeyboardInterrupt, SystemExit, Generatorexit raise except: portably across python versions. Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Sat Jul 19 12:41:16 2008 @@ -112,6 +112,7 @@ 'builtin.sorted' : ('./builtin/sorted.py', 'sorted'), 'builtin.BaseException' : ('./builtin/exception.py', 'BaseException'), 'builtin.GeneratorExit' : ('./builtin/exception.py', 'GeneratorExit'), + 'builtin.sysex' : ('./builtin/exception.py', 'sysex'), 'builtin.set' : ('./builtin/set.py', 'set'), 'builtin.frozenset' : ('./builtin/set.py', 'frozenset'), Modified: py/branch/event/py/builtin/exception.py ============================================================================== --- py/branch/event/py/builtin/exception.py (original) +++ py/branch/event/py/builtin/exception.py Sat Jul 19 12:41:16 2008 @@ -12,3 +12,5 @@ versions.""" pass GeneratorExit.__module__ = 'exceptions' + +sysex = (KeyboardInterrupt, SystemExit, GeneratorExit) Modified: py/branch/event/py/builtin/testing/test_exception.py ============================================================================== --- py/branch/event/py/builtin/testing/test_exception.py (original) +++ py/branch/event/py/builtin/testing/test_exception.py Sat Jul 19 12:41:16 2008 @@ -16,3 +16,10 @@ def test_GeneratorExit(): assert py.builtin.GeneratorExit.__module__ == 'exceptions' assert issubclass(py.builtin.GeneratorExit, Exception) + +def test__sysexceptions(): + sysex = py.builtin.sysex + assert SystemExit in sysex + assert KeyboardInterrupt in sysex + assert py.builtin.GeneratorExit in sysex + assert len(sysex) == 3 From hpk at codespeak.net Sat Jul 19 12:45:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 12:45:30 +0200 (CEST) Subject: [py-svn] r56666 - in py/branch/event/py: execnet test2 Message-ID: <20080719104530.1305A318001@codespeak.net> Author: hpk Date: Sat Jul 19 12:45:29 2008 New Revision: 56666 Modified: py/branch/event/py/execnet/gateway.py py/branch/event/py/test2/collect.py py/branch/event/py/test2/executor.py py/branch/event/py/test2/genitem.py Log: use py.builtin.sysex Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Sat Jul 19 12:45:29 2008 @@ -26,8 +26,6 @@ import os debug = 0 # open('/tmp/execnet-debug-%d' % os.getpid() , 'wa') -sysex = (KeyboardInterrupt, SystemExit) - # ---------------------------------------------------------- # cleanup machinery (for exiting processes) # ---------------------------------------------------------- @@ -105,7 +103,7 @@ for x in l: print >>debug, x debug.flush() - except sysex: + except py.builtin.sysex: raise except: traceback.print_exc() @@ -128,8 +126,8 @@ msg = Message.readfrom(self._io) self._trace("received <- %r" % msg) msg.received(self) - except sysex: - break + except py.builtin.sysex: + break # XXX? except EOFError: break except: Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sat Jul 19 12:45:29 2008 @@ -27,8 +27,6 @@ import py from py.__.test2 import present -sysex = (KeyboardInterrupt, SystemExit) # XXX GeneratorExit? - def configproperty(name): def fget(self): #print "retrieving %r property from %s" %(name, self.fspath) Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Sat Jul 19 12:45:29 2008 @@ -7,8 +7,6 @@ from py.__.test2.outcome import Skipped import py.__.test2.custompdb -sysex = (KeyboardInterrupt, SystemExit) - class RunExecutor(object): """ Same as in executor, but just running run """ @@ -28,7 +26,7 @@ def execute(self): try: self.runitem() - except sysex: + except py.builtin.sysex: raise except: excinfo = py.code.ExceptionInfo() Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Sat Jul 19 12:45:29 2008 @@ -1,7 +1,6 @@ import py from py.__.test2 import repevent -sysex = (KeyboardInterrupt, SystemExit) def genitems(config, colitems, keywordexpr=None, stopitems=(py.test2.collect.Item,)): @@ -24,7 +23,7 @@ cols = [next.join(x) for x in next.listdir()] for x in genitems(config, cols, keywordexpr): yield x - except sysex: + except py.builtin.sysex: raise except: excinfo = py.code.ExceptionInfo() From hpk at codespeak.net Sat Jul 19 13:00:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 13:00:36 +0200 (CEST) Subject: [py-svn] r56667 - in py/branch/event/py/test2: . testing Message-ID: <20080719110036.8F3C1318009@codespeak.net> Author: hpk Date: Sat Jul 19 13:00:35 2008 New Revision: 56667 Modified: py/branch/event/py/test2/executor.py py/branch/event/py/test2/item.py py/branch/event/py/test2/testing/test_executor.py Log: strike redunant config argument Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Sat Jul 19 13:00:35 2008 @@ -10,9 +10,9 @@ class RunExecutor(object): """ Same as in executor, but just running run """ - def __init__(self, item, config): + def __init__(self, item): self.item = item - self.config = config + self.config = item._config self.outerr = None def runitem(self): @@ -65,8 +65,8 @@ """ Same as RunExecutor, but takes tracer to trace calls as an argument to execute """ - def __init__(self, item, config, tracer): - super(ApigenExecutor, self).__init__(item, config) + def __init__(self, item, tracer): + super(ApigenExecutor, self).__init__(item) self.tracer = tracer def runitem(self): @@ -85,9 +85,9 @@ finally: self.item.execute = orig_exec -def getexecutor(item, config): +def getexecutor(item): cls = RunExecutor - if config.option.boxed: + if item._config.option.boxed: cls = ForkExecutor - return cls(item, config) + return cls(item) Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sat Jul 19 13:00:35 2008 @@ -49,7 +49,7 @@ information about running the underlying test item. """ from py.__.test2.executor import getexecutor - executor = getexecutor(self, self._config) + executor = getexecutor(self) return executor.execute() class Function(FunctionMixin, Item): Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sat Jul 19 13:00:35 2008 @@ -13,7 +13,7 @@ def getexecutor(self, examplename): funcitem = self.getfunc(examplename) - return self.Executor(funcitem, self.config) + return self.Executor(funcitem) def exrun(self, examplename): ex = self.getexecutor(examplename) @@ -74,7 +74,7 @@ def getexecutor(self, examplename, Tracer=Tracer): funcitem = self.getfunc(examplename) - return self.Executor(funcitem, self.config, tracer=Tracer()) + return self.Executor(funcitem, tracer=Tracer()) def test_apigen_executor_tracing_hook(self): tmpdir = py.test2.ensuretemp("apigen_executor") @@ -100,13 +100,13 @@ rootcol = config._getcollector(tmpdir) tracer = self.Tracer() item = rootcol._getitembynames("test_one.py/test_1") - ex = ApigenExecutor(item, config, tracer) + ex = ApigenExecutor(item, tracer) out1 = ex.execute() item = rootcol._getitembynames("test_one.py/TestX/()/test_one") - ex = ApigenExecutor(item, config, tracer) + ex = ApigenExecutor(item, tracer) out2 = ex.execute() item = rootcol._getitembynames("test_one.py/TestX/()/test_raise") - ex = ApigenExecutor(item, config, tracer) + ex = ApigenExecutor(item, tracer) out3 = ex.execute() assert tracer.starts == 3 assert tracer.ends == 3 From hpk at codespeak.net Sat Jul 19 13:06:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 13:06:22 +0200 (CEST) Subject: [py-svn] r56668 - in py/branch/event/py/io: test testing Message-ID: <20080719110622.33D9131800B@codespeak.net> Author: hpk Date: Sat Jul 19 13:06:21 2008 New Revision: 56668 Added: py/branch/event/py/io/testing/ - copied from r56624, py/branch/event/py/io/test/ py/branch/event/py/io/testing/test_boxing.py - copied unchanged from r56667, py/branch/event/py/io/test/test_boxing.py py/branch/event/py/io/testing/test_terminalwriter.py - copied unchanged from r56667, py/branch/event/py/io/test/test_terminalwriter.py Removed: py/branch/event/py/io/test/ Log: fix to follow naming convention From hpk at codespeak.net Sat Jul 19 13:10:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 13:10:37 +0200 (CEST) Subject: [py-svn] r56669 - py/branch/event/py/test2 Message-ID: <20080719111037.8DD95168552@codespeak.net> Author: hpk Date: Sat Jul 19 13:10:36 2008 New Revision: 56669 Modified: py/branch/event/py/test2/repevent.py Log: recognizing py.test.skip for now Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sat Jul 19 13:10:36 2008 @@ -4,6 +4,8 @@ import py import time from py.__.test2.outcome import Skipped +# XXX the below line helps to deal with the py/test py/test2 situation +from py.__.test.outcome import Skipped as Skipped2 class BaseEvent(object): @@ -64,7 +66,7 @@ trail = item._get_collector_trail() repr_failure = None if excinfo: - if excinfo.errisinstance(Skipped): + if excinfo.errisinstance((Skipped, Skipped2)): outcome = "skipped" else: outcome = "failed" From hpk at codespeak.net Sat Jul 19 16:42:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 16:42:35 +0200 (CEST) Subject: [py-svn] r56675 - in py/branch/event/py/test2: rep/testing rsession rsession/testing Message-ID: <20080719144235.EEDC2169E1B@codespeak.net> Author: hpk Date: Sat Jul 19 16:42:33 2008 New Revision: 56675 Modified: py/branch/event/py/test2/rep/testing/test_reporter.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/testing/test_hostmanage.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/rsession/testing/test_rsession.py Log: some internal renaming, HostInfo -> Host Modified: py/branch/event/py/test2/rep/testing/test_reporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_reporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_reporter.py Sat Jul 19 16:42:33 2008 @@ -24,7 +24,7 @@ from py.__.test2.reporter import RemoteReporter, LocalReporter, choose_reporter from py.__.test2 import repevent from py.__.test2.outcome import ReprOutcome, SerializableOutcome -from py.__.test2.rsession.hostmanage import HostInfo +from py.__.test2.rsession.hostmanage import Host from py.__.test2.box import Box from py.__.test2.rsession.testing.basetest import BasicRsessionTest import sys @@ -189,7 +189,7 @@ tmpdir.ensure("__init__.py") cap = py.io.StdCaptureFD() config = py.test2.config._reparse([str(tmpdir)]) - hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] + hosts = [Host(i) for i in ["host1", "host2", "host3"]] for host in hosts: host.gw_remotepath = '' r = self.reporter(config, hosts) @@ -240,7 +240,7 @@ reporter = RemoteReporter def get_hosts(self): - return [HostInfo("host")] + return [Host("host")] def test_still_to_go(self): self._test_still_to_go() Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Sat Jul 19 16:42:33 2008 @@ -11,7 +11,7 @@ from py.__.test2 import repevent from py.__.test2.rsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * -from py.__.test2.rsession.hostmanage import HostInfo +from py.__.test2.rsession.hostmanage import Host from py.__.test2.outcome import SerializableOutcome class Container(object): @@ -29,7 +29,7 @@ config = py.test2.config._reparse(["some_sub"]) config.option.verbose = False self.config = config - hosts = [HostInfo('localhost')] + hosts = [Host('localhost')] method.im_func.func_globals['ch'] = DummyChannel(hosts[0]) method.im_func.func_globals['reporter'] = r = RestReporter(config, hosts) @@ -55,14 +55,14 @@ 'localhost\n\n') def test_report_HostRSyncing(self): - event = repevent.HostRSyncing(HostInfo('localhost:/foo/bar'), "a", + event = repevent.HostRSyncing(Host('localhost:/foo/bar'), "a", "b", False) reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') def test_report_HostRSyncRootReady(self): - h = HostInfo('localhost') + h = Host('localhost') reporter.hosts_to_rsync = 1 reporter.report(repevent.HostGatewayReady(h, ["a"])) event = repevent.HostRSyncRootReady(h, "a") @@ -70,8 +70,8 @@ assert stdout.getvalue() == '::\n\n localhost: READY\n\n' def test_report_TestStarted(self): - event = repevent.TestStarted([HostInfo('localhost'), - HostInfo('foo.com')], + event = repevent.TestStarted([Host('localhost'), + Host('foo.com')], "aa", ["a", "b"]) reporter.report(event) assert stdout.getvalue() == """\ @@ -336,7 +336,7 @@ reporter = RestReporter def get_hosts(self): - return [HostInfo('localhost')] + return [Host('localhost')] def test_failed_to_load(self): py.test.skip("Not implemented") Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Sat Jul 19 16:42:33 2008 @@ -3,7 +3,7 @@ from py.__.test2.rsession.master import MasterNode from py.__.test2 import repevent -class HostInfo(object): +class Host(object): """ Host location representation for distributed testing. """ _hostname2list = {} @@ -53,7 +53,7 @@ self.gw_remotepath = channel.receive() def __str__(self): - return "" % (self.hostname, self.relpath) + return "" % (self.hostname, self.relpath) __repr__ = __str__ def __hash__(self): @@ -126,7 +126,7 @@ self.roots = roots if hosts is None: hosts = self.config.getvalue("dist_hosts") - hosts = [HostInfo(x, addrel) for x in hosts] + hosts = [Host(x, addrel) for x in hosts] self.hosts = hosts def prepare_gateways(self): Modified: py/branch/event/py/test2/rsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/rsession/testing/test_hostmanage.py Sat Jul 19 16:42:33 2008 @@ -4,49 +4,55 @@ import py from basetest import DirSetup -from py.__.test2.rsession.hostmanage import HostRSync, HostInfo, HostManager +from py.__.test2.rsession.hostmanage import HostRSync, Host, HostManager from py.__.test2.rsession.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test2 import repevent -class TestHostInfo(DirSetup): +class TestHost(DirSetup): def _gethostinfo(self, relpath=""): exampledir = self.tmpdir.join("gethostinfo") if relpath: exampledir = exampledir.join(relpath) assert not exampledir.check() - hostinfo = HostInfo("localhost:%s" % exampledir) + hostinfo = Host("localhost:%s" % exampledir) return hostinfo def test_defaultpath(self): - x = HostInfo("localhost:") + x = Host("localhost:") assert x.hostname == "localhost" assert not x.relpath def test_addrel(self): - host = HostInfo("localhost:", addrel="whatever") + host = Host("localhost:", addrel="whatever") assert host.inplacelocal assert not host.relpath - host = HostInfo("localhost:/tmp", addrel="base") + host = Host("localhost:/tmp", addrel="base") assert host.relpath == "/tmp/base" - host = HostInfo("localhost:tmp", addrel="base2") + host = Host("localhost:tmp", addrel="base2") assert host.relpath == "tmp/base2" def test_path(self): - x = HostInfo("localhost:/tmp") + x = Host("localhost:/tmp") assert x.relpath == "/tmp" assert x.hostname == "localhost" assert not x.inplacelocal + def test_equality(self): + x = Host("localhost:") + y = Host("localhost:") + assert x != y + assert not (x == y) + def test_hostid(self): - x = HostInfo("localhost:") - y = HostInfo("localhost:") + x = Host("localhost:") + y = Host("localhost:") assert x.hostid != y.hostid - x = HostInfo("localhost:/tmp") - y = HostInfo("localhost:") + x = Host("localhost:/tmp") + y = Host("localhost:") assert x.hostid != y.hostid def test_non_existing_hosts(self): - host = HostInfo("alskdjalsdkjasldkajlsd") + host = Host("alskdjalsdkjasldkajlsd") py.test2.raises((py.process.cmdexec.Error, IOError, EOFError), host.initgateway) @@ -68,7 +74,7 @@ old.chdir() def test_initgateway_localhost_relpath(self): - host = HostInfo("localhost:somedir") + host = Host("localhost:somedir") host.initgateway() assert host.gw try: @@ -82,7 +88,7 @@ option = py.test2.config.option if getattr(option, 'sshtarget', None) is None: py.test.skip("no known ssh target, use -S to set one") - host = HostInfo("%s" % (option.sshtarget, )) + host = Host("%s" % (option.sshtarget, )) # this test should be careful to not write/rsync anything # as the remotepath is the default location # and may be used in the real world @@ -102,7 +108,7 @@ class TestSyncing(DirSetup): def _gethostinfo(self): - hostinfo = HostInfo("localhost:%s" % self.dest) + hostinfo = Host("localhost:%s" % self.dest) return hostinfo def test_hrsync_filter(self): @@ -120,7 +126,7 @@ assert 'somedir' in basenames def test_hrsync_localhost_inplace(self): - h1 = HostInfo("localhost") + h1 = Host("localhost") events = [] rsync = HostRSync(self.source) h1.initgateway() @@ -216,7 +222,7 @@ """)) config = py.test2.config._reparse([self.source]) hm = HostManager(config, - hosts=[HostInfo("localhost:" + str(self.dest))]) + hosts=[Host("localhost:" + str(self.dest))]) hm.init_rsync() assert self.dest.join("dir2").check() assert not self.dest.join("dir1").check() @@ -232,7 +238,7 @@ """)) config = py.test2.config._reparse([self.source]) hm = HostManager(config, - hosts=[HostInfo("localhost:" + str(self.dest))]) + hosts=[Host("localhost:" + str(self.dest))]) hm.init_rsync() assert self.dest.join("dir1").check() assert not self.dest.join("dir1", "dir2").check() @@ -240,7 +246,7 @@ assert not self.dest.join("dir6").check() def test_hostmanage_optimise_localhost(self): - hosts = [HostInfo("localhost") for i in range(3)] + hosts = [Host("localhost") for i in range(3)] config = py.test2.config._reparse([self.source]) hm = HostManager(config, hosts=hosts) hm.init_rsync() @@ -254,8 +260,8 @@ option = py.test2.config.option if option.sshtarget is None: py.test.skip("no known ssh target, use -S to set one") - host1 = HostInfo("%s" % (option.sshtarget, )) - host2 = HostInfo("%s" % (option.sshtarget, )) + host1 = Host("%s" % (option.sshtarget, )) + host2 = Host("%s" % (option.sshtarget, )) hm = HostManager(config, hosts=[host1, host2]) events = [] hm.init_rsync(events.append) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Sat Jul 19 16:42:33 2008 @@ -1,7 +1,7 @@ import py from py.__.test2.rsession.master import MasterNode -from py.__.test2.rsession.hostmanage import HostInfo +from py.__.test2.rsession.hostmanage import Host from basetest import BasicRsessionTest from py.__.test2 import repevent @@ -16,7 +16,7 @@ return queue def test_node_down(self): - host = HostInfo("localhost") + host = Host("localhost") host.initgateway() node = MasterNode(host, self.config) assert not node.channel.isclosed() @@ -27,7 +27,7 @@ assert event.host == host def test_send_one(self): - host = HostInfo("localhost") + host = Host("localhost") host.initgateway() node = MasterNode(host, self.config) assert not node.channel.isclosed() @@ -39,7 +39,7 @@ assert not node.pending def test_send_multiple(self): - host = HostInfo("localhost") + host = Host("localhost") host.initgateway() node = MasterNode(host, self.config) assert not node.channel.isclosed() Modified: py/branch/event/py/test2/rsession/testing/test_rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_rsession.py (original) +++ py/branch/event/py/test2/rsession/testing/test_rsession.py Sat Jul 19 16:42:33 2008 @@ -5,7 +5,7 @@ import py from py.__.test2 import repevent from py.__.test2.rsession.rsession import RSession -from py.__.test2.rsession.hostmanage import HostManager, HostInfo +from py.__.test2.rsession.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup from py.__.test2.testing import suptest @@ -86,7 +86,7 @@ assert len([x for x in testevents if x.skipped]) == 0 def test_setup_teardown_run_ssh(self): - hosts = [HostInfo('localhost:%s' % self.dest)] + hosts = [Host('localhost:%s' % self.dest)] queue = py.std.Queue.Queue() self.config.bus.subscribe(queue.put) @@ -123,7 +123,7 @@ def test_nice_level(self): """ Tests if nice level behaviour is ok """ - hosts = [HostInfo('localhost:%s' % self.dest)] + hosts = [Host('localhost:%s' % self.dest)] tmpdir = self.source tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write(py.code.Source(""" From hpk at codespeak.net Sat Jul 19 17:51:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 17:51:29 +0200 (CEST) Subject: [py-svn] r56676 - py/branch/event/py/test2/testing Message-ID: <20080719155129.67D2B169FD4@codespeak.net> Author: hpk Date: Sat Jul 19 17:51:26 2008 New Revision: 56676 Removed: py/branch/event/py/test2/testing/test_session_xxx.py Log: these tests are redundant Deleted: /py/branch/event/py/test2/testing/test_session_xxx.py ============================================================================== --- /py/branch/event/py/test2/testing/test_session_xxx.py Sat Jul 19 17:51:26 2008 +++ (empty file) @@ -1,84 +0,0 @@ - -""" test of local version of py.test2 distributed -""" - -import py -from py.__.test2 import repevent -import suptest -import py.__.test2.custompdb -from py.__.test2.session import Session - -def check_has_fork(): - if not hasattr(py.std.os, 'fork'): - py.test.skip('platform does not support os.fork') - -def setup_module(mod): - mod.tmp = py.test2.ensuretemp(mod.__name__) - -class TestExampleDistribution(object): - # XXX: Some tests of that should be run as well on RSession, while - # some not at all - def example_distribution(self, boxed=False): - # XXX find a better way for the below - tfile = suptest.makeuniquepyfile(""" - def test_1(): - pass - def test_2(): - assert 0 - def test_3(): - raise ValueError(23) - def test_4(someargs): - pass - #def test_5(): - # import os - # os.kill(os.getpid(), 11) - """) - args = [tfile] - if boxed: - check_has_fork() - args.append('--boxed') - sorter = suptest.events_from_cmdline(args) - passed, skipped, failed = sorter.listoutcomes() - assert len(passed) == 1 - assert len(skipped) == 0 - assert len(failed) == 3 - #assert len(signalevents) == 1 - return - # XXX - tb = failevents[0].outcome.excinfo.traceback - assert str(tb[0].path).find("test_one") != -1 - assert str(tb[0].source).find("test_2") != -1 - assert failevents[0].outcome.excinfo.typename == 'AssertionError' - tb = failevents[1].outcome.excinfo.traceback - assert str(tb[0].path).find("test_one") != -1 - assert str(tb[0].source).find("test_3") != -1 - assert failevents[1].outcome.excinfo.typename == 'ValueError' - assert str(failevents[1].outcome.excinfo.value) == '23' - tb = failevents[2].outcome.excinfo.traceback - assert failevents[2].outcome.excinfo.typename == 'TypeError' - assert str(tb[0].path).find("executor") != -1 - assert str(tb[0].source).find("execute") != -1 - - def test_boxed(self): - check_has_fork() - self.example_distribution(True) - - def test_plain(self): - self.example_distribution(False) - - def test_box_exploding(self): - check_has_fork() - tfile = suptest.makeuniquepyfile(""" - def test_5(): - import os - os.kill(os.getpid(), 11) - """) - sorter = suptest.events_from_cmdline([tfile, '--boxed']) - passed, skipped, failed = sorter.listoutcomes() - py.test.skip("FIX BOXING event generation") - assert len(passed) == len(skipped) == 0 - assert len(failed) == 1 - py.test.skip("implement checking for signal outcomes") - #assert testevents[0].outcome.signal - - From hpk at codespeak.net Sat Jul 19 17:53:25 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 17:53:25 +0200 (CEST) Subject: [py-svn] r56677 - py/branch/event/py/test2/testing Message-ID: <20080719155325.85A5A2D8005@codespeak.net> Author: hpk Date: Sat Jul 19 17:53:25 2008 New Revision: 56677 Modified: py/branch/event/py/test2/testing/test_executor.py Log: only disable tests that require fork Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sat Jul 19 17:53:25 2008 @@ -4,9 +4,6 @@ from py.__.test2.executor import RunExecutor, ForkExecutor, ApigenExecutor from py.__.test2.rsession.testing.basetest import BasicRsessionTest -def setup_module(mod): - if py.std.sys.platform == "win32": - py.test.skip("skipping executor tests (some require os.fork)") class TestExecutor(BasicRsessionTest): Executor = RunExecutor @@ -56,6 +53,7 @@ assert testrep.failed class TestForkExecutor(TestExecutor): + disabled = (py.std.sys.platform == "win32") Executor = ForkExecutor class TestApigenExecutor(TestExecutor): From hpk at codespeak.net Sat Jul 19 17:56:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 17:56:10 +0200 (CEST) Subject: [py-svn] r56678 - py/branch/event/py/test2/testing Message-ID: <20080719155610.0B8CD318002@codespeak.net> Author: hpk Date: Sat Jul 19 17:56:09 2008 New Revision: 56678 Modified: py/branch/event/py/test2/testing/test_executor.py Log: rather skip, than disable Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sat Jul 19 17:56:09 2008 @@ -1,6 +1,5 @@ import py - from py.__.test2.executor import RunExecutor, ForkExecutor, ApigenExecutor from py.__.test2.rsession.testing.basetest import BasicRsessionTest @@ -53,7 +52,9 @@ assert testrep.failed class TestForkExecutor(TestExecutor): - disabled = (py.std.sys.platform == "win32") + def setup_class(cls): + if not hasattr(py.std.os, 'fork'): + py.test.skip("need os.fork()") Executor = ForkExecutor class TestApigenExecutor(TestExecutor): From hpk at codespeak.net Sat Jul 19 18:14:59 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 19 Jul 2008 18:14:59 +0200 (CEST) Subject: [py-svn] r56679 - py/branch/event/py/execnet Message-ID: <20080719161459.33709318002@codespeak.net> Author: hpk Date: Sat Jul 19 18:14:58 2008 New Revision: 56679 Modified: py/branch/event/py/execnet/gateway.py Log: hum, the 56 666 commit was evil (sic!) violated the rule of being careful when modifying py.execnet internals. Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Sat Jul 19 18:14:58 2008 @@ -7,13 +7,13 @@ import __future__ # note that the whole code of this module (as well as some -# other modules) execute not only on the local side but -# also on any gateway's remote side. On such remote sides -# we cannot assume the py library to be there and -# InstallableGateway._remote_bootstrap_gateway() (located -# in register.py) will take care to send source fragments -# to the other side. Yes, it is fragile but we have a -# few tests that try to catch when we mess up. +# other modules) execute on two sides: on the side of the +# local gateway as well as on the remote side. +# On such remote sides we do not assume the py library to +# be there and InstallableGateway._remote_bootstrap_gateway() +# (located # in register.py) will take care to send require +# source fragments # to the other side. Yes, it is fragile +# but we have a # few tests that try to catch when we mess up. # XXX the following lines should not be here if 'ThreadOut' not in globals(): @@ -26,6 +26,8 @@ import os debug = 0 # open('/tmp/execnet-debug-%d' % os.getpid() , 'wa') + +sysex = (SystemExit, KeyboardInterrupt) # ---------------------------------------------------------- # cleanup machinery (for exiting processes) # ---------------------------------------------------------- @@ -103,7 +105,7 @@ for x in l: print >>debug, x debug.flush() - except py.builtin.sysex: + except sysex: raise except: traceback.print_exc() @@ -126,7 +128,7 @@ msg = Message.readfrom(self._io) self._trace("received <- %r" % msg) msg.received(self) - except py.builtin.sysex: + except sysex: break # XXX? except EOFError: break From hpk at codespeak.net Sun Jul 20 13:47:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 13:47:12 +0200 (CEST) Subject: [py-svn] r56681 - in py/branch/event/py/test2: . testing Message-ID: <20080720114712.03B472A807C@codespeak.net> Author: hpk Date: Sun Jul 20 13:47:11 2008 New Revision: 56681 Modified: py/branch/event/py/test2/executor.py py/branch/event/py/test2/testing/test_executor.py Log: inline executor tests to make them more readable Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Sun Jul 20 13:47:11 2008 @@ -8,7 +8,8 @@ import py.__.test2.custompdb class RunExecutor(object): - """ Same as in executor, but just running run + """ + executes a test item and fills in a ItemTestReport. """ def __init__(self, item): self.item = item Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sun Jul 20 13:47:11 2008 @@ -1,55 +1,125 @@ import py from py.__.test2.executor import RunExecutor, ForkExecutor, ApigenExecutor -from py.__.test2.rsession.testing.basetest import BasicRsessionTest +def setup_module(mod): + mod.tmpdir = py.test.ensuretemp(mod.__name__) -class TestExecutor(BasicRsessionTest): +class TestExecutor: Executor = RunExecutor - def getexecutor(self, examplename): - funcitem = self.getfunc(examplename) - return self.Executor(funcitem) + def setup_method(self, method): + self.tmpdir = tmpdir.join("%s_%s" % + (self.__class__.__name__, method.__name__)) + + def makeitem(self, func, funcname="testfunc"): + funcname = getattr(func, '__name__', funcname) + self.tmpdir.ensure("__init__.py") + path = self.tmpdir.ensure(funcname + ".py") + path.write(py.code.Source(func)) + self.config = py.test2.config._reparse([path.dirpath()]) + modulecol = self.config._getcollector(path) + item = modulecol.join(funcname) + assert item is not None, (item, funcname) + return item + + def exectestfunc(self, func, funcname="testfunc"): + item = self.makeitem(func, funcname=funcname) + executor = self.getexec(item) + return executor.execute() - def exrun(self, examplename): - ex = self.getexecutor(examplename) - return ex.execute() + def getexec(self, item): + return self.Executor(item) - def test_run_executor(self): - testrep = self.exrun("passed") + def test_run_executor_pass(self): + testrep = self.exectestfunc(""" + def testfunc(): + pass + """) assert testrep.passed + + def test_run_executor_fail(self): + testrep = self.exectestfunc(""" + def testfunc(): + assert 0 + """) + assert testrep.failed - for name in 'failed', 'skipped': - testrep = self.exrun(name) - assert getattr(testrep, name) - assert not hasattr(testrep, 'signal') - #assert testrep._excinfo + def test_run_executor_skip(self): + testrep = self.exectestfunc(""" + import py + def testfunc(): + py.test2.skip("hello") + """) + assert testrep.skipped def test_run_executor_capture_stdout(self): - testrep = self.exrun("print") + testrep = self.exectestfunc(""" + def testfunc(): + print "samfing" + """) assert testrep.outerr[0][1] == "samfing\n" assert not testrep.outerr[1][1] def test_run_executor_capture_stderr(self): - testrep = self.exrun("printerr") - assert testrep.outerr[1][1] == "samfing\n" + testrep = self.exectestfunc(""" + import sys + def testfunc(): + print >>sys.stderr, "samfong" + """) + assert testrep.outerr[1][1] == "samfong\n" assert not testrep.outerr[0][1] - def test_box_executor_printfailing(self): - testrep = self.exrun("printfail") + def test_executor_print_with_assertion_failed(self): + testrep = self.exectestfunc(""" + import sys + def testfunc(): + print "samfing elz" + print >>sys.stderr, "sure" + assert 0 + """) assert not testrep.passed assert testrep.failed - assert testrep.outerr[0][1].find("samfing elz") != -1 - assert not testrep.outerr[1][1] + assert testrep.outerr[0][1] == "samfing elz\n" + assert testrep.outerr[1][1] == "sure\n" - def test_executor_explicit_Failed(self): - testrep = self.exrun("explicitfail") - assert not testrep.passed + def test_executor_print_with_explicit_fail(self): + testrep = self.exectestfunc(""" + import py, sys + def testfunc(): + print "samfing elz" + print >>sys.stderr, "sure" + py.test.fail() + """) + assert not testrep.passed assert testrep.failed + assert testrep.outerr[0][1] == "samfing elz\n" + assert testrep.outerr[1][1] == "sure\n" - def test_executor_raises_fails(self): - testrep = self.exrun("raisesfails") + def test_executor_print_raises_fail(self): + testrep = self.exectestfunc(""" + import py, sys + def testfunc(): + print "samfing elz" + print >>sys.stderr, "sure" + py.test2.raises(ValueError, "pass") + """) + assert not testrep.passed assert testrep.failed + assert testrep.outerr[0][1] == "samfing elz\n" + assert testrep.outerr[1][1] == "sure\n" + + def test_executor_setupfailure(self): + py.test.skip("xxx") + testrep = self.exectestfunc(""" + def setup_module(mod): + print "setupmodule" + raise ValueError(10) + def testfunc(): + pass + """) + assert not testrep.passed + assert testrep.setupfailure class TestForkExecutor(TestExecutor): def setup_class(cls): @@ -57,6 +127,17 @@ py.test.skip("need os.fork()") Executor = ForkExecutor + def test_suicide(self): + py.test.skip("XXX") + testrep = self.exectestfunc(""" + import os + def testfunc(): + os.kill(15, os.getpid()) + """) + assert testrep.signal == 15 + assert not testrep.passed + assert testrep.failed + class TestApigenExecutor(TestExecutor): Executor = ApigenExecutor @@ -75,6 +156,9 @@ funcitem = self.getfunc(examplename) return self.Executor(funcitem, tracer=Tracer()) + def getexec(self, item, Tracer=Tracer): + return self.Executor(item, tracer=Tracer()) + def test_apigen_executor_tracing_hook(self): tmpdir = py.test2.ensuretemp("apigen_executor") tmpdir.ensure("__init__.py") From hpk at codespeak.net Sun Jul 20 14:25:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 14:25:30 +0200 (CEST) Subject: [py-svn] r56682 - py/branch/event/py/test2/testing Message-ID: <20080720122530.4B0F82A807C@codespeak.net> Author: hpk Date: Sun Jul 20 14:25:27 2008 New Revision: 56682 Modified: py/branch/event/py/test2/testing/test_executor.py Log: a few non-implemented tests for setupfailures Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sun Jul 20 14:25:27 2008 @@ -43,6 +43,7 @@ def testfunc(): assert 0 """) + assert not testrep.passed assert testrep.failed def test_run_executor_skip(self): @@ -110,16 +111,49 @@ assert testrep.outerr[1][1] == "sure\n" def test_executor_setupfailure(self): - py.test.skip("xxx") + py.test.skip("implement setupfailures") testrep = self.exectestfunc(""" def setup_module(mod): - print "setupmodule" raise ValueError(10) def testfunc(): pass """) assert not testrep.passed assert testrep.setupfailure + assert testrep.failed # ??? + + # xxx repr_failure of setupfailures should always + # display python Tracebacks starting from the + # failing setup function + + def test_executor_setupfailure_on_eager_teardown(self): + py.test.skip("implement setupfailures") + testrep = self.exectestfunc(""" + def testfunc(): + pass + def teardown_function(func): + raise ValueError(11) + """) + assert not testrep.passed + assert testrep.setupfailure + assert testrep.failed # ??? + # xxx repr_failure of setupfailures should always + # display python Tracebacks starting from the + # failing teardown function + + def test_executor_setupfailure_on_deferred_teardown(self): + py.test.skip("implement setupfailures, extend help machinery") + testrep = self.exectestfunc(""" + class TestA: + def testfunc(self): + pass + def teardown_class(cls): + raise ValueError(12) + def setup_function(self, method): + raise ValueError(11) + def testfunc(): + pass + """, "TestA.testfunc", "testfunc") class TestForkExecutor(TestExecutor): def setup_class(cls): From hpk at codespeak.net Sun Jul 20 15:27:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 15:27:09 +0200 (CEST) Subject: [py-svn] r56683 - in py/branch/event/py/test2: . rep testing Message-ID: <20080720132709.39E85318005@codespeak.net> Author: hpk Date: Sun Jul 20 15:27:07 2008 New Revision: 56683 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/executor.py py/branch/event/py/test2/item.py py/branch/event/py/test2/present.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/test_executor.py py/branch/event/py/test2/testing/test_present.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_session.py py/branch/event/py/test2/testing/test_setup_nested.py Log: refactoring and improving stdout/stderr capturing. Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sun Jul 20 15:27:07 2008 @@ -149,18 +149,9 @@ """ return self._config.get_collector_trail(self) - def repr_failure(self, excinfo): - xxx # needs test and sensible output - p = present.FuncPresenter(self._config) - p.repr_failure_headline(self) - p.out.line("") - p.repr_tb(self, excinfo) - return p.stringio.getvalue() - def prunetraceback(self, traceback): return traceback - class Collector(Node): """ Collector instances generate children through Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Sun Jul 20 15:27:07 2008 @@ -1,10 +1,12 @@ -""" Remote executor +""" + test run functions """ import py, os, sys from py.__.test2 import repevent from py.__.test2.outcome import Skipped +from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb class RunExecutor(object): @@ -35,7 +37,27 @@ py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) else: excinfo = None - return repevent.ItemTestReport.fromitem(self.item, excinfo, self.outerr) + return self._makereport(excinfo) + + def _makereport(self, excinfo): + trail = self.item._get_collector_trail() + repr_run = None + if excinfo is not None: + if excinfo.errisinstance((Skipped, Skipped2)): + outcome = "skipped" + else: + outcome = "failed" + else: + outcome = "passed" + runinfo = RunInfo(excinfo, self.outerr) + repr_run = self.item.repr_run(runinfo) + repr_path = self.item.repr_path() + return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) + +class RunInfo: + def __init__(self, excinfo, outerr): + self.excinfo = excinfo + self.outerr = outerr class ForkExecutor(RunExecutor): """ Same as RunExecutor, but boxes test instead Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sun Jul 20 15:27:07 2008 @@ -39,7 +39,7 @@ """ xxx - def repr_failure(self, excinfo): + def repr_run(self, excinfo): """ return string failure represenation for this item. """ xxx @@ -71,11 +71,10 @@ """ execute the given test function. """ target(*args) - def repr_failure(self, excinfo): - """ return a textual failure representation for this item. """ - #xxx outerr + def repr_run(self, runinfo): + """ return a textual representation of run info. """ p = present.PythonFunctionPresenter(self._config, self) - p.repr_failure(excinfo) + p.repr_run(runinfo) return p.out.stringio.getvalue() def repr_path(self): Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Sun Jul 20 15:27:07 2008 @@ -32,8 +32,14 @@ self.out.line(line) - def repr_failure(self, excinfo): + def repr_run(self, runinfo): + excinfo = runinfo.excinfo self.header() - excinfo.traceback = self._item.prunetraceback(excinfo.traceback) - self.repr_tb(excinfo) - # xxx outerr self.footer() + if excinfo: + excinfo.traceback = self._item.prunetraceback(excinfo.traceback) + self.repr_tb(excinfo) + for name, value in runinfo.outerr: + if value: + self.out.sep("- ", "%s, len=%d" %(name, len(value))) + self.out.line(value.rstrip()) + self.repr_sep("_") Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sun Jul 20 15:27:07 2008 @@ -43,7 +43,7 @@ return for ev in self._failed: #self.out.sep("_", "entrypoint: %s" %(ev.repr_path[1],)) - self.out.line(ev.repr_failure) + self.out.line(ev.repr_run) def _getletter(self, ev): if ev.passed: Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sun Jul 20 15:27:07 2008 @@ -64,17 +64,17 @@ @classmethod def fromitem(cls, item, excinfo, outerr): trail = item._get_collector_trail() - repr_failure = None + repr_run = None if excinfo: if excinfo.errisinstance((Skipped, Skipped2)): outcome = "skipped" else: outcome = "failed" - repr_failure = item.repr_failure(excinfo) + repr_run = item.repr_run(excinfo) else: outcome = "passed" repr_path = item.repr_path() - return ItemTestReport(trail, outcome, repr_failure, repr_path, outerr) + return ItemTestReport(trail, outcome, repr_run, repr_path, outerr) @classmethod def fromdumps(cls, string): @@ -82,11 +82,11 @@ assert isinstance(d, dict) return cls(**d) - def __init__(self, trail, outcome, repr_failure, repr_path, outerr): + def __init__(self, trail, outcome, repr_run, repr_path, outerr=None): self.trail = trail assert outcome in ("passed", "failed", "skipped") self.outcome = outcome - self.repr_failure = repr_failure + self.repr_run = repr_run self.repr_path = repr_path self.outerr = outerr Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sun Jul 20 15:27:07 2008 @@ -59,8 +59,9 @@ def testfunc(): print "samfing" """) - assert testrep.outerr[0][1] == "samfing\n" - assert not testrep.outerr[1][1] + assert testrep.passed + assert testrep.repr_run.find("stderr") == -1 + assert testrep.repr_run.find("samfing\n") != -1 def test_run_executor_capture_stderr(self): testrep = self.exectestfunc(""" @@ -68,8 +69,10 @@ def testfunc(): print >>sys.stderr, "samfong" """) - assert testrep.outerr[1][1] == "samfong\n" - assert not testrep.outerr[0][1] + print testrep.repr_run + assert testrep.repr_run.find("stdout") == -1 + assert testrep.repr_run.find("stderr") != -1 + assert testrep.repr_run.find("samfong\n") != -1 def test_executor_print_with_assertion_failed(self): testrep = self.exectestfunc(""" @@ -81,8 +84,10 @@ """) assert not testrep.passed assert testrep.failed - assert testrep.outerr[0][1] == "samfing elz\n" - assert testrep.outerr[1][1] == "sure\n" + assert testrep.repr_run.find("stdout") != -1 + assert testrep.repr_run.find("stderr") != -1 + assert testrep.repr_run.find("samfing elz") != -1 + assert testrep.repr_run.find("sure") != -1 def test_executor_print_with_explicit_fail(self): testrep = self.exectestfunc(""" @@ -94,21 +99,10 @@ """) assert not testrep.passed assert testrep.failed - assert testrep.outerr[0][1] == "samfing elz\n" - assert testrep.outerr[1][1] == "sure\n" - - def test_executor_print_raises_fail(self): - testrep = self.exectestfunc(""" - import py, sys - def testfunc(): - print "samfing elz" - print >>sys.stderr, "sure" - py.test2.raises(ValueError, "pass") - """) - assert not testrep.passed - assert testrep.failed - assert testrep.outerr[0][1] == "samfing elz\n" - assert testrep.outerr[1][1] == "sure\n" + assert testrep.repr_run.find("stdout") != -1 + assert testrep.repr_run.find("stderr") != -1 + assert testrep.repr_run.find("samfing elz\n") != -1 + assert testrep.repr_run.find("sure\n") != -1 def test_executor_setupfailure(self): py.test.skip("implement setupfailures") @@ -122,7 +116,7 @@ assert testrep.setupfailure assert testrep.failed # ??? - # xxx repr_failure of setupfailures should always + # xxx repr_run of setupfailures should always # display python Tracebacks starting from the # failing setup function @@ -137,7 +131,7 @@ assert not testrep.passed assert testrep.setupfailure assert testrep.failed # ??? - # xxx repr_failure of setupfailures should always + # xxx repr_run of setupfailures should always # display python Tracebacks starting from the # failing teardown function Modified: py/branch/event/py/test2/testing/test_present.py ============================================================================== --- py/branch/event/py/test2/testing/test_present.py (original) +++ py/branch/event/py/test2/testing/test_present.py Sun Jul 20 15:27:07 2008 @@ -21,7 +21,7 @@ assert x yield check, 0 """) - s = itemtestreport.repr_failure + s = itemtestreport.repr_run print s lines = s.split("\n") assert lines[0].find("test_0.test_0.test_gen[0]") != -1 @@ -36,7 +36,7 @@ def test_f(): f(0) """, tb="short") - s = itemtestreport.repr_failure + s = itemtestreport.repr_run print s index = -1 basename = fn.basename Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Sun Jul 20 15:27:07 2008 @@ -10,7 +10,7 @@ ev1 = reports[0] ev2 = repevent.ItemTestReport.fromdumps(ev1.dumps()) assert ev1.repr_path == ev2.repr_path - assert ev1.repr_failure == ev2.repr_failure + assert ev1.repr_run == ev2.repr_run assert ev1.outcome == ev2.outcome def test_failing(self): @@ -18,7 +18,7 @@ reports = sorter.get(repevent.ItemTestReport) ev = reports[0] assert ev.failed - #assert ev.repr_failure.find("AssertionError") != -1 + #assert ev.repr_run.find("AssertionError") != -1 filepath, modpath = ev.repr_path assert filepath.endswith("filetest.py") assert modpath.endswith("filetest.test_one") Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Sun Jul 20 15:27:07 2008 @@ -107,11 +107,11 @@ ev_list = sorter.get(repevent.ItemTestReport) ev1, ev2 = ev_list - assert ev1.outerr[0][1] == "42\n" - assert ev1.outerr[1][1] == "23\n" + assert ev1.repr_run.find("42\n") != -1 + assert ev1.repr_run.find("23\n") != -1 - assert ev2.outerr[0][1] == "1\n" - assert ev2.outerr[1][1] == "2\n" + assert ev2.repr_run.find("1\n") != -1 + assert ev2.repr_run.find("2\n") != -1 def test_raises_output(self): sorter = suptest.events_from_runsource(''' @@ -121,7 +121,7 @@ ''') passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 - out = failed[0].repr_failure # exconly + out = failed[0].repr_run # exconly if not out.find("DID NOT RAISE") != -1: print out py.test2.fail("incorrect raises() output") @@ -171,9 +171,9 @@ sorter = suptest.events_run_example("brokenrepr.py") passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 2 - out = failed[0].repr_failure # exconly + out = failed[0].repr_run # exconly assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' - out = failed[1].repr_failure # exconly + out = failed[1].repr_run # exconly assert out.find("[unknown exception raised in repr()]") != -1 def test_collect_only_with_various_situations(self): @@ -245,38 +245,22 @@ assert failed[1].repr_path[1].endswith("test_other") assert failed[2].repr_path[1].endswith("test_two") - def test_capture_on_function(self): - sorter = suptest.events_from_runsource(""" - def test_one(): - print "passed" - def test_two(): - print "failed" - assert 0 - """) - testrep = sorter.getreport("test_one") - assert testrep.outerr[0][1].startswith("passed") - assert not testrep.outerr[1][1] - - testrep = sorter.getreport("test_two") - assert testrep.outerr[0][1].startswith("failed") - assert not testrep.outerr[1][1] - - def test_function_repr_failure(self): + def test_function_repr_run(self): o = setupdata.getexamplefile("filetest.py") conftest = o.dirpath('conftest.py') conftest.write(py.code.Source(""" import py l = [] class Function(py.test2.collect.Function): - def repr_failure(self, excinfo): + def repr_run(self, excinfo): return "custom_repr %s" % self.name """)) sorter = suptest.events_from_cmdline([o]) sorter.assertoutcome(failed=2) rep = sorter.getreport("test_one") - assert rep.repr_failure == "custom_repr test_one" + assert rep.repr_run == "custom_repr test_one" rep = sorter.getreport("test_method_one") - assert rep.repr_failure == "custom_repr test_method_one" + assert rep.repr_run == "custom_repr test_method_one" from py.__.test2.terminal.remote import RemoteTerminalSession Modified: py/branch/event/py/test2/testing/test_setup_nested.py ============================================================================== --- py/branch/event/py/test2/testing/test_setup_nested.py (original) +++ py/branch/event/py/test2/testing/test_setup_nested.py Sun Jul 20 15:27:07 2008 @@ -123,6 +123,6 @@ """) rep = sorter.getreport("test_one") assert rep.passed - assert rep.outerr[0][1] == "check\n" - assert rep.outerr[1][1] == "e\n" + assert rep.repr_run.find("check\n") != -1 + assert rep.repr_run.find("e\n") != -1 From hpk at codespeak.net Sun Jul 20 15:50:01 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 15:50:01 +0200 (CEST) Subject: [py-svn] r56684 - in py/branch/event: . py/test2 py/test2/testing Message-ID: <20080720135001.5A241698009@codespeak.net> Author: hpk Date: Sun Jul 20 15:50:00 2008 New Revision: 56684 Modified: py/branch/event/NOTES_FOR_MERGE py/branch/event/py/test2/executor.py py/branch/event/py/test2/testing/test_executor.py Log: don't care for apigen at the moment Modified: py/branch/event/NOTES_FOR_MERGE ============================================================================== --- py/branch/event/NOTES_FOR_MERGE (original) +++ py/branch/event/NOTES_FOR_MERGE Sun Jul 20 15:50:00 2008 @@ -1,3 +1,7 @@ remove misc/terminal_helper.py (now py.io.TerminalWriter) -review py.io.ForkedFunc and see if to provide a function that throws ExecutionFailed aka py.process.cmdexec merge py.process.cmdexec to py.io +apigen executor / execution? + +-- after merge? -- +review py.io.ForkedFunc and see if to provide a function that throws ExecutionFailed aka py.process.cmdexec + Modified: py/branch/event/py/test2/executor.py ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/executor.py Sun Jul 20 15:50:00 2008 @@ -84,30 +84,6 @@ testrep.signal = result.err return testrep -class ApigenExecutor(RunExecutor): - """ Same as RunExecutor, but takes tracer to trace calls as - an argument to execute - """ - def __init__(self, item, tracer): - super(ApigenExecutor, self).__init__(item) - self.tracer = tracer - - def runitem(self): - # only trace Python Function items - if hasattr(self.item, 'obj') and isinstance(self.item, py.test2.collect.Function): - orig_exec = self.item.execute - def traced_exec(target, *args): - self.tracer.start_tracing() - try: - orig_exec(target, *args) - finally: - self.tracer.end_tracing() - self.item.execute = traced_exec - try: - super(ApigenExecutor, self).runitem() - finally: - self.item.execute = orig_exec - def getexecutor(item): cls = RunExecutor if item._config.option.boxed: Modified: py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_executor.py Sun Jul 20 15:50:00 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.executor import RunExecutor, ForkExecutor, ApigenExecutor +from py.__.test2.executor import RunExecutor, ForkExecutor def setup_module(mod): mod.tmpdir = py.test.ensuretemp(mod.__name__) @@ -30,7 +30,7 @@ def getexec(self, item): return self.Executor(item) - + def test_run_executor_pass(self): testrep = self.exectestfunc(""" def testfunc(): @@ -166,62 +166,3 @@ assert not testrep.passed assert testrep.failed -class TestApigenExecutor(TestExecutor): - Executor = ApigenExecutor - - class Tracer(object): - def __init__(self): - self.starts = 0 - self.ends = 0 - - def start_tracing(self): - self.starts += 1 - - def end_tracing(self): - self.ends += 1 - - def getexecutor(self, examplename, Tracer=Tracer): - funcitem = self.getfunc(examplename) - return self.Executor(funcitem, tracer=Tracer()) - - def getexec(self, item, Tracer=Tracer): - return self.Executor(item, tracer=Tracer()) - - def test_apigen_executor_tracing_hook(self): - tmpdir = py.test2.ensuretemp("apigen_executor") - tmpdir.ensure("__init__.py") - tmpdir.ensure("test_one.py").write(py.code.Source(""" - def g(): - pass - - def test_1(): - g() - - class TestX(object): - def setup_method(self, m): - self.ttt = 1 - - def test_one(self): - self.ttt += 1 - - def test_raise(self): - 1/0 - """)) - config = py.test2.config._reparse([tmpdir]) - rootcol = config._getcollector(tmpdir) - tracer = self.Tracer() - item = rootcol._getitembynames("test_one.py/test_1") - ex = ApigenExecutor(item, tracer) - out1 = ex.execute() - item = rootcol._getitembynames("test_one.py/TestX/()/test_one") - ex = ApigenExecutor(item, tracer) - out2 = ex.execute() - item = rootcol._getitembynames("test_one.py/TestX/()/test_raise") - ex = ApigenExecutor(item, tracer) - out3 = ex.execute() - assert tracer.starts == 3 - assert tracer.ends == 3 - assert out1.passed - assert out2.passed - assert not out3.passed - From hpk at codespeak.net Sun Jul 20 16:18:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 16:18:20 +0200 (CEST) Subject: [py-svn] r56685 - in py/branch/event/py/test2: . rsession testing Message-ID: <20080720141820.45B89498090@codespeak.net> Author: hpk Date: Sun Jul 20 16:18:18 2008 New Revision: 56685 Added: py/branch/event/py/test2/runner.py - copied, changed from r56684, py/branch/event/py/test2/executor.py py/branch/event/py/test2/testing/test_runner.py - copied, changed from r56684, py/branch/event/py/test2/testing/test_executor.py Removed: py/branch/event/py/test2/executor.py py/branch/event/py/test2/testing/test_executor.py Modified: py/branch/event/py/test2/item.py py/branch/event/py/test2/rsession/slave.py py/branch/event/py/test2/session.py Log: executor classes -> simpler runner functions -> 30% less code in that module :) Deleted: /py/branch/event/py/test2/executor.py ============================================================================== --- /py/branch/event/py/test2/executor.py Sun Jul 20 16:18:18 2008 +++ (empty file) @@ -1,92 +0,0 @@ -""" - test run functions -""" - -import py, os, sys - -from py.__.test2 import repevent -from py.__.test2.outcome import Skipped -from py.__.test.outcome import Skipped as Skipped2 -import py.__.test2.custompdb - -class RunExecutor(object): - """ - executes a test item and fills in a ItemTestReport. - """ - def __init__(self, item): - self.item = item - self.config = item._config - self.outerr = None - - def runitem(self): - capture = self.config._getcapture(path=self.item.fspath) - try: - self.item.run() - finally: - out, err = capture.reset() - self.outerr = (("recorded stdout", out), ("recorded stderr", err)) - - def execute(self): - try: - self.runitem() - except py.builtin.sysex: - raise - except: - excinfo = py.code.ExceptionInfo() - if self.config.option.usepdb and not excinfo.errisinstance(Skipped): - py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) - else: - excinfo = None - return self._makereport(excinfo) - - def _makereport(self, excinfo): - trail = self.item._get_collector_trail() - repr_run = None - if excinfo is not None: - if excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - else: - outcome = "failed" - else: - outcome = "passed" - runinfo = RunInfo(excinfo, self.outerr) - repr_run = self.item.repr_run(runinfo) - repr_path = self.item.repr_path() - return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) - -class RunInfo: - def __init__(self, excinfo, outerr): - self.excinfo = excinfo - self.outerr = outerr - -class ForkExecutor(RunExecutor): - """ Same as RunExecutor, but boxes test instead - """ - def execute_forked(self): - testrep = RunExecutor.execute(self) - return testrep.dumps() - - def execute(self): - ff = py.io.ForkedFunc(self.execute_forked) - result = ff.waitfinish() - return self.maketestreport(result) - - def maketestreport(self, result): - if result.retval is not None: - testrep = repevent.ItemTestReport.fromdumps(result.retval) - testrep.stdout = result.out - testrep.stderr = result.err - else: - testrep = repevent.ItemTestReport.fromitem(self.item, None, None) - testrep.outcome = "failed" - testrep.stdout = result.out - testrep.stderr = result.err - testrep.signal = result.err - return testrep - -def getexecutor(item): - cls = RunExecutor - if item._config.option.boxed: - cls = ForkExecutor - return cls(item) - Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sun Jul 20 16:18:18 2008 @@ -1,5 +1,6 @@ import py from py.__.test2.collect import FunctionMixin, Node +from py.__.test2.runner import basic_runner, fork_runner from py.__.test2 import present _dummy = object() @@ -44,13 +45,10 @@ """ xxx - def runtest(self): - """ Return an ItemTestReport event which has all the - information about running the underlying test item. - """ - from py.__.test2.executor import getexecutor - executor = getexecutor(self) - return executor.execute() + def _getrunner(self): + if self._config.option.boxed: + return fork_runner + return basic_runner class Function(FunctionMixin, Item): """ a Function Item is responsible for setting up Modified: py/branch/event/py/test2/rsession/slave.py ============================================================================== --- py/branch/event/py/test2/rsession/slave.py (original) +++ py/branch/event/py/test2/rsession/slave.py Sun Jul 20 16:18:18 2008 @@ -13,7 +13,8 @@ send(None) break item = config._getcollector(itemspec) - testrep = item.runtest() + runner = item._getrunner() + testrep = runner(item) send(testrep.dumps()) def setup(): Copied: py/branch/event/py/test2/runner.py (from r56684, py/branch/event/py/test2/executor.py) ============================================================================== --- py/branch/event/py/test2/executor.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 16:18:18 2008 @@ -9,84 +9,62 @@ from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb -class RunExecutor(object): - """ - executes a test item and fills in a ItemTestReport. - """ - def __init__(self, item): - self.item = item - self.config = item._config - self.outerr = None +class RunInfo: + """ info on test runs. """ + def __init__(self, excinfo, outerr): + self.excinfo = excinfo + self.outerr = outerr - def runitem(self): - capture = self.config._getcapture(path=self.item.fspath) +def basic_runner(item): + """ returns a test report after having run the given test item. """ + config = item._config + capture = config._getcapture(path=item.fspath) + excinfo = None + try: try: - self.item.run() + item.run() finally: out, err = capture.reset() - self.outerr = (("recorded stdout", out), ("recorded stderr", err)) - - def execute(self): - try: - self.runitem() - except py.builtin.sysex: - raise - except: - excinfo = py.code.ExceptionInfo() - if self.config.option.usepdb and not excinfo.errisinstance(Skipped): - py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) + outerr = (("recorded stdout", out), ("recorded stderr", err)) + except py.builtin.sysex: + raise + except: + excinfo = py.code.ExceptionInfo() + if config.option.usepdb and not excinfo.errisinstance(Skipped): + py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) + + # make report + trail = item._get_collector_trail() + repr_run = None + if excinfo is not None: + if excinfo.errisinstance((Skipped, Skipped2)): + outcome = "skipped" else: - excinfo = None - return self._makereport(excinfo) + outcome = "failed" + else: + outcome = "passed" + runinfo = RunInfo(excinfo, outerr) + repr_run = item.repr_run(runinfo) + repr_path = item.repr_path() + return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) - def _makereport(self, excinfo): - trail = self.item._get_collector_trail() - repr_run = None - if excinfo is not None: - if excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - else: - outcome = "failed" - else: - outcome = "passed" - runinfo = RunInfo(excinfo, self.outerr) - repr_run = self.item.repr_run(runinfo) - repr_path = self.item.repr_path() - return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) -class RunInfo: - def __init__(self, excinfo, outerr): - self.excinfo = excinfo - self.outerr = outerr - -class ForkExecutor(RunExecutor): - """ Same as RunExecutor, but boxes test instead - """ - def execute_forked(self): - testrep = RunExecutor.execute(self) +def fork_runner(item): + def runforked(): + testrep = basic_runner(item) return testrep.dumps() - def execute(self): - ff = py.io.ForkedFunc(self.execute_forked) - result = ff.waitfinish() - return self.maketestreport(result) - - def maketestreport(self, result): - if result.retval is not None: - testrep = repevent.ItemTestReport.fromdumps(result.retval) - testrep.stdout = result.out - testrep.stderr = result.err - else: - testrep = repevent.ItemTestReport.fromitem(self.item, None, None) - testrep.outcome = "failed" - testrep.stdout = result.out - testrep.stderr = result.err - testrep.signal = result.err - return testrep - -def getexecutor(item): - cls = RunExecutor - if item._config.option.boxed: - cls = ForkExecutor - return cls(item) + ff = py.io.ForkedFunc(runforked) + result = ff.waitfinish() + if result.retval is not None: + testrep = repevent.ItemTestReport.fromdumps(result.retval) + testrep.stdout = result.out + testrep.stderr = result.err + else: + testrep = repevent.ItemTestReport.fromitem(self.item, None, None) + testrep.outcome = "failed" + testrep.stdout = result.out + testrep.stderr = result.err + testrep.signal = result.err + return testrep Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sun Jul 20 16:18:18 2008 @@ -84,5 +84,6 @@ return failures def runtest(self, item): - return item.runtest() + runner = item._getrunner() + return runner(item) Deleted: /py/branch/event/py/test2/testing/test_executor.py ============================================================================== --- /py/branch/event/py/test2/testing/test_executor.py Sun Jul 20 16:18:18 2008 +++ (empty file) @@ -1,168 +0,0 @@ - -import py -from py.__.test2.executor import RunExecutor, ForkExecutor - -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - -class TestExecutor: - Executor = RunExecutor - - def setup_method(self, method): - self.tmpdir = tmpdir.join("%s_%s" % - (self.__class__.__name__, method.__name__)) - - def makeitem(self, func, funcname="testfunc"): - funcname = getattr(func, '__name__', funcname) - self.tmpdir.ensure("__init__.py") - path = self.tmpdir.ensure(funcname + ".py") - path.write(py.code.Source(func)) - self.config = py.test2.config._reparse([path.dirpath()]) - modulecol = self.config._getcollector(path) - item = modulecol.join(funcname) - assert item is not None, (item, funcname) - return item - - def exectestfunc(self, func, funcname="testfunc"): - item = self.makeitem(func, funcname=funcname) - executor = self.getexec(item) - return executor.execute() - - def getexec(self, item): - return self.Executor(item) - - def test_run_executor_pass(self): - testrep = self.exectestfunc(""" - def testfunc(): - pass - """) - assert testrep.passed - - def test_run_executor_fail(self): - testrep = self.exectestfunc(""" - def testfunc(): - assert 0 - """) - assert not testrep.passed - assert testrep.failed - - def test_run_executor_skip(self): - testrep = self.exectestfunc(""" - import py - def testfunc(): - py.test2.skip("hello") - """) - assert testrep.skipped - - def test_run_executor_capture_stdout(self): - testrep = self.exectestfunc(""" - def testfunc(): - print "samfing" - """) - assert testrep.passed - assert testrep.repr_run.find("stderr") == -1 - assert testrep.repr_run.find("samfing\n") != -1 - - def test_run_executor_capture_stderr(self): - testrep = self.exectestfunc(""" - import sys - def testfunc(): - print >>sys.stderr, "samfong" - """) - print testrep.repr_run - assert testrep.repr_run.find("stdout") == -1 - assert testrep.repr_run.find("stderr") != -1 - assert testrep.repr_run.find("samfong\n") != -1 - - def test_executor_print_with_assertion_failed(self): - testrep = self.exectestfunc(""" - import sys - def testfunc(): - print "samfing elz" - print >>sys.stderr, "sure" - assert 0 - """) - assert not testrep.passed - assert testrep.failed - assert testrep.repr_run.find("stdout") != -1 - assert testrep.repr_run.find("stderr") != -1 - assert testrep.repr_run.find("samfing elz") != -1 - assert testrep.repr_run.find("sure") != -1 - - def test_executor_print_with_explicit_fail(self): - testrep = self.exectestfunc(""" - import py, sys - def testfunc(): - print "samfing elz" - print >>sys.stderr, "sure" - py.test.fail() - """) - assert not testrep.passed - assert testrep.failed - assert testrep.repr_run.find("stdout") != -1 - assert testrep.repr_run.find("stderr") != -1 - assert testrep.repr_run.find("samfing elz\n") != -1 - assert testrep.repr_run.find("sure\n") != -1 - - def test_executor_setupfailure(self): - py.test.skip("implement setupfailures") - testrep = self.exectestfunc(""" - def setup_module(mod): - raise ValueError(10) - def testfunc(): - pass - """) - assert not testrep.passed - assert testrep.setupfailure - assert testrep.failed # ??? - - # xxx repr_run of setupfailures should always - # display python Tracebacks starting from the - # failing setup function - - def test_executor_setupfailure_on_eager_teardown(self): - py.test.skip("implement setupfailures") - testrep = self.exectestfunc(""" - def testfunc(): - pass - def teardown_function(func): - raise ValueError(11) - """) - assert not testrep.passed - assert testrep.setupfailure - assert testrep.failed # ??? - # xxx repr_run of setupfailures should always - # display python Tracebacks starting from the - # failing teardown function - - def test_executor_setupfailure_on_deferred_teardown(self): - py.test.skip("implement setupfailures, extend help machinery") - testrep = self.exectestfunc(""" - class TestA: - def testfunc(self): - pass - def teardown_class(cls): - raise ValueError(12) - def setup_function(self, method): - raise ValueError(11) - def testfunc(): - pass - """, "TestA.testfunc", "testfunc") - -class TestForkExecutor(TestExecutor): - def setup_class(cls): - if not hasattr(py.std.os, 'fork'): - py.test.skip("need os.fork()") - Executor = ForkExecutor - - def test_suicide(self): - py.test.skip("XXX") - testrep = self.exectestfunc(""" - import os - def testfunc(): - os.kill(15, os.getpid()) - """) - assert testrep.signal == 15 - assert not testrep.passed - assert testrep.failed - Copied: py/branch/event/py/test2/testing/test_runner.py (from r56684, py/branch/event/py/test2/testing/test_executor.py) ============================================================================== --- py/branch/event/py/test2/testing/test_executor.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 16:18:18 2008 @@ -1,12 +1,13 @@ import py -from py.__.test2.executor import RunExecutor, ForkExecutor +from py.__.test2.runner import basic_runner, fork_runner def setup_module(mod): mod.tmpdir = py.test.ensuretemp(mod.__name__) -class TestExecutor: - Executor = RunExecutor +class TestRunner: + def getrunner(self): + return basic_runner def setup_method(self, method): self.tmpdir = tmpdir.join("%s_%s" % @@ -23,39 +24,37 @@ assert item is not None, (item, funcname) return item - def exectestfunc(self, func, funcname="testfunc"): + def runtestfunc(self, func, funcname="testfunc"): item = self.makeitem(func, funcname=funcname) - executor = self.getexec(item) - return executor.execute() + runner = self.getrunner() + return runner(item) - def getexec(self, item): - return self.Executor(item) - def test_run_executor_pass(self): - testrep = self.exectestfunc(""" + def test_run_pass(self): + testrep = self.runtestfunc(""" def testfunc(): pass """) assert testrep.passed - def test_run_executor_fail(self): - testrep = self.exectestfunc(""" + def test_run_fail(self): + testrep = self.runtestfunc(""" def testfunc(): assert 0 """) assert not testrep.passed assert testrep.failed - def test_run_executor_skip(self): - testrep = self.exectestfunc(""" + def test_run_skip(self): + testrep = self.runtestfunc(""" import py def testfunc(): py.test2.skip("hello") """) assert testrep.skipped - def test_run_executor_capture_stdout(self): - testrep = self.exectestfunc(""" + def test_run_capture_stdout(self): + testrep = self.runtestfunc(""" def testfunc(): print "samfing" """) @@ -63,8 +62,8 @@ assert testrep.repr_run.find("stderr") == -1 assert testrep.repr_run.find("samfing\n") != -1 - def test_run_executor_capture_stderr(self): - testrep = self.exectestfunc(""" + def test_run_capture_stderr(self): + testrep = self.runtestfunc(""" import sys def testfunc(): print >>sys.stderr, "samfong" @@ -74,8 +73,8 @@ assert testrep.repr_run.find("stderr") != -1 assert testrep.repr_run.find("samfong\n") != -1 - def test_executor_print_with_assertion_failed(self): - testrep = self.exectestfunc(""" + def test_print_with_assertion_failed(self): + testrep = self.runtestfunc(""" import sys def testfunc(): print "samfing elz" @@ -89,8 +88,8 @@ assert testrep.repr_run.find("samfing elz") != -1 assert testrep.repr_run.find("sure") != -1 - def test_executor_print_with_explicit_fail(self): - testrep = self.exectestfunc(""" + def test_print_with_explicit_fail(self): + testrep = self.runtestfunc(""" import py, sys def testfunc(): print "samfing elz" @@ -104,9 +103,9 @@ assert testrep.repr_run.find("samfing elz\n") != -1 assert testrep.repr_run.find("sure\n") != -1 - def test_executor_setupfailure(self): + def test_setupfailure(self): py.test.skip("implement setupfailures") - testrep = self.exectestfunc(""" + testrep = self.runtestfunc(""" def setup_module(mod): raise ValueError(10) def testfunc(): @@ -120,9 +119,9 @@ # display python Tracebacks starting from the # failing setup function - def test_executor_setupfailure_on_eager_teardown(self): + def test_setupfailure_on_eager_teardown(self): py.test.skip("implement setupfailures") - testrep = self.exectestfunc(""" + testrep = self.runtestfunc(""" def testfunc(): pass def teardown_function(func): @@ -135,9 +134,9 @@ # display python Tracebacks starting from the # failing teardown function - def test_executor_setupfailure_on_deferred_teardown(self): + def test_setupfailure_on_deferred_teardown(self): py.test.skip("implement setupfailures, extend help machinery") - testrep = self.exectestfunc(""" + testrep = self.runtestfunc(""" class TestA: def testfunc(self): pass @@ -149,15 +148,16 @@ pass """, "TestA.testfunc", "testfunc") -class TestForkExecutor(TestExecutor): +class TestForkRunner(TestRunner): def setup_class(cls): if not hasattr(py.std.os, 'fork'): py.test.skip("need os.fork()") - Executor = ForkExecutor + def getrunner(self): + return fork_runner def test_suicide(self): py.test.skip("XXX") - testrep = self.exectestfunc(""" + testrep = self.runtestfunc(""" import os def testfunc(): os.kill(15, os.getpid()) From hpk at codespeak.net Sun Jul 20 16:54:16 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 16:54:16 +0200 (CEST) Subject: [py-svn] r56686 - in py/branch/event/py/test2: . testing Message-ID: <20080720145416.164924980CA@codespeak.net> Author: hpk Date: Sun Jul 20 16:54:15 2008 New Revision: 56686 Modified: py/branch/event/py/test2/item.py py/branch/event/py/test2/present.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: internal rename Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sun Jul 20 16:54:15 2008 @@ -60,19 +60,14 @@ if callobj is not _dummy: self._obj = callobj - def run(self): - """ setup and execute the underlying test function. """ - self._config._setupstate.prepare(self) - self.execute(self.obj, *self._args) - - def execute(self, target, *args): + def execute(self): """ execute the given test function. """ - target(*args) + self.obj(*self._args) - def repr_run(self, runinfo): + def repr_run(self, runnerinfo): """ return a textual representation of run info. """ p = present.PythonFunctionPresenter(self._config, self) - p.repr_run(runinfo) + p.repr_run(runnerinfo) return p.out.stringio.getvalue() def repr_path(self): Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Sun Jul 20 16:54:15 2008 @@ -1,12 +1,9 @@ - -""" XXX this file serves as a reminder about some remaining - reporting issues that are not handled by the new py/code/tbpresenter +""" + Some specifics about reporting Python tracebacks in py.test """ import py - from py.__.code.tbpresent import TBPresenter - from py.__.test2.rep.reporter import getmodpath class PythonFunctionPresenter(TBPresenter): @@ -32,13 +29,13 @@ self.out.line(line) - def repr_run(self, runinfo): - excinfo = runinfo.excinfo + def repr_run(self, runnerinfo): + excinfo = runnerinfo.excinfo self.header() if excinfo: excinfo.traceback = self._item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) - for name, value in runinfo.outerr: + for name, value in runnerinfo.outerr: if value: self.out.sep("- ", "%s, len=%d" %(name, len(value))) self.out.line(value.rstrip()) Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 16:54:15 2008 @@ -9,7 +9,7 @@ from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb -class RunInfo: +class RunnerInfo: """ info on test runs. """ def __init__(self, excinfo, outerr): self.excinfo = excinfo @@ -22,7 +22,8 @@ excinfo = None try: try: - item.run() + config._setupstate.prepare(item) + item.execute() finally: out, err = capture.reset() outerr = (("recorded stdout", out), ("recorded stderr", err)) @@ -43,8 +44,8 @@ outcome = "failed" else: outcome = "passed" - runinfo = RunInfo(excinfo, outerr) - repr_run = item.repr_run(runinfo) + runnerinfo = RunnerInfo(excinfo, outerr) + repr_run = item.repr_run(runnerinfo) repr_path = item.repr_path() return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 16:54:15 2008 @@ -114,7 +114,7 @@ assert not testrep.passed assert testrep.setupfailure assert testrep.failed # ??? - + # xxx repr_run of setupfailures should always # display python Tracebacks starting from the # failing setup function From hpk at codespeak.net Sun Jul 20 17:01:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 17:01:17 +0200 (CEST) Subject: [py-svn] r56687 - py/branch/event/py/test2 Message-ID: <20080720150117.1CF984980D5@codespeak.net> Author: hpk Date: Sun Jul 20 17:01:13 2008 New Revision: 56687 Modified: py/branch/event/py/test2/runner.py Log: mini-refactor before trying tackling setupfailure tests Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 17:01:13 2008 @@ -14,6 +14,14 @@ def __init__(self, excinfo, outerr): self.excinfo = excinfo self.outerr = outerr + if excinfo is not None: + if excinfo.errisinstance((Skipped, Skipped2)): + outcome = "skipped" + else: + outcome = "failed" + else: + outcome = "passed" + self.outcome = outcome def basic_runner(item): """ returns a test report after having run the given test item. """ @@ -36,18 +44,11 @@ # make report trail = item._get_collector_trail() - repr_run = None - if excinfo is not None: - if excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - else: - outcome = "failed" - else: - outcome = "passed" runnerinfo = RunnerInfo(excinfo, outerr) repr_run = item.repr_run(runnerinfo) repr_path = item.repr_path() - return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) + return repevent.ItemTestReport(trail, runnerinfo.outcome, + repr_run, repr_path) def fork_runner(item): From hpk at codespeak.net Sun Jul 20 17:49:15 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 17:49:15 +0200 (CEST) Subject: [py-svn] r56688 - in py/branch/event/py/test2: . testing Message-ID: <20080720154915.4DCED698005@codespeak.net> Author: hpk Date: Sun Jul 20 17:49:13 2008 New Revision: 56688 Modified: py/branch/event/py/test2/item.py py/branch/event/py/test2/present.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: implement setupfailure recognition and specialized reporting Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sun Jul 20 17:49:13 2008 @@ -17,8 +17,8 @@ def prepare(self, colitem): """ setup objects along the collector chain to the test-method - Teardown any unneccessary previously setup objects. - """ + Teardown any unneccessary previously setup objects.""" + needed_collectors = colitem.listchain() while self.stack: if self.stack == needed_collectors[:len(self.stack)]: @@ -66,9 +66,7 @@ def repr_run(self, runnerinfo): """ return a textual representation of run info. """ - p = present.PythonFunctionPresenter(self._config, self) - p.repr_run(runnerinfo) - return p.out.stringio.getvalue() + return present.python_repr_run(self, runnerinfo) def repr_path(self): from py.__.test2.rep.reporter import getrelpath, getmodpath Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Sun Jul 20 17:49:13 2008 @@ -16,10 +16,12 @@ style=config.option.tbstyle, ) - def header(self): + def header(self, title=None): item = self._item - modpath = getmodpath(item) - self.out.sep("_", "entrypoint: %s" %(modpath)) + if title is None: + modpath = getmodpath(item) + title = "entrypoint: %s" % modpath + self.out.sep("_", title) self.out.line("") if item.name[-1] == "]": # print extra info for generated tests if not self._config.option.fulltrace: @@ -29,9 +31,9 @@ self.out.line(line) - def repr_run(self, runnerinfo): + def repr_run(self, runnerinfo, title=None): excinfo = runnerinfo.excinfo - self.header() + self.header(title) if excinfo: excinfo.traceback = self._item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) @@ -40,3 +42,8 @@ self.out.sep("- ", "%s, len=%d" %(name, len(value))) self.out.line(value.rstrip()) self.repr_sep("_") + +def python_repr_run(item, runnerinfo, title=None): + p = PythonFunctionPresenter(item._config, item) + p.repr_run(runnerinfo, title=title) + return p.out.stringio.getvalue() Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sun Jul 20 17:49:13 2008 @@ -58,7 +58,8 @@ class ItemTestReport(BaseEvent): passed = property(lambda self: self.outcome == "passed") - failed = property(lambda self: self.outcome == "failed") + failed = property(lambda self: self.outcome == "failed" or self.outcome == "setupfailed") + setupfailed = property(lambda self: self.outcome == "setupfailed") skipped = property(lambda self: self.outcome == "skipped") @classmethod @@ -84,7 +85,7 @@ def __init__(self, trail, outcome, repr_run, repr_path, outerr=None): self.trail = trail - assert outcome in ("passed", "failed", "skipped") + assert outcome in ("passed", "failed", "skipped", "setupfailed") self.outcome = outcome self.repr_run = repr_run self.repr_path = repr_path Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 17:49:13 2008 @@ -8,20 +8,13 @@ from py.__.test2.outcome import Skipped from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb +from py.__.test2 import present class RunnerInfo: """ info on test runs. """ def __init__(self, excinfo, outerr): self.excinfo = excinfo self.outerr = outerr - if excinfo is not None: - if excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - else: - outcome = "failed" - else: - outcome = "passed" - self.outcome = outcome def basic_runner(item): """ returns a test report after having run the given test item. """ @@ -29,8 +22,10 @@ capture = config._getcapture(path=item.fspath) excinfo = None try: + outcome = "setupfailed" try: config._setupstate.prepare(item) + outcome = "passed" item.execute() finally: out, err = capture.reset() @@ -39,17 +34,28 @@ raise except: excinfo = py.code.ExceptionInfo() - if config.option.usepdb and not excinfo.errisinstance(Skipped): - py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) # make report trail = item._get_collector_trail() + repr_run = None + if excinfo is not None and outcome == "passed": + if excinfo.errisinstance((Skipped, Skipped2)): + outcome = "skipped" + else: + outcome = "failed" runnerinfo = RunnerInfo(excinfo, outerr) - repr_run = item.repr_run(runnerinfo) repr_path = item.repr_path() - return repevent.ItemTestReport(trail, runnerinfo.outcome, - repr_run, repr_path) - + if outcome != "setupfailed": + # dispatch to custom item function + repr_run = item.repr_run(runnerinfo) + else: + repr_run = present.python_repr_run(item, runnerinfo, + title="setupfailure with: %s" % (repr_path[1],)) + rep = repevent.ItemTestReport(trail, outcome, repr_run, repr_path) + if config.option.usepdb and rep.failed: + # xxx print rep.repr_run() ? + py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) + return rep def fork_runner(item): def runforked(): Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 17:49:13 2008 @@ -104,7 +104,6 @@ assert testrep.repr_run.find("sure\n") != -1 def test_setupfailure(self): - py.test.skip("implement setupfailures") testrep = self.runtestfunc(""" def setup_module(mod): raise ValueError(10) @@ -112,15 +111,39 @@ pass """) assert not testrep.passed - assert testrep.setupfailure - assert testrep.failed # ??? - - # xxx repr_run of setupfailures should always + assert testrep.setupfailed + assert testrep.failed + + s = testrep.repr_run + print s + exp = "setupfailure with: %s" %(testrep.repr_path[1]) + assert s.find(exp) != -1 + i = s.find("def ") + assert s[i+4:].startswith("setup_module") + + def test_setupfailure_always_python_traceback(self): + item = self.makeitem(""" + def setup_module(mod): + raise ValueError(10) + def testfunc(): + pass + """) + runner = self.getrunner() + item.repr_run = lambda *args: "cusWRONG TRACEBACK!" + testrep = runner(item) + assert not testrep.passed + assert testrep.setupfailed + assert testrep.failed + # repr_run of setupfailures should always # display python Tracebacks starting from the # failing setup function + s = testrep.repr_run + print s + i = s.find("def ") + assert s[i+4:].startswith("setup_module") def test_setupfailure_on_eager_teardown(self): - py.test.skip("implement setupfailures") + py.test.skip("implement eager teardown") testrep = self.runtestfunc(""" def testfunc(): pass From hpk at codespeak.net Sun Jul 20 17:56:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 17:56:29 +0200 (CEST) Subject: [py-svn] r56689 - in py/branch/event/py/test2: . rep testing Message-ID: <20080720155629.AA7E8698005@codespeak.net> Author: hpk Date: Sun Jul 20 17:56:28 2008 New Revision: 56689 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: fix skipping at setup time + test Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sun Jul 20 17:56:28 2008 @@ -50,5 +50,7 @@ return "." elif ev.skipped: return "s" + elif ev.setupfailed: + return "f" elif ev.failed: return "F" Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 17:56:28 2008 @@ -38,10 +38,10 @@ # make report trail = item._get_collector_trail() repr_run = None - if excinfo is not None and outcome == "passed": + if excinfo is not None: if excinfo.errisinstance((Skipped, Skipped2)): outcome = "skipped" - else: + elif outcome == "passed": outcome = "failed" runnerinfo = RunnerInfo(excinfo, outerr) repr_path = item.repr_path() Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 17:56:28 2008 @@ -52,6 +52,9 @@ py.test2.skip("hello") """) assert testrep.skipped + assert not testrep.passed + assert not testrep.failed + assert not testrep.setupfailed def test_run_capture_stdout(self): testrep = self.runtestfunc(""" @@ -121,6 +124,19 @@ i = s.find("def ") assert s[i+4:].startswith("setup_module") + def test_setup_skip(self): + testrep = self.runtestfunc(""" + import py + def setup_module(mod): + py.test.skip("setupmoduleskip") + def testfunc(): + pass + """) + assert testrep.skipped + assert not testrep.passed + assert not testrep.setupfailed + assert not testrep.failed + def test_setupfailure_always_python_traceback(self): item = self.makeitem(""" def setup_module(mod): @@ -157,20 +173,6 @@ # display python Tracebacks starting from the # failing teardown function - def test_setupfailure_on_deferred_teardown(self): - py.test.skip("implement setupfailures, extend help machinery") - testrep = self.runtestfunc(""" - class TestA: - def testfunc(self): - pass - def teardown_class(cls): - raise ValueError(12) - def setup_function(self, method): - raise ValueError(11) - def testfunc(): - pass - """, "TestA.testfunc", "testfunc") - class TestForkRunner(TestRunner): def setup_class(cls): if not hasattr(py.std.os, 'fork'): From hpk at codespeak.net Sun Jul 20 18:09:15 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 18:09:15 +0200 (CEST) Subject: [py-svn] r56690 - py/branch/event/py/test2 Message-ID: <20080720160915.D1751498019@codespeak.net> Author: hpk Date: Sun Jul 20 18:09:14 2008 New Revision: 56690 Modified: py/branch/event/py/test2/present.py py/branch/event/py/test2/runner.py Log: cleanup/small review of basic_runner. Modified: py/branch/event/py/test2/present.py ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/present.py Sun Jul 20 18:09:14 2008 @@ -37,7 +37,7 @@ if excinfo: excinfo.traceback = self._item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) - for name, value in runnerinfo.outerr: + for name, value in zip(["recorded stdout", "recorded stderr"], runnerinfo.outerr): if value: self.out.sep("- ", "%s, len=%d" %(name, len(value))) self.out.line(value.rstrip()) Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 18:09:14 2008 @@ -22,35 +22,33 @@ capture = config._getcapture(path=item.fspath) excinfo = None try: - outcome = "setupfailed" try: + outcome = "setupfailed" config._setupstate.prepare(item) - outcome = "passed" + outcome = "failed" item.execute() + outcome = "passed" finally: - out, err = capture.reset() - outerr = (("recorded stdout", out), ("recorded stderr", err)) + outerr = capture.reset() except py.builtin.sysex: raise except: excinfo = py.code.ExceptionInfo() + # # make report + # trail = item._get_collector_trail() - repr_run = None - if excinfo is not None: - if excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - elif outcome == "passed": - outcome = "failed" runnerinfo = RunnerInfo(excinfo, outerr) repr_path = item.repr_path() + if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): + outcome = "skipped" if outcome != "setupfailed": - # dispatch to custom item function repr_run = item.repr_run(runnerinfo) else: repr_run = present.python_repr_run(item, runnerinfo, title="setupfailure with: %s" % (repr_path[1],)) + rep = repevent.ItemTestReport(trail, outcome, repr_run, repr_path) if config.option.usepdb and rep.failed: # xxx print rep.repr_run() ? From hpk at codespeak.net Sun Jul 20 18:34:48 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 18:34:48 +0200 (CEST) Subject: [py-svn] r56691 - py/branch/event/py/test2 Message-ID: <20080720163448.E0A4F318005@codespeak.net> Author: hpk Date: Sun Jul 20 18:34:47 2008 New Revision: 56691 Modified: py/branch/event/py/test2/collect.py Log: better naming for when this appears in tracebacks Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sun Jul 20 18:34:47 2008 @@ -416,9 +416,9 @@ else: name = 'setup_function' obj = self.parent.obj - meth = getattr(obj, name, None) - if meth is not None: - return meth(self.obj) + setup_func_or_method = getattr(obj, name, None) + if setup_func_or_method is not None: + return setup_func_or_method(self.obj) def teardown(self): """ perform teardown for this test function. """ @@ -427,9 +427,9 @@ else: name = 'teardown_function' obj = self.parent.obj - meth = getattr(obj, name, None) - if meth is not None: - return meth(self.obj) + teardown_func_or_meth = getattr(obj, name, None) + if teardown_func_or_meth is not None: + return teardown_func_or_meth(self.obj) def prunetraceback(self, traceback): if not self._config.option.fulltrace: From hpk at codespeak.net Sun Jul 20 18:35:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 18:35:03 +0200 (CEST) Subject: [py-svn] r56692 - in py/branch/event/py/test2: . testing Message-ID: <20080720163503.28E6D1684F4@codespeak.net> Author: hpk Date: Sun Jul 20 18:35:02 2008 New Revision: 56692 Modified: py/branch/event/py/test2/item.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: do some eager teardown Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sun Jul 20 18:35:02 2008 @@ -14,6 +14,11 @@ while self.stack: col = self.stack.pop() col.teardown() + + def teardown_exact(self, item): + if self.stack[-1] == item: + col = self.stack.pop() + col.teardown() def prepare(self, colitem): """ setup objects along the collector chain to the test-method Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 18:35:02 2008 @@ -27,6 +27,8 @@ config._setupstate.prepare(item) outcome = "failed" item.execute() + outcome = "setupfailed" + config._setupstate.teardown_exact(item) outcome = "passed" finally: outerr = capture.reset() Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 18:35:02 2008 @@ -159,16 +159,19 @@ assert s[i+4:].startswith("setup_module") def test_setupfailure_on_eager_teardown(self): - py.test.skip("implement eager teardown") testrep = self.runtestfunc(""" def testfunc(): pass def teardown_function(func): raise ValueError(11) """) + assert testrep.setupfailed assert not testrep.passed - assert testrep.setupfailure - assert testrep.failed # ??? + assert testrep.failed + s = testrep.repr_run + print s + i = s.find("def ") + assert s[i+4:].startswith("teardown_function") # xxx repr_run of setupfailures should always # display python Tracebacks starting from the # failing teardown function From hpk at codespeak.net Sun Jul 20 19:14:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 19:14:03 +0200 (CEST) Subject: [py-svn] r56694 - in py/branch/event/py/test2: . testing Message-ID: <20080720171403.93FE2169E92@codespeak.net> Author: hpk Date: Sun Jul 20 19:14:02 2008 New Revision: 56694 Modified: py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: * be less specific because setup/teardown might be misleading in more * refactor test setup a bit Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 19:14:02 2008 @@ -49,7 +49,7 @@ repr_run = item.repr_run(runnerinfo) else: repr_run = present.python_repr_run(item, runnerinfo, - title="setupfailure with: %s" % (repr_path[1],)) + title="failure during setup/teardown") rep = repevent.ItemTestReport(trail, outcome, repr_run, repr_path) if config.option.usepdb and rep.failed: Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 19:14:02 2008 @@ -13,23 +13,30 @@ self.tmpdir = tmpdir.join("%s_%s" % (self.__class__.__name__, method.__name__)) - def makeitem(self, func, funcname="testfunc"): + def getmodulecol(self, func, funcname="testfunc"): funcname = getattr(func, '__name__', funcname) self.tmpdir.ensure("__init__.py") path = self.tmpdir.ensure(funcname + ".py") path.write(py.code.Source(func)) self.config = py.test2.config._reparse([path.dirpath()]) modulecol = self.config._getcollector(path) + return modulecol + + def makeitem(self, source, funcname="testfunc"): + modulecol = self.getmodulecol(source) item = modulecol.join(funcname) assert item is not None, (item, funcname) return item + def getitems(self, source): + modulecol = self.getmodulecol(source) + return [modulecol.join(x) for x in modulecol.listdir()] + def runtestfunc(self, func, funcname="testfunc"): item = self.makeitem(func, funcname=funcname) runner = self.getrunner() return runner(item) - def test_run_pass(self): testrep = self.runtestfunc(""" def testfunc(): @@ -119,7 +126,7 @@ s = testrep.repr_run print s - exp = "setupfailure with: %s" %(testrep.repr_path[1]) + exp = "failure during setup/teardown" assert s.find(exp) != -1 i = s.find("def ") assert s[i+4:].startswith("setup_module") @@ -172,9 +179,6 @@ print s i = s.find("def ") assert s[i+4:].startswith("teardown_function") - # xxx repr_run of setupfailures should always - # display python Tracebacks starting from the - # failing teardown function class TestForkRunner(TestRunner): def setup_class(cls): From hpk at codespeak.net Sun Jul 20 19:37:51 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 20 Jul 2008 19:37:51 +0200 (CEST) Subject: [py-svn] r56695 - in py/branch/event/py/test2: . testing Message-ID: <20080720173751.0EE712A80C6@codespeak.net> Author: hpk Date: Sun Jul 20 19:37:49 2008 New Revision: 56695 Modified: py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: have a nice CRASH report in case a test process gets SIGKILLED Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Jul 20 19:37:49 2008 @@ -67,12 +67,23 @@ if result.retval is not None: testrep = repevent.ItemTestReport.fromdumps(result.retval) - testrep.stdout = result.out - testrep.stderr = result.err + #testrep.stdout = result.out + #testrep.stderr = result.err else: - testrep = repevent.ItemTestReport.fromitem(self.item, None, None) - testrep.outcome = "failed" - testrep.stdout = result.out - testrep.stderr = result.err - testrep.signal = result.err + # xxx look into reusing present/tbpresent-writer + tw = py.io.TerminalWriter() + tw.sep("_", "CRASHED with signal=%d: %s" % + (result.signal, present.getmodpath(item))) + code = py.code.Code(item.obj) + path, firstlineno = code.path, code.firstlineno + src = py.code.Source(item.obj) + tw.line() + tw.line(str(src.indent())) + tw.line("[%s:%d]" %(path, firstlineno)) + repr_run = tw.stringio.getvalue() + repr_path = item.repr_path() + trail = item._get_collector_trail() + testrep = repevent.ItemTestReport(trail, "failed", repr_run, repr_path) + #testrep.stdout = result.out + #testrep.stderr = result.err return testrep Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Sun Jul 20 19:37:49 2008 @@ -188,13 +188,16 @@ return fork_runner def test_suicide(self): - py.test.skip("XXX") testrep = self.runtestfunc(""" import os def testfunc(): - os.kill(15, os.getpid()) + os.kill(os.getpid(), 15) + # """) - assert testrep.signal == 15 assert not testrep.passed assert testrep.failed + assert testrep.repr_run.find("CRASHED") != -1 + assert testrep.repr_run.find("signal=15") != -1 + print testrep.repr_run + # assert 0 From hpk at codespeak.net Tue Jul 22 16:27:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 16:27:36 +0200 (CEST) Subject: [py-svn] r56711 - py/dist Message-ID: <20080722142736.1E3B6169E16@codespeak.net> Author: hpk Date: Tue Jul 22 16:27:35 2008 New Revision: 56711 Added: py/dist/ - copied from r56710, py/release/0.9.1/ Log: going for a saner "dist" snapshost, the current one is loughably outdated From hpk at codespeak.net Tue Jul 22 16:39:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 16:39:45 +0200 (CEST) Subject: [py-svn] r56712 - py/trunk/py/doc Message-ID: <20080722143945.EBC4D169E2A@codespeak.net> Author: hpk Date: Tue Jul 22 16:39:45 2008 New Revision: 56712 Modified: py/trunk/py/doc/TODO.txt Log: add a note about the needed FILE:LINENO summary. Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Tue Jul 22 16:39:45 2008 @@ -24,6 +24,14 @@ - BUG: write test/fix --showlocals (not showing anything) +- implement a summary at the end of a test run + FILENO:LINENO EXCONLY + FILENO:LINENO EXCONLY + ... + that should be easily parseable for editors like scits, vim. + +- implement "py.test --version" + - extend conftest mechanism to allow to check for: conftest_*.py @@ -37,6 +45,9 @@ - twisted support: checkout and integrate Samuele's twisted support files also look at Ralf Schmitt's way of going over greenlets +- ensure that a full "py.test --boxed" passes - probably needs + a bit of temp directory handling enhancements + - (ongoing) review and refactor architecture of py.test with particular respect to: - allow custom reporting @@ -47,6 +58,8 @@ - porting existing extensions (htmlconftest / PyPy's conftest's ...) +- remove "-j" "starton" option, maybe introduce keyword + expression syntax for the same purposes - fix reporting/usage regression after reporter-merge merge: - collapse skips with same reason and lineno into one line From hpk at codespeak.net Tue Jul 22 18:58:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 18:58:24 +0200 (CEST) Subject: [py-svn] r56716 - py/release/0.9.x Message-ID: <20080722165824.B489C169E79@codespeak.net> Author: hpk Date: Tue Jul 22 18:58:23 2008 New Revision: 56716 Added: py/release/0.9.x/ - copied from r56715, py/release/0.9.1/ Log: gather fixes for a just-in-case 0.9.2 From hpk at codespeak.net Tue Jul 22 19:01:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 19:01:13 +0200 (CEST) Subject: [py-svn] r56717 - in py: dist/py/test/rsession release/0.9.x/py/test/rsession Message-ID: <20080722170113.01B9F169E92@codespeak.net> Author: hpk Date: Tue Jul 22 19:01:13 2008 New Revision: 56717 Modified: py/dist/py/test/rsession/web.py py/release/0.9.x/py/test/rsession/web.py Log: fix the check for the generated javascript file (make "py.test -d -r" work) both on dist and on 0.9.x Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Tue Jul 22 19:01:13 2008 @@ -400,7 +400,7 @@ js_name = py.path.local(__file__).dirpath("webdata").join("source.js") web_name = py.path.local(__file__).dirpath().join("webjs.py") if IMPORTED_PYPY and web_name.mtime() > js_name.mtime() or \ - (not js_name.check()) or 1: + (not js_name.check()): from py.__.test.rsession import webjs javascript_source = rpython2javascript(webjs, Modified: py/release/0.9.x/py/test/rsession/web.py ============================================================================== --- py/release/0.9.x/py/test/rsession/web.py (original) +++ py/release/0.9.x/py/test/rsession/web.py Tue Jul 22 19:01:13 2008 @@ -400,7 +400,7 @@ js_name = py.path.local(__file__).dirpath("webdata").join("source.js") web_name = py.path.local(__file__).dirpath().join("webjs.py") if IMPORTED_PYPY and web_name.mtime() > js_name.mtime() or \ - (not js_name.check()) or 1: + (not js_name.check()): from py.__.test.rsession import webjs javascript_source = rpython2javascript(webjs, From hpk at codespeak.net Tue Jul 22 20:41:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 20:41:26 +0200 (CEST) Subject: [py-svn] r56718 - py/release/0.9.x/py/execnet Message-ID: <20080722184126.07A14169EC8@codespeak.net> Author: hpk Date: Tue Jul 22 20:41:25 2008 New Revision: 56718 Modified: py/release/0.9.x/py/execnet/inputoutput.py py/release/0.9.x/py/execnet/register.py Log: set in/out binary modes as early as possible. this improve execnet behaviour (py.test py\execnet and py.test -f) for me on windows. Modified: py/release/0.9.x/py/execnet/inputoutput.py ============================================================================== --- py/release/0.9.x/py/execnet/inputoutput.py (original) +++ py/release/0.9.x/py/execnet/inputoutput.py Tue Jul 22 20:41:25 2008 @@ -58,7 +58,10 @@ class Popen2IO: server_stmt = """ -import sys, StringIO +import os, sys, StringIO +import msvcrt +msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) +msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: @@ -70,10 +73,6 @@ error = (IOError, OSError, EOFError) def __init__(self, infile, outfile): - if sys.platform == 'win32': - import msvcrt - msvcrt.setmode(infile.fileno(), os.O_BINARY) - msvcrt.setmode(outfile.fileno(), os.O_BINARY) self.outfile, self.infile = infile, outfile self.readable = self.writeable = True self.lock = thread.allocate_lock() Modified: py/release/0.9.x/py/execnet/register.py ============================================================================== --- py/release/0.9.x/py/execnet/register.py (original) +++ py/release/0.9.x/py/execnet/register.py Tue Jul 22 20:41:25 2008 @@ -51,6 +51,10 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) + if sys.platform == 'win32': + import msvcrt + msvcrt.setmode(infile.fileno(), os.O_BINARY) + msvcrt.setmode(outfile.fileno(), os.O_BINARY) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) From hpk at codespeak.net Tue Jul 22 20:43:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 20:43:20 +0200 (CEST) Subject: [py-svn] r56719 - py/release/0.9.x/py/execnet Message-ID: <20080722184320.10BF7169EC9@codespeak.net> Author: hpk Date: Tue Jul 22 20:43:20 2008 New Revision: 56719 Modified: py/release/0.9.x/py/execnet/inputoutput.py Log: i know why i want ad-hoc cross-platform testing ... Modified: py/release/0.9.x/py/execnet/inputoutput.py ============================================================================== --- py/release/0.9.x/py/execnet/inputoutput.py (original) +++ py/release/0.9.x/py/execnet/inputoutput.py Tue Jul 22 20:43:20 2008 @@ -59,9 +59,10 @@ class Popen2IO: server_stmt = """ import os, sys, StringIO -import msvcrt -msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) -msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) +if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: From hpk at codespeak.net Tue Jul 22 20:46:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 20:46:12 +0200 (CEST) Subject: [py-svn] r56720 - py/release/0.9.x/py Message-ID: <20080722184612.4AEA1169ECB@codespeak.net> Author: hpk Date: Tue Jul 22 20:46:11 2008 New Revision: 56720 Modified: py/release/0.9.x/py/__init__.py Log: bump version Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Tue Jul 22 20:46:11 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.1" +version = "0.9.2-pre-alpha" initpkg(__name__, description = "py lib: agile development and test support library", From hpk at codespeak.net Tue Jul 22 21:12:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 21:12:20 +0200 (CEST) Subject: [py-svn] r56721 - py/trunk/py/doc Message-ID: <20080722191220.717452A019C@codespeak.net> Author: hpk Date: Tue Jul 22 21:12:17 2008 New Revision: 56721 Modified: py/trunk/py/doc/release-notes-1.0.0 Log: some more upcoming notes Modified: py/trunk/py/doc/release-notes-1.0.0 ============================================================================== --- py/trunk/py/doc/release-notes-1.0.0 (original) +++ py/trunk/py/doc/release-notes-1.0.0 Tue Jul 22 21:12:17 2008 @@ -4,6 +4,13 @@ py.test: - collector.run() deprecated, implement/use listdir() instead +- item.run() deprecated, item.execute() signature modified + # item.setup() and item.execute() are the ones to override +- py.test --pdb works without implied "-s" +- uses less RAM with long test runs??? (due to keeping less + references around) + +py.builtin: From hpk at codespeak.net Tue Jul 22 21:17:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 21:17:17 +0200 (CEST) Subject: [py-svn] r56722 - in py: branch/event/py/execnet dist/py/execnet trunk/py/execnet Message-ID: <20080722191717.B0E3E2A0197@codespeak.net> Author: hpk Date: Tue Jul 22 21:17:17 2008 New Revision: 56722 Modified: py/branch/event/py/execnet/inputoutput.py py/branch/event/py/execnet/register.py py/dist/py/execnet/inputoutput.py py/dist/py/execnet/register.py py/trunk/py/execnet/inputoutput.py py/trunk/py/execnet/register.py Log: porting the windows-execnet 56717 and 56718 fixes to other active branches Modified: py/branch/event/py/execnet/inputoutput.py ============================================================================== --- py/branch/event/py/execnet/inputoutput.py (original) +++ py/branch/event/py/execnet/inputoutput.py Tue Jul 22 21:17:17 2008 @@ -58,7 +58,11 @@ class Popen2IO: server_stmt = """ -import sys, StringIO +import os, sys, StringIO +if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: @@ -70,10 +74,6 @@ error = (IOError, OSError, EOFError) def __init__(self, infile, outfile): - if sys.platform == 'win32': - import msvcrt - msvcrt.setmode(infile.fileno(), os.O_BINARY) - msvcrt.setmode(outfile.fileno(), os.O_BINARY) self.outfile, self.infile = infile, outfile self.readable = self.writeable = True self.lock = thread.allocate_lock() Modified: py/branch/event/py/execnet/register.py ============================================================================== --- py/branch/event/py/execnet/register.py (original) +++ py/branch/event/py/execnet/register.py Tue Jul 22 21:17:17 2008 @@ -51,6 +51,10 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) + if sys.platform == 'win32': + import msvcrt + msvcrt.setmode(infile.fileno(), os.O_BINARY) + msvcrt.setmode(outfile.fileno(), os.O_BINARY) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) Modified: py/dist/py/execnet/inputoutput.py ============================================================================== --- py/dist/py/execnet/inputoutput.py (original) +++ py/dist/py/execnet/inputoutput.py Tue Jul 22 21:17:17 2008 @@ -58,7 +58,11 @@ class Popen2IO: server_stmt = """ -import sys, StringIO +import os, sys, StringIO +if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: @@ -70,10 +74,6 @@ error = (IOError, OSError, EOFError) def __init__(self, infile, outfile): - if sys.platform == 'win32': - import msvcrt - msvcrt.setmode(infile.fileno(), os.O_BINARY) - msvcrt.setmode(outfile.fileno(), os.O_BINARY) self.outfile, self.infile = infile, outfile self.readable = self.writeable = True self.lock = thread.allocate_lock() Modified: py/dist/py/execnet/register.py ============================================================================== --- py/dist/py/execnet/register.py (original) +++ py/dist/py/execnet/register.py Tue Jul 22 21:17:17 2008 @@ -51,6 +51,10 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) + if sys.platform == 'win32': + import msvcrt + msvcrt.setmode(infile.fileno(), os.O_BINARY) + msvcrt.setmode(outfile.fileno(), os.O_BINARY) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) Modified: py/trunk/py/execnet/inputoutput.py ============================================================================== --- py/trunk/py/execnet/inputoutput.py (original) +++ py/trunk/py/execnet/inputoutput.py Tue Jul 22 21:17:17 2008 @@ -58,7 +58,11 @@ class Popen2IO: server_stmt = """ -import sys, StringIO +import os, sys, StringIO +if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: @@ -70,10 +74,6 @@ error = (IOError, OSError, EOFError) def __init__(self, infile, outfile): - if sys.platform == 'win32': - import msvcrt - msvcrt.setmode(infile.fileno(), os.O_BINARY) - msvcrt.setmode(outfile.fileno(), os.O_BINARY) self.outfile, self.infile = infile, outfile self.readable = self.writeable = True self.lock = thread.allocate_lock() Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Tue Jul 22 21:17:17 2008 @@ -51,6 +51,10 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) + if sys.platform == 'win32': + import msvcrt + msvcrt.setmode(infile.fileno(), os.O_BINARY) + msvcrt.setmode(outfile.fileno(), os.O_BINARY) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) From hpk at codespeak.net Tue Jul 22 22:00:55 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 22 Jul 2008 22:00:55 +0200 (CEST) Subject: [py-svn] r56723 - in py/branch/event/py/test2: . testing Message-ID: <20080722200055.6CD2F168515@codespeak.net> Author: hpk Date: Tue Jul 22 22:00:54 2008 New Revision: 56723 Modified: py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: SystemExit exceptions should not end the testing process. Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Jul 22 22:00:54 2008 @@ -32,7 +32,7 @@ outcome = "passed" finally: outerr = capture.reset() - except py.builtin.sysex: + except KeyboardInterrupt: raise except: excinfo = py.code.ExceptionInfo() Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Tue Jul 22 22:00:54 2008 @@ -63,6 +63,16 @@ assert not testrep.failed assert not testrep.setupfailed + def test_systemexit_does_not_bail_out(self): + testrep = self.runtestfunc(""" + import sys + def testfunc(): + raise SystemExit(42) + """) + assert testrep.failed + assert not testrep.setupfailed + assert testrep.repr_run.find("SystemExit") != -1 + def test_run_capture_stdout(self): testrep = self.runtestfunc(""" def testfunc(): From hpk at codespeak.net Wed Jul 23 13:19:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 13:19:11 +0200 (CEST) Subject: [py-svn] r56734 - py/branch/bugfix-0.9.0 Message-ID: <20080723111911.64D9A169DB6@codespeak.net> Author: hpk Date: Wed Jul 23 13:19:10 2008 New Revision: 56734 Removed: py/branch/bugfix-0.9.0/ Log: removing this, we have release/0.9.x for the bugfix branch From hpk at codespeak.net Wed Jul 23 13:20:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 13:20:31 +0200 (CEST) Subject: [py-svn] r56735 - py/branch/reporter-merge Message-ID: <20080723112031.BC861169DFA@codespeak.net> Author: hpk Date: Wed Jul 23 13:20:30 2008 New Revision: 56735 Removed: py/branch/reporter-merge/ Log: removing this branch as it got merged mostly - and is in either case supserseded by the current event branch. From hpk at codespeak.net Wed Jul 23 13:25:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 13:25:29 +0200 (CEST) Subject: [py-svn] r56736 - py/branch/guido-svn-auth Message-ID: <20080723112529.098B9169E43@codespeak.net> Author: hpk Date: Wed Jul 23 13:25:29 2008 New Revision: 56736 Removed: py/branch/guido-svn-auth/ Log: i am pretty sure that guido merged this for 0.9.1 if not -> resurrect. From hpk at codespeak.net Wed Jul 23 15:04:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 15:04:05 +0200 (CEST) Subject: [py-svn] r56737 - in py/branch/event/py/test2: . rsession testing Message-ID: <20080723130405.1C4CB2A00DD@codespeak.net> Author: hpk Date: Wed Jul 23 15:04:03 2008 New Revision: 56737 Modified: py/branch/event/py/test2/rsession/slave.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_runner.py py/branch/event/py/test2/testing/test_session.py Log: refactoring the runner to not use the config "kitchen sink" object but require explicit objects for its operations. also involves a refactoring of runner tests making them independent from the collection process, more unit-testish. Modified: py/branch/event/py/test2/rsession/slave.py ============================================================================== --- py/branch/event/py/test2/rsession/slave.py (original) +++ py/branch/event/py/test2/rsession/slave.py Wed Jul 23 15:04:03 2008 @@ -14,7 +14,7 @@ break item = config._getcollector(itemspec) runner = item._getrunner() - testrep = runner(item) + testrep = runner(item, item._config._setupstate, item._config._getcapture) send(testrep.dumps()) def setup(): Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Wed Jul 23 15:04:03 2008 @@ -5,7 +5,7 @@ import py, os, sys from py.__.test2 import repevent -from py.__.test2.outcome import Skipped +from py.__.test2.outcome import Skipped, Exit from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb from py.__.test2 import present @@ -16,23 +16,24 @@ self.excinfo = excinfo self.outerr = outerr -def basic_runner(item): +def basic_runner(item, setupstate, getcapture, pdbfunc=None): """ returns a test report after having run the given test item. """ - config = item._config - capture = config._getcapture(path=item.fspath) + capture = getcapture() excinfo = None try: try: outcome = "setupfailed" - config._setupstate.prepare(item) - outcome = "failed" - item.execute() - outcome = "setupfailed" - config._setupstate.teardown_exact(item) + setupstate.prepare(item) + try: + item.execute() + finally: + outcome = "setupfailed" + setupstate.teardown_exact(item) + outcome = "failed" outcome = "passed" finally: outerr = capture.reset() - except KeyboardInterrupt: + except (Exit, KeyboardInterrupt): raise except: excinfo = py.code.ExceptionInfo() @@ -52,38 +53,42 @@ title="failure during setup/teardown") rep = repevent.ItemTestReport(trail, outcome, repr_run, repr_path) - if config.option.usepdb and rep.failed: + if pdbfunc and rep.failed: # xxx print rep.repr_run() ? - py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) + pdbfunc(excinfo._excinfo[2]) return rep -def fork_runner(item): +def fork_runner(item, setupstate, getcapture): def runforked(): - testrep = basic_runner(item) + testrep = basic_runner(item, setupstate, getcapture) return testrep.dumps() ff = py.io.ForkedFunc(runforked) result = ff.waitfinish() + print vars(result) if result.retval is not None: - testrep = repevent.ItemTestReport.fromdumps(result.retval) + return repevent.ItemTestReport.fromdumps(result.retval) #testrep.stdout = result.out #testrep.stderr = result.err else: - # xxx look into reusing present/tbpresent-writer - tw = py.io.TerminalWriter() - tw.sep("_", "CRASHED with signal=%d: %s" % - (result.signal, present.getmodpath(item))) - code = py.code.Code(item.obj) - path, firstlineno = code.path, code.firstlineno - src = py.code.Source(item.obj) - tw.line() - tw.line(str(src.indent())) - tw.line("[%s:%d]" %(path, firstlineno)) - repr_run = tw.stringio.getvalue() - repr_path = item.repr_path() - trail = item._get_collector_trail() - testrep = repevent.ItemTestReport(trail, "failed", repr_run, repr_path) - #testrep.stdout = result.out - #testrep.stderr = result.err + return report_crash(item, result) + +def report_crash(item, result): + # xxx look into reusing present/tbpresent-writer + tw = py.io.TerminalWriter() + tw.sep("_", "CRASHED with signal=%d: %s" % + (result.signal, present.getmodpath(item))) + code = py.code.Code(item.obj) + path, firstlineno = code.path, code.firstlineno + src = py.code.Source(item.obj) + tw.line() + tw.line(str(src.indent())) + tw.line("[%s:%d]" %(path, firstlineno)) + repr_run = tw.stringio.getvalue() + repr_path = item.repr_path() + trail = item._get_collector_trail() + testrep = repevent.ItemTestReport(trail, "failed", repr_run, repr_path) + #testrep.stdout = result.out + #testrep.stderr = result.err return testrep Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Jul 23 15:04:03 2008 @@ -85,5 +85,5 @@ def runtest(self, item): runner = item._getrunner() - return runner(item) + return runner(item, item._config._setupstate, item._config._getcapture) Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Wed Jul 23 15:04:03 2008 @@ -1,213 +1,236 @@ import py -from py.__.test2.runner import basic_runner, fork_runner +from py.__.test2 import runner +from py.__.test2.outcome import Exit +from py.__.test2 import present def setup_module(mod): mod.tmpdir = py.test.ensuretemp(mod.__name__) -class TestRunner: - def getrunner(self): - return basic_runner - - def setup_method(self, method): - self.tmpdir = tmpdir.join("%s_%s" % - (self.__class__.__name__, method.__name__)) - - def getmodulecol(self, func, funcname="testfunc"): - funcname = getattr(func, '__name__', funcname) - self.tmpdir.ensure("__init__.py") - path = self.tmpdir.ensure(funcname + ".py") - path.write(py.code.Source(func)) - self.config = py.test2.config._reparse([path.dirpath()]) - modulecol = self.config._getcollector(path) - return modulecol - - def makeitem(self, source, funcname="testfunc"): - modulecol = self.getmodulecol(source) - item = modulecol.join(funcname) - assert item is not None, (item, funcname) - return item - - def getitems(self, source): - modulecol = self.getmodulecol(source) - return [modulecol.join(x) for x in modulecol.listdir()] +class MockItem: + def __init__(self, func): + self.func = func + + def _get_collector_trail(self): + return "MockItem.trail" + + def repr_path(self): + return "MockItem.repr_path" + + def repr_run(self, runnerinfo): + excinfo = runnerinfo.excinfo + if not excinfo: + return ("(%s)MockItem.repr_run" %(runnerinfo.outerr,)) + else: + assert isinstance(excinfo, py.code.ExceptionInfo) + return ("(%s)MockItem.repr_run: %s" %( + runnerinfo.outerr, excinfo.exconly())) + + def execute(self): + self.func() + +class MockCapture: + def reset(self): + return "out", "err" + +class MockSetupState: + def prepare(self, item): + return + def teardown_exact(self, item): + pass + def teardown_all(self): + pass - def runtestfunc(self, func, funcname="testfunc"): - item = self.makeitem(func, funcname=funcname) +class RunnerTests: + def run(self, func, setupstate=None, getcapture=None): runner = self.getrunner() - return runner(item) + item = MockItem(func) + if not setupstate: setupstate = MockSetupState() + if not getcapture: getcapture = MockCapture + testrep = runner(item, setupstate, getcapture) + return testrep def test_run_pass(self): - testrep = self.runtestfunc(""" - def testfunc(): - pass - """) - assert testrep.passed + def test_func(): + pass + testrep = self.run(test_func) + assert testrep.passed def test_run_fail(self): - testrep = self.runtestfunc(""" - def testfunc(): - assert 0 - """) + def testfunc(): + assert 0 + testrep = self.run(testfunc) assert not testrep.passed assert testrep.failed def test_run_skip(self): - testrep = self.runtestfunc(""" - import py - def testfunc(): - py.test2.skip("hello") - """) + def testfunc(): + py.test2.skip("hello") + testrep = self.run(testfunc) assert testrep.skipped assert not testrep.passed assert not testrep.failed assert not testrep.setupfailed def test_systemexit_does_not_bail_out(self): - testrep = self.runtestfunc(""" - import sys - def testfunc(): - raise SystemExit(42) - """) + def testfunc(): + raise SystemExit(42) + try: + testrep = self.run(testfunc) + except SystemExit: + py.test.fail("runner did not catch SystemExit") assert testrep.failed assert not testrep.setupfailed assert testrep.repr_run.find("SystemExit") != -1 - def test_run_capture_stdout(self): - testrep = self.runtestfunc(""" - def testfunc(): - print "samfing" - """) - assert testrep.passed - assert testrep.repr_run.find("stderr") == -1 - assert testrep.repr_run.find("samfing\n") != -1 - - def test_run_capture_stderr(self): - testrep = self.runtestfunc(""" - import sys - def testfunc(): - print >>sys.stderr, "samfong" - """) - print testrep.repr_run - assert testrep.repr_run.find("stdout") == -1 - assert testrep.repr_run.find("stderr") != -1 - assert testrep.repr_run.find("samfong\n") != -1 - - def test_print_with_assertion_failed(self): - testrep = self.runtestfunc(""" - import sys - def testfunc(): - print "samfing elz" - print >>sys.stderr, "sure" - assert 0 - """) - assert not testrep.passed - assert testrep.failed - assert testrep.repr_run.find("stdout") != -1 - assert testrep.repr_run.find("stderr") != -1 - assert testrep.repr_run.find("samfing elz") != -1 - assert testrep.repr_run.find("sure") != -1 - - def test_print_with_explicit_fail(self): - testrep = self.runtestfunc(""" - import py, sys - def testfunc(): - print "samfing elz" - print >>sys.stderr, "sure" - py.test.fail() - """) - assert not testrep.passed - assert testrep.failed - assert testrep.repr_run.find("stdout") != -1 - assert testrep.repr_run.find("stderr") != -1 - assert testrep.repr_run.find("samfing elz\n") != -1 - assert testrep.repr_run.find("sure\n") != -1 - - def test_setupfailure(self): - testrep = self.runtestfunc(""" - def setup_module(mod): - raise ValueError(10) - def testfunc(): - pass - """) + def test_exit_does_bail_out(self): + def testfunc(): + raise Exit() + py.test.raises(Exit, "self.run(testfunc)") + + def test_runner_handles_skip_in_prepare(self): + l = [] + class MySetupState: + def prepare(self, item): + py.test2.skip("skip") + def testfunc(): pass + def pythonreprrun(item, runnerinfo, title=None): return "" + py.magic.patch(present, "python_repr_run", pythonreprrun) + try: + testrep = self.run(testfunc, setupstate=MySetupState()) + finally: + py.magic.revert(present, "python_repr_run") + assert testrep.skipped + assert not testrep.failed assert not testrep.passed - assert testrep.setupfailed - assert testrep.failed - s = testrep.repr_run - print s - exp = "failure during setup/teardown" - assert s.find(exp) != -1 - i = s.find("def ") - assert s[i+4:].startswith("setup_module") - - def test_setup_skip(self): - testrep = self.runtestfunc(""" - import py - def setup_module(mod): - py.test.skip("setupmoduleskip") - def testfunc(): - pass - """) - assert testrep.skipped + def test_runnner_handles_setupfailure_in_prepare(self): + l = [] + class MySetupState: + def prepare(self, item): + raise ValueError(2) + def testfunc(): + pass + def pythonreprrun(item, runnerinfo, title=None): + assert runnerinfo.excinfo.errisinstance(ValueError) + return "pythonreprrun" + py.magic.patch(present, "python_repr_run", pythonreprrun) + try: + testrep = self.run(testfunc, setupstate=MySetupState()) + finally: + py.magic.revert(present, "python_repr_run") + assert testrep.setupfailed + assert testrep.failed assert not testrep.passed - assert not testrep.setupfailed - assert not testrep.failed + assert testrep.repr_run == "pythonreprrun" - def test_setupfailure_always_python_traceback(self): - item = self.makeitem(""" - def setup_module(mod): - raise ValueError(10) - def testfunc(): - pass - """) - runner = self.getrunner() - item.repr_run = lambda *args: "cusWRONG TRACEBACK!" - testrep = runner(item) - assert not testrep.passed - assert testrep.setupfailed - assert testrep.failed - # repr_run of setupfailures should always - # display python Tracebacks starting from the - # failing setup function - s = testrep.repr_run - print s - i = s.find("def ") - assert s[i+4:].startswith("setup_module") - - def test_setupfailure_on_eager_teardown(self): - testrep = self.runtestfunc(""" - def testfunc(): + def test_runner_handles_capture(self): + l = [] + class MyCapture: + def __init__(self): + l.append("init") + def reset(self): + l.append("reset") + return ":".join(l), "" + def testfunc(): + l.append("run") + testrep = self.run(testfunc, getcapture=MyCapture) + assert testrep.passed + assert testrep.repr_run.find("init:run:reset") != -1 + + def testfuncfail(): + l.append("run") + assert 0 + l[:] = [] + testrep = self.run(testfuncfail, getcapture=MyCapture) + assert testrep.repr_run.find("init:run:reset") != -1 + assert testrep.failed + +class TestBasicRunner(RunnerTests): + def getrunner(self): + return runner.basic_runner + + def test_runner_handles_setupstate(self): + l = [] + class MySetupState: + def prepare(self, item): + l.append("prepare") + def teardown_exact(self, item): + l.append("teardown_exact") + def testfunc(): + l.append("run") + testrep = self.run(testfunc, setupstate=MySetupState()) + assert l == ["prepare", "run", "teardown_exact"] + assert testrep.passed + + def testfuncfail(): + l.append("run") + assert 0 + l[:] = [] + testrep = self.run(testfuncfail, setupstate=MySetupState()) + assert l == ["prepare", "run", "teardown_exact"] + assert testrep.failed + + + def test_runnner_handles_setupfailure_in_eager_teardown(self): + l = [] + class MySetupState: + def prepare(self, item): pass - def teardown_function(func): - raise ValueError(11) - """) + def teardown_exact(self, item): + raise ValueError(17) + def testfunc(): + l.append(0) + pass + def pythonreprrun(item, runnerinfo, title=None): + l.append(runnerinfo) + py.magic.patch(present, "python_repr_run", pythonreprrun) + try: + testrep = self.run(testfunc, setupstate=MySetupState()) + finally: + py.magic.revert(present, "python_repr_run") assert testrep.setupfailed + assert testrep.failed assert not testrep.passed - assert testrep.failed - s = testrep.repr_run - print s - i = s.find("def ") - assert s[i+4:].startswith("teardown_function") + assert len(l) == 2 # means that testfunc didnt run + assert l[0] == 0 + assert l[1].excinfo.errisinstance(ValueError) + + def test_runner_handles_pdb(self): + py.test.skip("check for proper pdb interaction") -class TestForkRunner(TestRunner): +class TestForkRunner(RunnerTests): def setup_class(cls): if not hasattr(py.std.os, 'fork'): py.test.skip("need os.fork()") + def getrunner(self): - return fork_runner + return runner.fork_runner + + def test_exit_does_bail_out(self): + py.test.skip("XXX needs work") + def testfunc(): + raise Exit() + py.test.raises(Exit, "self.run(testfunc)") def test_suicide(self): - testrep = self.runtestfunc(""" + def testfunc(): import os - def testfunc(): - os.kill(os.getpid(), 15) - # - """) - assert not testrep.passed - assert testrep.failed - assert testrep.repr_run.find("CRASHED") != -1 - assert testrep.repr_run.find("signal=15") != -1 - print testrep.repr_run + os.kill(os.getpid(), 15) + crashes = [] + def myreport(item, result): + crashes.append((item, result)) + py.magic.patch(runner, "report_crash", myreport) + try: + testrep = self.run(testfunc) + finally: + py.magic.revert(runner, "report_crash") + assert len(crashes) == 1 + assert crashes[0][1].signal == 15 # assert 0 +def test_crash_report(): + py.test.skip("check that crash reporting works") + +def test_present_tb_and_python_repr_run(): + py.test.skip("check that present_tb and python_repr_run work") Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Wed Jul 23 15:04:03 2008 @@ -206,6 +206,7 @@ assert len(failedcollections) == 2 def test_pdb_run(self): + py.test.skip("fix this test after the runner refactoring") tfile = suptest.makeuniquepyfile(""" def test_usepdb(): assert 0 From hpk at codespeak.net Wed Jul 23 15:18:01 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 15:18:01 +0200 (CEST) Subject: [py-svn] r56738 - py/branch/event/py/test2/testing Message-ID: <20080723131801.CF4DD2A00DD@codespeak.net> Author: hpk Date: Wed Jul 23 15:17:59 2008 New Revision: 56738 Modified: py/branch/event/py/test2/testing/test_runner.py Log: remove not needed tempdir Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Wed Jul 23 15:17:59 2008 @@ -3,9 +3,7 @@ from py.__.test2 import runner from py.__.test2.outcome import Exit from py.__.test2 import present - -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) +from py.__.test2 import mock class MockItem: def __init__(self, func): From hpk at codespeak.net Wed Jul 23 15:33:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 15:33:08 +0200 (CEST) Subject: [py-svn] r56739 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080723133308.687342A01A2@codespeak.net> Author: hpk Date: Wed Jul 23 15:33:07 2008 New Revision: 56739 Added: py/branch/event/py/test2/pypresent.py - copied, changed from r56729, py/branch/event/py/test2/present.py py/branch/event/py/test2/testing/test_pypresent.py - copied, changed from r56729, py/branch/event/py/test2/testing/test_present.py Removed: py/branch/event/py/test2/present.py py/branch/event/py/test2/rep/testing/test_reporter_helper.py py/branch/event/py/test2/testing/test_present.py Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/item.py py/branch/event/py/test2/rep/reporter.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: move python related presentation issues into pypresent.py Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Jul 23 15:33:07 2008 @@ -25,7 +25,6 @@ """ from __future__ import generators import py -from py.__.test2 import present def configproperty(name): def fget(self): Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Wed Jul 23 15:33:07 2008 @@ -1,7 +1,7 @@ import py from py.__.test2.collect import FunctionMixin, Node from py.__.test2.runner import basic_runner, fork_runner -from py.__.test2 import present +from py.__.test2 import pypresent _dummy = object() @@ -71,10 +71,9 @@ def repr_run(self, runnerinfo): """ return a textual representation of run info. """ - return present.python_repr_run(self, runnerinfo) + return pypresent.python_repr_run(self, runnerinfo) def repr_path(self): - from py.__.test2.rep.reporter import getrelpath, getmodpath - fspath = getrelpath(self._config.topdir, self.fspath) - modpath = getmodpath(self) + fspath = pypresent.getrelpath(self._config.topdir, self.fspath) + modpath = pypresent.getmodpath(self) return (fspath, modpath) Deleted: /py/branch/event/py/test2/present.py ============================================================================== --- /py/branch/event/py/test2/present.py Wed Jul 23 15:33:07 2008 +++ (empty file) @@ -1,49 +0,0 @@ -""" - Some specifics about reporting Python tracebacks in py.test -""" - -import py -from py.__.code.tbpresent import TBPresenter -from py.__.test2.rep.reporter import getmodpath - -class PythonFunctionPresenter(TBPresenter): - """ presenting information about failing Functions and Generators. """ - def __init__(self, config, item): - self._config = config - self._item = item - super(PythonFunctionPresenter, self).__init__( - showlocals=config.option.showlocals, - style=config.option.tbstyle, - ) - - def header(self, title=None): - item = self._item - if title is None: - modpath = getmodpath(item) - title = "entrypoint: %s" % modpath - self.out.sep("_", title) - self.out.line("") - if item.name[-1] == "]": # print extra info for generated tests - if not self._config.option.fulltrace: - args = self._saferepr(item._args) - line = "%s%s -> %s%s" %(item.parent.name, item.name, - item.obj.__name__, args) - self.out.line(line) - - - def repr_run(self, runnerinfo, title=None): - excinfo = runnerinfo.excinfo - self.header(title) - if excinfo: - excinfo.traceback = self._item.prunetraceback(excinfo.traceback) - self.repr_tb(excinfo) - for name, value in zip(["recorded stdout", "recorded stderr"], runnerinfo.outerr): - if value: - self.out.sep("- ", "%s, len=%d" %(name, len(value))) - self.out.line(value.rstrip()) - self.repr_sep("_") - -def python_repr_run(item, runnerinfo, title=None): - p = PythonFunctionPresenter(item._config, item) - p.repr_run(runnerinfo, title=title) - return p.out.stringio.getvalue() Copied: py/branch/event/py/test2/pypresent.py (from r56729, py/branch/event/py/test2/present.py) ============================================================================== --- py/branch/event/py/test2/present.py (original) +++ py/branch/event/py/test2/pypresent.py Wed Jul 23 15:33:07 2008 @@ -1,10 +1,8 @@ """ Some specifics about reporting Python tracebacks in py.test """ - import py from py.__.code.tbpresent import TBPresenter -from py.__.test2.rep.reporter import getmodpath class PythonFunctionPresenter(TBPresenter): """ presenting information about failing Functions and Generators. """ @@ -47,3 +45,37 @@ p = PythonFunctionPresenter(item._config, item) p.repr_run(runnerinfo, title=title) return p.out.stringio.getvalue() + +def getmodpath(pycolitem): + """ return dotted module path for the given colitem. """ + colitems = pycolitem.listchain() + while colitems: + colitem = colitems.pop(0) + if isinstance(colitem, colitem.Module): + parts = [colitem.obj.__name__] + for colitem in colitems: + if colitem.name[0] in '([': + parts[-1] += colitem.name + else: + parts.append(colitem.name) + return ".".join(parts) + return colitem.name + +def repr_pythonversion(): + v = py.std.sys.version_info + try: + return "%s.%s.%s-%s-%s" % v + except (TypeError, ValueError): + return str(v) + +def getrelpath(source, dest): + base = source.common(dest) + if not base: + return None + # with posix local paths '/' is always a common base + relsource = source.relto(base) + reldest = dest.relto(base) + n = relsource.count(source.sep) + target = dest.sep.join(('..', )*n + (reldest, )) + return target + Modified: py/branch/event/py/test2/rep/reporter.py ============================================================================== --- py/branch/event/py/test2/rep/reporter.py (original) +++ py/branch/event/py/test2/rep/reporter.py Wed Jul 23 15:33:07 2008 @@ -14,39 +14,6 @@ from time import time as now -def getrelpath(source, dest): - base = source.common(dest) - if not base: - return None - # with posix local paths '/' is always a common base - relsource = source.relto(base) - reldest = dest.relto(base) - n = relsource.count(source.sep) - target = dest.sep.join(('..', )*n + (reldest, )) - return target - -def getmodpath(pycolitem): - """ return dotted module path for the given colitem. """ - # XXX what about non-functions? - colitems = pycolitem.listchain() - while colitems: - colitem = colitems.pop(0) - if isinstance(colitem, colitem.Module): - parts = [colitem.obj.__name__] - for colitem in colitems: - if colitem.name[0] in '([': - parts[-1] += colitem.name - else: - parts.append(colitem.name) - return ".".join(parts) - return colitem.name - -def repr_pythonversion(): - v = py.std.sys.version_info - try: - return "%s.%s.%s-%s-%s" % v - except (TypeError, ValueError): - return str(v) def choose_reporter(reporterclass, config): option = config.option Deleted: /py/branch/event/py/test2/rep/testing/test_reporter_helper.py ============================================================================== --- /py/branch/event/py/test2/rep/testing/test_reporter_helper.py Wed Jul 23 15:33:07 2008 +++ (empty file) @@ -1,37 +0,0 @@ - -import py - -from py.__.test2.rep.reporter import getmodpath, repr_pythonversion -from py.__.test2.testing import setupdata - -def test_getmodpath_cases(): - tmpdir = py.test.ensuretemp("test_getmodpath_cases") - pkgdir = tmpdir.join("pkg") - pkgdir.ensure("__init__.py") - nopkgdir = tmpdir.ensure("nopkg", dir=1) - def checkpkg(names, expected): - fcol = setupdata.getexamplecollector(names, tmpdir=pkgdir) - assert getmodpath(fcol) == pkgdir.basename + "." + expected - def checknopkg(names, expected): - fcol = setupdata.getexamplecollector(names, tmpdir=nopkgdir) - assert getmodpath(fcol) == expected - - for names in ( - 'mod.py test_f1 mod.test_f1', - 'mod.py TestA () test_m1 mod.TestA().test_m1', - 'mod.py test_g1 mod.test_g1', - 'mod.py test_g1 [0] mod.test_g1[0]', - ): - names = names.split() - expected = names.pop() - yield checkpkg, names, expected - yield checknopkg, names, expected - -def test_repr_python_version(): - py.magic.patch(py.std.sys, 'version_info', (2, 5, 1, 'final', 0)) - try: - assert repr_pythonversion() == "2.5.1-final-0" - py.std.sys.version_info = x = (2,3) - assert repr_pythonversion() == str(x) - finally: - py.magic.revert(py.std.sys, 'version_info') Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Wed Jul 23 15:33:07 2008 @@ -8,7 +8,7 @@ from py.__.test2.outcome import Skipped, Exit from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb -from py.__.test2 import present +from py.__.test2 import pypresent class RunnerInfo: """ info on test runs. """ @@ -49,7 +49,7 @@ if outcome != "setupfailed": repr_run = item.repr_run(runnerinfo) else: - repr_run = present.python_repr_run(item, runnerinfo, + repr_run = pypresent.python_repr_run(item, runnerinfo, title="failure during setup/teardown") rep = repevent.ItemTestReport(trail, outcome, repr_run, repr_path) @@ -78,7 +78,7 @@ # xxx look into reusing present/tbpresent-writer tw = py.io.TerminalWriter() tw.sep("_", "CRASHED with signal=%d: %s" % - (result.signal, present.getmodpath(item))) + (result.signal, pypresent.getmodpath(item))) code = py.code.Code(item.obj) path, firstlineno = code.path, code.firstlineno src = py.code.Source(item.obj) Deleted: /py/branch/event/py/test2/testing/test_present.py ============================================================================== --- /py/branch/event/py/test2/testing/test_present.py Wed Jul 23 15:33:07 2008 +++ (empty file) @@ -1,56 +0,0 @@ -import py -from py.__.test2 import present, repevent -import suptest, setupdata -import re, sys - -class TestPresenter: - def setup_class(cls): - cls.tmpdir = py.test2.ensuretemp(cls.__name__) - - def getpresenter(self, cmdlinearg=None): - args = [self.tmpdir] - if cmdlinearg: - args.append(cmdlinearg) - config = py.test2.config._reparse(args) - return present.FuncPresenter(config) - - def test_repr_pruning_tb_generated_test(self): - itemtestreport,fn = suptest.getItemTestReport(""" - def test_gen(): - def check(x): - assert x - yield check, 0 - """) - s = itemtestreport.repr_run - print s - lines = s.split("\n") - assert lines[0].find("test_0.test_0.test_gen[0]") != -1 - assert lines[2].find("test_gen[0] -> check(0,)") != -1 - assert lines[3].find("def check(x):") != -1 - - def test_repr_tb_short(self): - # XXX probably a redundant test - itemtestreport,fn = suptest.getItemTestReport(""" - def f(x): - assert x - def test_f(): - f(0) - """, tb="short") - s = itemtestreport.repr_run - print s - index = -1 - basename = fn.basename - lines = s.split("\n")[2:] - for line in ( - ' File "%s", line 5, in test_f' % basename, - ' f(0)', - ' File "%s", line 3, in f' % basename, - ' assert x', - 'E assert 0' - ): - actual = lines.pop(0) - actual = actual.rstrip() - if line != actual: - print "expected:", repr(line) - print "got :", repr(actual) - assert 0 Copied: py/branch/event/py/test2/testing/test_pypresent.py (from r56729, py/branch/event/py/test2/testing/test_present.py) ============================================================================== --- py/branch/event/py/test2/testing/test_present.py (original) +++ py/branch/event/py/test2/testing/test_pypresent.py Wed Jul 23 15:33:07 2008 @@ -1,7 +1,8 @@ import py -from py.__.test2 import present, repevent +from py.__.test2 import pypresent +from py.__.test2.testing import setupdata import suptest, setupdata -import re, sys +import sys class TestPresenter: def setup_class(cls): @@ -12,7 +13,7 @@ if cmdlinearg: args.append(cmdlinearg) config = py.test2.config._reparse(args) - return present.FuncPresenter(config) + return pypresent.FuncPresenter(config) def test_repr_pruning_tb_generated_test(self): itemtestreport,fn = suptest.getItemTestReport(""" @@ -54,3 +55,36 @@ print "expected:", repr(line) print "got :", repr(actual) assert 0 + + +def test_getmodpath_cases(): + tmpdir = py.test.ensuretemp("test_getmodpath_cases") + pkgdir = tmpdir.join("pkg") + pkgdir.ensure("__init__.py") + nopkgdir = tmpdir.ensure("nopkg", dir=1) + def checkpkg(names, expected): + fcol = setupdata.getexamplecollector(names, tmpdir=pkgdir) + assert pypresent.getmodpath(fcol) == pkgdir.basename + "." + expected + def checknopkg(names, expected): + fcol = setupdata.getexamplecollector(names, tmpdir=nopkgdir) + assert pypresent.getmodpath(fcol) == expected + + for names in ( + 'mod.py test_f1 mod.test_f1', + 'mod.py TestA () test_m1 mod.TestA().test_m1', + 'mod.py test_g1 mod.test_g1', + 'mod.py test_g1 [0] mod.test_g1[0]', + ): + names = names.split() + expected = names.pop() + yield checkpkg, names, expected + yield checknopkg, names, expected + +def test_repr_python_version(): + py.magic.patch(py.std.sys, 'version_info', (2, 5, 1, 'final', 0)) + try: + assert pypresent.repr_pythonversion() == "2.5.1-final-0" + py.std.sys.version_info = x = (2,3) + assert pypresent.repr_pythonversion() == str(x) + finally: + py.magic.revert(py.std.sys, 'version_info') Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Wed Jul 23 15:33:07 2008 @@ -2,7 +2,7 @@ import py from py.__.test2 import runner from py.__.test2.outcome import Exit -from py.__.test2 import present +from py.__.test2 import pypresent from py.__.test2 import mock class MockItem: @@ -93,11 +93,11 @@ py.test2.skip("skip") def testfunc(): pass def pythonreprrun(item, runnerinfo, title=None): return "" - py.magic.patch(present, "python_repr_run", pythonreprrun) + py.magic.patch(pypresent, "python_repr_run", pythonreprrun) try: testrep = self.run(testfunc, setupstate=MySetupState()) finally: - py.magic.revert(present, "python_repr_run") + py.magic.revert(pypresent, "python_repr_run") assert testrep.skipped assert not testrep.failed assert not testrep.passed @@ -112,11 +112,11 @@ def pythonreprrun(item, runnerinfo, title=None): assert runnerinfo.excinfo.errisinstance(ValueError) return "pythonreprrun" - py.magic.patch(present, "python_repr_run", pythonreprrun) + py.magic.patch(pypresent, "python_repr_run", pythonreprrun) try: testrep = self.run(testfunc, setupstate=MySetupState()) finally: - py.magic.revert(present, "python_repr_run") + py.magic.revert(pypresent, "python_repr_run") assert testrep.setupfailed assert testrep.failed assert not testrep.passed @@ -182,11 +182,11 @@ pass def pythonreprrun(item, runnerinfo, title=None): l.append(runnerinfo) - py.magic.patch(present, "python_repr_run", pythonreprrun) + py.magic.patch(pypresent, "python_repr_run", pythonreprrun) try: testrep = self.run(testfunc, setupstate=MySetupState()) finally: - py.magic.revert(present, "python_repr_run") + py.magic.revert(pypresent, "python_repr_run") assert testrep.setupfailed assert testrep.failed assert not testrep.passed @@ -230,5 +230,5 @@ def test_crash_report(): py.test.skip("check that crash reporting works") -def test_present_tb_and_python_repr_run(): - py.test.skip("check that present_tb and python_repr_run work") +def test_pypresent_tb_and_python_repr_run(): + py.test.skip("check that pypresent_tb and python_repr_run work") From hpk at codespeak.net Wed Jul 23 17:29:25 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 17:29:25 +0200 (CEST) Subject: [py-svn] r56742 - py/release/0.9.x/py/misc Message-ID: <20080723152925.7A0E8169EDA@codespeak.net> Author: hpk Date: Wed Jul 23 17:29:24 2008 New Revision: 56742 Modified: py/release/0.9.x/py/misc/_dist.py Log: don't add to the PATH repeatedly Modified: py/release/0.9.x/py/misc/_dist.py ============================================================================== --- py/release/0.9.x/py/misc/_dist.py (original) +++ py/release/0.9.x/py/misc/_dist.py Wed Jul 23 17:29:24 2008 @@ -100,6 +100,8 @@ reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = get_registry_value(reg, key, "Path") + if bindir in path: + return path += ";" + bindir print "Setting PATH to:", path set_registry_value(reg, key, "Path", path) From hpk at codespeak.net Wed Jul 23 17:30:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 17:30:44 +0200 (CEST) Subject: [py-svn] r56743 - in py: dist/py/misc trunk/py/misc Message-ID: <20080723153044.5AC70169F4F@codespeak.net> Author: hpk Date: Wed Jul 23 17:30:43 2008 New Revision: 56743 Modified: py/dist/py/misc/_dist.py py/trunk/py/misc/_dist.py Log: port path-fix 56724 Modified: py/dist/py/misc/_dist.py ============================================================================== --- py/dist/py/misc/_dist.py (original) +++ py/dist/py/misc/_dist.py Wed Jul 23 17:30:43 2008 @@ -100,6 +100,8 @@ reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = get_registry_value(reg, key, "Path") + if bindir in path: + return path += ";" + bindir print "Setting PATH to:", path set_registry_value(reg, key, "Path", path) Modified: py/trunk/py/misc/_dist.py ============================================================================== --- py/trunk/py/misc/_dist.py (original) +++ py/trunk/py/misc/_dist.py Wed Jul 23 17:30:43 2008 @@ -100,6 +100,8 @@ reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = get_registry_value(reg, key, "Path") + if bindir in path: + return path += ";" + bindir print "Setting PATH to:", path set_registry_value(reg, key, "Path", path) From hpk at codespeak.net Wed Jul 23 17:47:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 17:47:18 +0200 (CEST) Subject: [py-svn] r56744 - py/extradoc/talk/pycon-uk-2008 Message-ID: <20080723154718.7AB4A169F85@codespeak.net> Author: hpk Date: Wed Jul 23 17:47:12 2008 New Revision: 56744 Added: py/extradoc/talk/pycon-uk-2008/ py/extradoc/talk/pycon-uk-2008/pytest.txt Log: tutorial talk draft for pycon uk 2008 Added: py/extradoc/talk/pycon-uk-2008/pytest.txt ============================================================================== --- (empty file) +++ py/extradoc/talk/pycon-uk-2008/pytest.txt Wed Jul 23 17:47:12 2008 @@ -0,0 +1,11 @@ +py.test - rapid testing with minimal effort +-------------------------------------------------------- + +This tutorial talk introduces usage of py.test, a popular tool +for writing and running automated tests. We walk through the +implementation of tests, layered setup of test state and how +to write project specific or global extensions. We discuss the +current feature set including extensions for generating HTML +pages, running tests for other languages such as Javascript +or for distributing tests across machines. + From hpk at codespeak.net Wed Jul 23 18:13:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 18:13:29 +0200 (CEST) Subject: [py-svn] r56746 - py/extradoc/talk/pycon-uk-2008 Message-ID: <20080723161329.C7323169F72@codespeak.net> Author: hpk Date: Wed Jul 23 18:13:29 2008 New Revision: 56746 Modified: py/extradoc/talk/pycon-uk-2008/pytest.txt Log: add frame info Modified: py/extradoc/talk/pycon-uk-2008/pytest.txt ============================================================================== --- py/extradoc/talk/pycon-uk-2008/pytest.txt (original) +++ py/extradoc/talk/pycon-uk-2008/pytest.txt Wed Jul 23 18:13:29 2008 @@ -9,3 +9,18 @@ pages, running tests for other languages such as Javascript or for distributing tests across machines. +level: beginner +duration: 1 hour min, 2 hour max. + +biography +------------ + +Holger Krekel is a co-founder of the PyPy project and +participates on many levels in it. He is the initiator and +maintainer of the popular py.test and a few other tools. In +2004 he founded merlinux, a company organising itself +virtually, mostly with european developers from PyPy and +py.test contexts. + +Contact: holger at merlinux.eu + From ghum at codespeak.net Wed Jul 23 18:33:21 2008 From: ghum at codespeak.net (ghum at codespeak.net) Date: Wed, 23 Jul 2008 18:33:21 +0200 (CEST) Subject: [py-svn] r56747 - py/dist/py/misc Message-ID: <20080723163321.B67C2169E3A@codespeak.net> Author: ghum Date: Wed Jul 23 18:33:19 2008 New Revision: 56747 Modified: py/dist/py/misc/_dist.py Log: _winreg.OpenKey(reg, key, 0, _winreg.KEY_WRITE) can fail if no Admin-rights, added fallback to add path to user-PATH, and another fallback to create an user-path if not allready present Modified: py/dist/py/misc/_dist.py ============================================================================== --- py/dist/py/misc/_dist.py (original) +++ py/dist/py/misc/_dist.py Wed Jul 23 18:33:19 2008 @@ -97,14 +97,48 @@ # Add py/bin to PATH environment variable bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") + + # check for the user path + ureg = _winreg.ConnectRegistry(None, _winreg.HKEY_CURRENT_USER) + ukey = r"Environment" + + # not every user has his own path on windows + try: + upath = get_registry_value(ureg, ukey, "PATH") + except WindowsError: + upath="" + # if bindir allready in userpath -> do nothing + if bindir in upath: + return + reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = get_registry_value(reg, key, "Path") + # if bindir allready in systempath -> do nothing if bindir in path: return path += ";" + bindir print "Setting PATH to:", path - set_registry_value(reg, key, "Path", path) + + pathset=False + try: + set_registry_value(reg, key, "PATH", path) + pathset=True + except WindowsError: + print "cannot set systempath, falling back to userpath" + pass + + if not pathset: + try: + if len(upath)>0: #if no user path present + upath += ";" + upath+=bindir + set_registry_value(ureg, ukey, "Path", upath) + pathset=True + except WindowsError: + print "cannot set userpath, please add %s to your path" % (bindir,) + return + #print "Current PATH is:", get_registry_value(reg, key, "Path") # Propagate changes throughout the system From hpk at codespeak.net Wed Jul 23 19:39:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 19:39:34 +0200 (CEST) Subject: [py-svn] r56748 - py/release/0.9.x/py/misc Message-ID: <20080723173934.B89CF168529@codespeak.net> Author: hpk Date: Wed Jul 23 19:39:33 2008 New Revision: 56748 Added: py/release/0.9.x/py/misc/_dist.py - copied unchanged from r56747, py/dist/py/misc/_dist.py Log: porting file from dist to 0.9.x branch From hpk at codespeak.net Wed Jul 23 19:40:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 23 Jul 2008 19:40:05 +0200 (CEST) Subject: [py-svn] r56749 - py/trunk/py/misc Message-ID: <20080723174005.5C6FB168543@codespeak.net> Author: hpk Date: Wed Jul 23 19:40:04 2008 New Revision: 56749 Added: py/trunk/py/misc/_dist.py - copied unchanged from r56748, py/dist/py/misc/_dist.py Log: porting fixes from dist to trunk From hpk at codespeak.net Thu Jul 24 14:22:48 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 24 Jul 2008 14:22:48 +0200 (CEST) Subject: [py-svn] r56769 - py/dist/py/test Message-ID: <20080724122248.90FE3169E3A@codespeak.net> Author: hpk Date: Thu Jul 24 14:22:48 2008 New Revision: 56769 Modified: py/dist/py/test/representation.py Log: a try at printing filename/lineno information parseable from editors Modified: py/dist/py/test/representation.py ============================================================================== --- py/dist/py/test/representation.py (original) +++ py/dist/py/test/representation.py Thu Jul 24 14:22:48 2008 @@ -119,7 +119,13 @@ else: self.repr_source(source, '>', marker_location) self.out.line("") - self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) + + # filename and lineno output for each entry, + # using an output format that most editors unterstand + loc = "%s:%d:" %(entry.path, entry.lineno+1) + if entry == last: + loc += " %r" % excinfo.exconly() + self.out.line(loc) self.repr_locals(entry.locals) # trailing info From hpk at codespeak.net Thu Jul 24 20:19:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 24 Jul 2008 20:19:09 +0200 (CEST) Subject: [py-svn] r56776 - py/release/0.9.x/py/test Message-ID: <20080724181909.DC5142A01B8@codespeak.net> Author: hpk Date: Thu Jul 24 20:19:07 2008 New Revision: 56776 Modified: py/release/0.9.x/py/test/representation.py Log: port 56769 (parseable tb output for editors) Modified: py/release/0.9.x/py/test/representation.py ============================================================================== --- py/release/0.9.x/py/test/representation.py (original) +++ py/release/0.9.x/py/test/representation.py Thu Jul 24 20:19:07 2008 @@ -119,7 +119,13 @@ else: self.repr_source(source, '>', marker_location) self.out.line("") - self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) + + # filename and lineno output for each entry, + # using an output format that most editors unterstand + loc = "%s:%d:" %(entry.path, entry.lineno+1) + if entry == last: + loc += " %r" % excinfo.exconly() + self.out.line(loc) self.repr_locals(entry.locals) # trailing info From hpk at codespeak.net Thu Jul 24 20:26:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 24 Jul 2008 20:26:08 +0200 (CEST) Subject: [py-svn] r56777 - in py/branch/event/py/code: . testing Message-ID: <20080724182608.A7DF22A01A7@codespeak.net> Author: hpk Date: Thu Jul 24 20:26:08 2008 New Revision: 56777 Modified: py/branch/event/py/code/tbpresent.py py/branch/event/py/code/testing/test_tbpresent.py Log: tb output adapted to be better parseable for editors. with tests. Modified: py/branch/event/py/code/tbpresent.py ============================================================================== --- py/branch/event/py/code/tbpresent.py (original) +++ py/branch/event/py/code/tbpresent.py Thu Jul 24 20:26:08 2008 @@ -90,10 +90,14 @@ if self.style == "long": self.repr_source(source, marker_location) + # filename and lineno output for each entry, + # using an output format that most editors unterstand + loc = "%s:%d:" %(entry.path, entry.lineno+1) if excinfo: self.repr_failure_explanation(excinfo, source) + loc += " %r" % excinfo.exconly() self.out.line("") - self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) + self.out.line(loc) self.repr_locals(entry.locals) else: if self.style == "short": Modified: py/branch/event/py/code/testing/test_tbpresent.py ============================================================================== --- py/branch/event/py/code/testing/test_tbpresent.py (original) +++ py/branch/event/py/code/testing/test_tbpresent.py Thu Jul 24 20:26:08 2008 @@ -77,7 +77,7 @@ assert lines[0] == " def entry():" assert lines[1] == "> func1()" assert not lines[2] - assert lines[3] == "[%s:5]" %(mod.__file__,) + assert lines[3] == "%s:5:" %(mod.__file__,) # test last entry p = self.getpresenter() @@ -89,7 +89,7 @@ assert lines[1] == '> raise ValueError("hello")' assert lines[2] == "E ValueError: hello" assert not lines[3] - assert lines[4] == "[%s:3]" %(mod.__file__,) + assert lines[4] == "%s:3: 'ValueError: hello'" %(mod.__file__,) def test_repr_tbentry_short(self): From hpk at codespeak.net Fri Jul 25 15:06:01 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 15:06:01 +0200 (CEST) Subject: [py-svn] r56784 - py/branch/event/py/test2/testing Message-ID: <20080725130601.1F9B2169DFF@codespeak.net> Author: hpk Date: Fri Jul 25 15:06:00 2008 New Revision: 56784 Modified: py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_runner.py Log: recover some help machinery for tests Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Fri Jul 25 15:06:00 2008 @@ -126,3 +126,44 @@ assert len(failevents) == 1 return failevents[0],tfile + +# XXX below some code to help with inlining examples +# as source code. + +class XXXBaseInlineCollectionTests: + + def setup_method(self, method): + self.tmpdir = tmpdir.join("%s_%s_%s" % + (__name__, self.__class__.__name__, method.__name__)) + + def getmodulecol(self, func, funcname="testfunc"): + funcname = getattr(func, '__name__', funcname) + self.tmpdir.ensure("__init__.py") + path = self.tmpdir.ensure(funcname + ".py") + path.write(py.code.Source(func)) + self.config = py.test2.config._reparse([path.dirpath()]) + modulecol = self.config._getcollector(path) + return modulecol + + def makeitem(self, source, funcname="testfunc"): + modulecol = self.getmodulecol(source) + item = modulecol.join(funcname) + assert item is not None, (item, funcname) + return item + + def getitems(self, source): + modulecol = self.getmodulecol(source) + return [modulecol.join(x) for x in modulecol.listdir()] + + def runtestfunc(self, func, funcname="testfunc"): + item = self.makeitem(func, funcname=funcname) + runner = self.getrunner() + return runner(item) + + def exampleusage(self): + testrep = self.runtestfunc(""" + def testfunc(): + pass + """) + assert testrep.passed + Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Fri Jul 25 15:06:00 2008 @@ -3,7 +3,6 @@ from py.__.test2 import runner from py.__.test2.outcome import Exit from py.__.test2 import pypresent -from py.__.test2 import mock class MockItem: def __init__(self, func): From hpk at codespeak.net Fri Jul 25 16:07:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 16:07:22 +0200 (CEST) Subject: [py-svn] r56785 - in py/branch/event/py/test2: . rep testing Message-ID: <20080725140722.9A4B7398007@codespeak.net> Author: hpk Date: Fri Jul 25 16:07:20 2008 New Revision: 56785 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_session.py Log: resolve XXX / clean start/finish of sessions and reporters with respect to event subscriptions Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 25 16:07:20 2008 @@ -5,7 +5,6 @@ class TerminalReporter(object): def __init__(self, config, out=None): self.config = config - self.config.bus.subscribe(self.consume) self.currentfspath = None self.out = py.io.TerminalWriter(sys.stdout) self._failed = [] Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Jul 25 16:07:20 2008 @@ -43,31 +43,29 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ + self.config.bus.subscribe(self.reporter.consume) self.config.bus.notify(repevent.SessionStart(self)) if not self.config.option.nomagic: py.magic.invoke(assertion=1) - self._failurelist = self._initfailurelist() + self._failurelist = [] + self.config.bus.subscribe(self._processfailures) + + def _processfailures(self, event): + if isinstance(event, repevent.ItemTestReport) and event.failed: + self._failurelist.append(event) + if self.config.option.exitfirst: + self.shouldstop = True def sessionfinishes(self): """ teardown any resources after a test run. """ - self.config._setupstate.teardown_all() + self.config._setupstate.teardown_all() # xxx this can raise exceptions! if not self.config.option.nomagic: py.magic.revoke(assertion=1) self.config.bus.notify(repevent.SessionFinish(self)) + self.config.bus.unsubscribe(self._processfailures) + self.config.bus.unsubscribe(self.reporter.consume) return self._failurelist - def _initfailurelist(self): - failurelist = [] - def processfailures(event): - if isinstance(event, repevent.ItemTestReport) and event.failed: - failurelist.append(event) - if self.config.option.exitfirst: - self.shouldstop = True - # XXX how to cleanly remove the processfailures - # subscription after it's done its work? - self.config.bus.subscribe(processfailures) - return failurelist - def main(self): """ main loop for running tests. """ self.shouldstop = False Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Fri Jul 25 16:07:20 2008 @@ -56,8 +56,17 @@ deslist = sorter.get(repevent.DeselectedTest) assert len(deslist) == 1 assert deslist[0].item.name == "test_one" + +def test_session_subscription(): + tmpdir = py.test.ensuretemp("test_session_subscription") + config = py.test2.config._reparse([tmpdir]) + old = config.bus._subscribers[:] + session = config.initsession() + session.main() + assert config.bus._subscribers == old class TestSession: + def test_terminal(self): sorter = suptest.events_run_example("filetest.py") passed, skipped, failed = sorter.countoutcomes() From hpk at codespeak.net Fri Jul 25 16:24:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 16:24:26 +0200 (CEST) Subject: [py-svn] r56786 - in py/branch/event/py/test2: . rep Message-ID: <20080725142426.A2A0539B596@codespeak.net> Author: hpk Date: Fri Jul 25 16:24:26 2008 New Revision: 56786 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py Log: * fix subscription logic * split runner into two smaller functions Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 25 16:24:26 2008 @@ -10,6 +10,7 @@ self._failed = [] self._numskipped = 0 self._numpassed = 0 + config.bus.subscribe(self.consume) def consume(self, ev): if isinstance(ev, repevent.ItemTestReport): Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Jul 25 16:24:26 2008 @@ -37,12 +37,13 @@ raise except: excinfo = py.code.ExceptionInfo() + #if pdbfunc and rep.failed: + # pdbfunc(excinfo._excinfo[2]) + return makereport(item, outcome, excinfo, outerr) - # - # make report - # - trail = item._get_collector_trail() +def makereport(item, outcome, excinfo, outerr): runnerinfo = RunnerInfo(excinfo, outerr) + trail = item._get_collector_trail() repr_path = item.repr_path() if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): outcome = "skipped" @@ -51,12 +52,7 @@ else: repr_run = pypresent.python_repr_run(item, runnerinfo, title="failure during setup/teardown") - - rep = repevent.ItemTestReport(trail, outcome, repr_run, repr_path) - if pdbfunc and rep.failed: - # xxx print rep.repr_run() ? - pdbfunc(excinfo._excinfo[2]) - return rep + return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) def fork_runner(item, setupstate, getcapture): def runforked(): Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Jul 25 16:24:26 2008 @@ -43,7 +43,6 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.config.bus.subscribe(self.reporter.consume) self.config.bus.notify(repevent.SessionStart(self)) if not self.config.option.nomagic: py.magic.invoke(assertion=1) @@ -63,7 +62,10 @@ py.magic.revoke(assertion=1) self.config.bus.notify(repevent.SessionFinish(self)) self.config.bus.unsubscribe(self._processfailures) - self.config.bus.unsubscribe(self.reporter.consume) + try: + self.config.bus.unsubscribe(self.reporter.consume) + except AttributeError: + pass return self._failurelist def main(self): From hpk at codespeak.net Fri Jul 25 17:04:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 17:04:37 +0200 (CEST) Subject: [py-svn] r56787 - in py/branch/event/py/test2: . testing Message-ID: <20080725150437.D197D2A80BE@codespeak.net> Author: hpk Date: Fri Jul 25 17:04:36 2008 New Revision: 56787 Modified: py/branch/event/py/test2/item.py py/branch/event/py/test2/pypresent.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: small refactoring to package run-related info better. Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Fri Jul 25 17:04:36 2008 @@ -69,9 +69,9 @@ """ execute the given test function. """ self.obj(*self._args) - def repr_run(self, runnerinfo): + def repr_run(self, runinfo): """ return a textual representation of run info. """ - return pypresent.python_repr_run(self, runnerinfo) + return pypresent.python_repr_run(runinfo) def repr_path(self): fspath = pypresent.getrelpath(self._config.topdir, self.fspath) Modified: py/branch/event/py/test2/pypresent.py ============================================================================== --- py/branch/event/py/test2/pypresent.py (original) +++ py/branch/event/py/test2/pypresent.py Fri Jul 25 17:04:36 2008 @@ -6,16 +6,14 @@ class PythonFunctionPresenter(TBPresenter): """ presenting information about failing Functions and Generators. """ - def __init__(self, config, item): + def __init__(self, config): self._config = config - self._item = item super(PythonFunctionPresenter, self).__init__( showlocals=config.option.showlocals, style=config.option.tbstyle, ) - def header(self, title=None): - item = self._item + def header(self, item, title=None): if title is None: modpath = getmodpath(item) title = "entrypoint: %s" % modpath @@ -29,21 +27,23 @@ self.out.line(line) - def repr_run(self, runnerinfo, title=None): - excinfo = runnerinfo.excinfo - self.header(title) + def repr_run(self, runinfo, title=None): + item = runinfo.item + excinfo = runinfo.excinfo + outerr = runinfo.outerr + self.header(item, title) if excinfo: - excinfo.traceback = self._item.prunetraceback(excinfo.traceback) + excinfo.traceback = item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) - for name, value in zip(["recorded stdout", "recorded stderr"], runnerinfo.outerr): + for name, value in zip(["recorded stdout", "recorded stderr"], outerr): if value: self.out.sep("- ", "%s, len=%d" %(name, len(value))) self.out.line(value.rstrip()) self.repr_sep("_") -def python_repr_run(item, runnerinfo, title=None): - p = PythonFunctionPresenter(item._config, item) - p.repr_run(runnerinfo, title=title) +def python_repr_run(runinfo, title=None): + p = PythonFunctionPresenter(runinfo.item._config) + p.repr_run(runinfo, title=title) return p.out.stringio.getvalue() def getmodpath(pycolitem): Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Jul 25 17:04:36 2008 @@ -10,14 +10,19 @@ import py.__.test2.custompdb from py.__.test2 import pypresent -class RunnerInfo: +class RunInfo: """ info on test runs. """ - def __init__(self, excinfo, outerr): + def __init__(self, item, outcome, excinfo, outerr): + if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): + assert outcome in ('setupfailed', 'failed') + outcome = "skipped" + self.item = item + self.outcome = outcome self.excinfo = excinfo self.outerr = outerr -def basic_runner(item, setupstate, getcapture, pdbfunc=None): - """ returns a test report after having run the given test item. """ +def basic_runner(item, setupstate, getcapture): + """ returns a RunInfo object after having run the given test items. """ capture = getcapture() excinfo = None try: @@ -37,22 +42,19 @@ raise except: excinfo = py.code.ExceptionInfo() - #if pdbfunc and rep.failed: - # pdbfunc(excinfo._excinfo[2]) - return makereport(item, outcome, excinfo, outerr) + runinfo = RunInfo(item, outcome, excinfo, outerr) + return makereport(runinfo) -def makereport(item, outcome, excinfo, outerr): - runnerinfo = RunnerInfo(excinfo, outerr) +def makereport(runinfo): + item = runinfo.item trail = item._get_collector_trail() repr_path = item.repr_path() - if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - if outcome != "setupfailed": - repr_run = item.repr_run(runnerinfo) + if runinfo.outcome != "setupfailed": + repr_run = item.repr_run(runinfo) else: - repr_run = pypresent.python_repr_run(item, runnerinfo, - title="failure during setup/teardown") - return repevent.ItemTestReport(trail, outcome, repr_run, repr_path) + repr_run = pypresent.python_repr_run(runinfo, + title="failure during setup/teardown") + return repevent.ItemTestReport(trail, runinfo.outcome, repr_run, repr_path) def fork_runner(item, setupstate, getcapture): def runforked(): Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Fri Jul 25 17:04:36 2008 @@ -14,14 +14,14 @@ def repr_path(self): return "MockItem.repr_path" - def repr_run(self, runnerinfo): - excinfo = runnerinfo.excinfo + def repr_run(self, runinfo): + excinfo = runinfo.excinfo if not excinfo: - return ("(%s)MockItem.repr_run" %(runnerinfo.outerr,)) + return ("(%s)MockItem.repr_run" %(runinfo.outerr,)) else: assert isinstance(excinfo, py.code.ExceptionInfo) return ("(%s)MockItem.repr_run: %s" %( - runnerinfo.outerr, excinfo.exconly())) + runinfo.outerr, excinfo.exconly())) def execute(self): self.func() @@ -91,7 +91,7 @@ def prepare(self, item): py.test2.skip("skip") def testfunc(): pass - def pythonreprrun(item, runnerinfo, title=None): return "" + def pythonreprrun(runinfo, title=None): return "" py.magic.patch(pypresent, "python_repr_run", pythonreprrun) try: testrep = self.run(testfunc, setupstate=MySetupState()) @@ -108,8 +108,8 @@ raise ValueError(2) def testfunc(): pass - def pythonreprrun(item, runnerinfo, title=None): - assert runnerinfo.excinfo.errisinstance(ValueError) + def pythonreprrun(runinfo, title=None): + assert runinfo.excinfo.errisinstance(ValueError) return "pythonreprrun" py.magic.patch(pypresent, "python_repr_run", pythonreprrun) try: @@ -179,8 +179,8 @@ def testfunc(): l.append(0) pass - def pythonreprrun(item, runnerinfo, title=None): - l.append(runnerinfo) + def pythonreprrun(runinfo, title=None): + l.append(runinfo) py.magic.patch(pypresent, "python_repr_run", pythonreprrun) try: testrep = self.run(testfunc, setupstate=MySetupState()) From hpk at codespeak.net Fri Jul 25 17:38:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 17:38:09 +0200 (CEST) Subject: [py-svn] r56788 - in py/branch/event/py/test2: . testing Message-ID: <20080725153809.81554498074@codespeak.net> Author: hpk Date: Fri Jul 25 17:38:09 2008 New Revision: 56788 Modified: py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: make sure that py.test.exit() works in boxed tests -This line, and those below, will be ignored-- M test2/runner.py M test2/testing/test_runner.py Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Jul 25 17:38:09 2008 @@ -57,19 +57,24 @@ return repevent.ItemTestReport(trail, runinfo.outcome, repr_run, repr_path) def fork_runner(item, setupstate, getcapture): + EXITSTATUS_TESTEXIT = 4 + def runforked(): - testrep = basic_runner(item, setupstate, getcapture) + try: + testrep = basic_runner(item, setupstate, getcapture) + except (KeyboardInterrupt, Exit): + os._exit(EXITSTATUS_TESTEXIT) return testrep.dumps() ff = py.io.ForkedFunc(runforked) result = ff.waitfinish() print vars(result) - if result.retval is not None: return repevent.ItemTestReport.fromdumps(result.retval) - #testrep.stdout = result.out - #testrep.stderr = result.err else: + if result.exitstatus == EXITSTATUS_TESTEXIT: + itemrepr = item.repr_path() + raise Exit("forked test item %s raised Exit" %(itemrepr,)) return report_crash(item, result) def report_crash(item, result): Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Fri Jul 25 17:38:09 2008 @@ -205,7 +205,6 @@ return runner.fork_runner def test_exit_does_bail_out(self): - py.test.skip("XXX needs work") def testfunc(): raise Exit() py.test.raises(Exit, "self.run(testfunc)") From hpk at codespeak.net Fri Jul 25 18:11:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 18:11:02 +0200 (CEST) Subject: [py-svn] r56789 - in py/branch/event/py/test2: . testing Message-ID: <20080725161102.AD69E169E91@codespeak.net> Author: hpk Date: Fri Jul 25 18:11:01 2008 New Revision: 56789 Modified: py/branch/event/py/test2/item.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_runner.py Log: refactor and add pdb operations to the runner Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Fri Jul 25 18:11:01 2008 @@ -1,6 +1,6 @@ import py from py.__.test2.collect import FunctionMixin, Node -from py.__.test2.runner import basic_runner, fork_runner +from py.__.test2.runner import basic_run_report, forked_run_report from py.__.test2 import pypresent _dummy = object() @@ -52,9 +52,9 @@ def _getrunner(self): if self._config.option.boxed: - return fork_runner - return basic_runner - + return forked_run_report + return basic_run_report + class Function(FunctionMixin, Item): """ a Function Item is responsible for setting up and executing a Python callable test object. Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Jul 25 18:11:01 2008 @@ -21,8 +21,8 @@ self.excinfo = excinfo self.outerr = outerr -def basic_runner(item, setupstate, getcapture): - """ returns a RunInfo object after having run the given test items. """ +def setup_and_execute(item, setupstate, getcapture): + """ returns RunInfo after setup and execution of test item. """ capture = getcapture() excinfo = None try: @@ -42,8 +42,7 @@ raise except: excinfo = py.code.ExceptionInfo() - runinfo = RunInfo(item, outcome, excinfo, outerr) - return makereport(runinfo) + return RunInfo(item, outcome, excinfo, outerr) def makereport(runinfo): item = runinfo.item @@ -56,14 +55,21 @@ title="failure during setup/teardown") return repevent.ItemTestReport(trail, runinfo.outcome, repr_run, repr_path) -def fork_runner(item, setupstate, getcapture): +def basic_run_report(item, setupstate, getcapture, pdb=None): + runinfo = setup_and_execute(item, setupstate, getcapture) + if pdb is not None and runinfo.outcome in ('failed', 'setupfailed'): + pdb(runinfo) + return makereport(runinfo) + +def forked_run_report(item, setupstate, getcapture, pdb=None): EXITSTATUS_TESTEXIT = 4 def runforked(): try: - testrep = basic_runner(item, setupstate, getcapture) + runinfo = setup_and_execute(item, setupstate, getcapture) except (KeyboardInterrupt, Exit): os._exit(EXITSTATUS_TESTEXIT) + testrep = makereport(runinfo) return testrep.dumps() ff = py.io.ForkedFunc(runforked) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Jul 25 18:11:01 2008 @@ -86,4 +86,3 @@ def runtest(self, item): runner = item._getrunner() return runner(item, item._config._setupstate, item._config._getcapture) - Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Fri Jul 25 18:11:01 2008 @@ -39,12 +39,12 @@ pass class RunnerTests: - def run(self, func, setupstate=None, getcapture=None): + def run(self, func, setupstate=None, getcapture=None, pdb=None): runner = self.getrunner() item = MockItem(func) if not setupstate: setupstate = MockSetupState() if not getcapture: getcapture = MockCapture - testrep = runner(item, setupstate, getcapture) + testrep = runner(item, setupstate, getcapture, pdb=pdb) return testrep def test_run_pass(self): @@ -145,7 +145,7 @@ class TestBasicRunner(RunnerTests): def getrunner(self): - return runner.basic_runner + return runner.basic_run_report def test_runner_handles_setupstate(self): l = [] @@ -193,8 +193,15 @@ assert l[0] == 0 assert l[1].excinfo.errisinstance(ValueError) - def test_runner_handles_pdb(self): - py.test.skip("check for proper pdb interaction") + def test_invoking_pdb(self): + l = [] + def testfunc(): + raise ValueError(7) + def mypdb(runinfo): + l.append(runinfo) + testrep = self.run(testfunc, pdb=mypdb) + assert len(l) == 1 + assert l[0].excinfo.exconly().find("ValueError: 7") != -1 class TestForkRunner(RunnerTests): def setup_class(cls): @@ -202,7 +209,7 @@ py.test.skip("need os.fork()") def getrunner(self): - return runner.fork_runner + return runner.forked_run_report def test_exit_does_bail_out(self): def testfunc(): From hpk at codespeak.net Fri Jul 25 21:45:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 21:45:12 +0200 (CEST) Subject: [py-svn] r56792 - in py/branch/event/py/test2: . rep rsession testing Message-ID: <20080725194512.EE7204980CC@codespeak.net> Author: hpk Date: Fri Jul 25 21:45:10 2008 New Revision: 56792 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/slave.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_pypresent.py py/branch/event/py/test2/testing/test_runner.py py/branch/event/py/test2/testing/test_session.py Log: * test crash reporting * normalize failed_* outcomes * re-enable test and implement pdb support Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Jul 25 21:45:10 2008 @@ -50,7 +50,7 @@ return "." elif ev.skipped: return "s" - elif ev.setupfailed: + elif ev.failed_setup: return "f" elif ev.failed: return "F" Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Fri Jul 25 21:45:10 2008 @@ -57,25 +57,10 @@ import marshal class ItemTestReport(BaseEvent): - passed = property(lambda self: self.outcome == "passed") - failed = property(lambda self: self.outcome == "failed" or self.outcome == "setupfailed") - setupfailed = property(lambda self: self.outcome == "setupfailed") - skipped = property(lambda self: self.outcome == "skipped") - - @classmethod - def fromitem(cls, item, excinfo, outerr): - trail = item._get_collector_trail() - repr_run = None - if excinfo: - if excinfo.errisinstance((Skipped, Skipped2)): - outcome = "skipped" - else: - outcome = "failed" - repr_run = item.repr_run(excinfo) - else: - outcome = "passed" - repr_path = item.repr_path() - return ItemTestReport(trail, outcome, repr_run, repr_path, outerr) + _outcomes = "passed skipped failed failed_setup failed_crashed".split() + for _ in _outcomes: + exec "%s = property(lambda self: self.outcome == %r)\n" %(_,_) + failed = property(lambda self: self.outcome.startswith("failed")) @classmethod def fromdumps(cls, string): @@ -85,7 +70,7 @@ def __init__(self, trail, outcome, repr_run, repr_path, outerr=None): self.trail = trail - assert outcome in ("passed", "failed", "skipped", "setupfailed") + assert outcome in self._outcomes self.outcome = outcome self.repr_run = repr_run self.repr_path = repr_path Modified: py/branch/event/py/test2/rsession/slave.py ============================================================================== --- py/branch/event/py/test2/rsession/slave.py (original) +++ py/branch/event/py/test2/rsession/slave.py Fri Jul 25 21:45:10 2008 @@ -5,7 +5,6 @@ import py from py.__.test2 import repevent - def slave_main(receive, send, config): while 1: itemspec = receive() Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Jul 25 21:45:10 2008 @@ -14,7 +14,7 @@ """ info on test runs. """ def __init__(self, item, outcome, excinfo, outerr): if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): - assert outcome in ('setupfailed', 'failed') + assert outcome in ('failed_setup', 'failed') outcome = "skipped" self.item = item self.outcome = outcome @@ -27,12 +27,12 @@ excinfo = None try: try: - outcome = "setupfailed" + outcome = "failed_setup" setupstate.prepare(item) try: item.execute() finally: - outcome = "setupfailed" + outcome = "failed_setup" setupstate.teardown_exact(item) outcome = "failed" outcome = "passed" @@ -48,7 +48,7 @@ item = runinfo.item trail = item._get_collector_trail() repr_path = item.repr_path() - if runinfo.outcome != "setupfailed": + if runinfo.outcome != "failed_setup": repr_run = item.repr_run(runinfo) else: repr_run = pypresent.python_repr_run(runinfo, @@ -57,7 +57,7 @@ def basic_run_report(item, setupstate, getcapture, pdb=None): runinfo = setup_and_execute(item, setupstate, getcapture) - if pdb is not None and runinfo.outcome in ('failed', 'setupfailed'): + if pdb is not None and runinfo.outcome in ('failed', 'failed_setup'): pdb(runinfo) return makereport(runinfo) @@ -84,20 +84,11 @@ return report_crash(item, result) def report_crash(item, result): - # xxx look into reusing present/tbpresent-writer tw = py.io.TerminalWriter() - tw.sep("_", "CRASHED with signal=%d: %s" % - (result.signal, pypresent.getmodpath(item))) - code = py.code.Code(item.obj) - path, firstlineno = code.path, code.firstlineno - src = py.code.Source(item.obj) - tw.line() - tw.line(str(src.indent())) - tw.line("[%s:%d]" %(path, firstlineno)) - repr_run = tw.stringio.getvalue() repr_path = item.repr_path() + tw.sep("!", "CRASHED with signal=%d: %s" % + (result.signal, repr_path)) + repr_run = tw.stringio.getvalue() trail = item._get_collector_trail() - testrep = repevent.ItemTestReport(trail, "failed", repr_run, repr_path) - #testrep.stdout = result.out - #testrep.stderr = result.err + testrep = repevent.ItemTestReport(trail, "failed_crashed", repr_run, repr_path) return testrep Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Jul 25 21:45:10 2008 @@ -8,6 +8,7 @@ import py from py.__.test2.genitem import genitems from py.__.test2 import repevent +import py.__.test2.custompdb class Session(object): """ @@ -83,6 +84,14 @@ failures = self.sessionfinishes() return failures + def runpdb(self, runinfo): + excinfo = runinfo.excinfo + py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) + def runtest(self, item): runner = item._getrunner() - return runner(item, item._config._setupstate, item._config._getcapture) + pdb = self.config.option.usepdb and self.runpdb or None + return runner(item, + setupstate=item._config._setupstate, + getcapture=item._config._getcapture, + pdb=pdb) Modified: py/branch/event/py/test2/testing/test_pypresent.py ============================================================================== --- py/branch/event/py/test2/testing/test_pypresent.py (original) +++ py/branch/event/py/test2/testing/test_pypresent.py Fri Jul 25 21:45:10 2008 @@ -29,32 +29,10 @@ assert lines[2].find("test_gen[0] -> check(0,)") != -1 assert lines[3].find("def check(x):") != -1 - def test_repr_tb_short(self): - # XXX probably a redundant test - itemtestreport,fn = suptest.getItemTestReport(""" - def f(x): - assert x - def test_f(): - f(0) - """, tb="short") - s = itemtestreport.repr_run - print s - index = -1 - basename = fn.basename - lines = s.split("\n")[2:] - for line in ( - ' File "%s", line 5, in test_f' % basename, - ' f(0)', - ' File "%s", line 3, in f' % basename, - ' assert x', - 'E assert 0' - ): - actual = lines.pop(0) - actual = actual.rstrip() - if line != actual: - print "expected:", repr(line) - print "got :", repr(actual) - assert 0 + def test_python_repr_run(self): + py.test.skip("xxx test/refactor pypresent...") + runinfo = RunInfo(item, "failed", excinfo, ("", "")) + pypresent.python_repr_run(runinfo, title="hello") def test_getmodpath_cases(): Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Fri Jul 25 21:45:10 2008 @@ -67,7 +67,7 @@ assert testrep.skipped assert not testrep.passed assert not testrep.failed - assert not testrep.setupfailed + assert not testrep.failed_setup def test_systemexit_does_not_bail_out(self): def testfunc(): @@ -77,7 +77,7 @@ except SystemExit: py.test.fail("runner did not catch SystemExit") assert testrep.failed - assert not testrep.setupfailed + assert not testrep.failed_setup assert testrep.repr_run.find("SystemExit") != -1 def test_exit_does_bail_out(self): @@ -116,7 +116,7 @@ testrep = self.run(testfunc, setupstate=MySetupState()) finally: py.magic.revert(pypresent, "python_repr_run") - assert testrep.setupfailed + assert testrep.failed_setup assert testrep.failed assert not testrep.passed assert testrep.repr_run == "pythonreprrun" @@ -186,7 +186,7 @@ testrep = self.run(testfunc, setupstate=MySetupState()) finally: py.magic.revert(pypresent, "python_repr_run") - assert testrep.setupfailed + assert testrep.failed_setup assert testrep.failed assert not testrep.passed assert len(l) == 2 # means that testfunc didnt run @@ -225,15 +225,14 @@ crashes.append((item, result)) py.magic.patch(runner, "report_crash", myreport) try: - testrep = self.run(testfunc) + self.run(testfunc) finally: py.magic.revert(runner, "report_crash") assert len(crashes) == 1 - assert crashes[0][1].signal == 15 - # assert 0 - -def test_crash_report(): - py.test.skip("check that crash reporting works") - -def test_pypresent_tb_and_python_repr_run(): - py.test.skip("check that pypresent_tb and python_repr_run work") + result = crashes[0][1] + assert result.signal == 15 + + testrep = runner.report_crash(*crashes[0]) + assert testrep.repr_run.find("CRASHED") != -1 + assert testrep.failed_crashed + assert testrep.failed Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Fri Jul 25 21:45:10 2008 @@ -215,7 +215,6 @@ assert len(failedcollections) == 2 def test_pdb_run(self): - py.test.skip("fix this test after the runner refactoring") tfile = suptest.makeuniquepyfile(""" def test_usepdb(): assert 0 From hpk at codespeak.net Fri Jul 25 22:57:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 22:57:56 +0200 (CEST) Subject: [py-svn] r56793 - py/branch/event/py/execnet Message-ID: <20080725205756.7AA9B169FDC@codespeak.net> Author: hpk Date: Fri Jul 25 22:57:54 2008 New Revision: 56793 Modified: py/branch/event/py/execnet/gateway.py Log: copy from release branch Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Fri Jul 25 22:57:54 2008 @@ -7,13 +7,13 @@ import __future__ # note that the whole code of this module (as well as some -# other modules) execute on two sides: on the side of the -# local gateway as well as on the remote side. -# On such remote sides we do not assume the py library to -# be there and InstallableGateway._remote_bootstrap_gateway() -# (located # in register.py) will take care to send require -# source fragments # to the other side. Yes, it is fragile -# but we have a # few tests that try to catch when we mess up. +# other modules) execute not only on the local side but +# also on any gateway's remote side. On such remote sides +# we cannot assume the py library to be there and +# InstallableGateway._remote_bootstrap_gateway() (located +# in register.py) will take care to send source fragments +# to the other side. Yes, it is fragile but we have a +# few tests that try to catch when we mess up. # XXX the following lines should not be here if 'ThreadOut' not in globals(): @@ -26,8 +26,8 @@ import os debug = 0 # open('/tmp/execnet-debug-%d' % os.getpid() , 'wa') +sysex = (KeyboardInterrupt, SystemExit) -sysex = (SystemExit, KeyboardInterrupt) # ---------------------------------------------------------- # cleanup machinery (for exiting processes) # ---------------------------------------------------------- @@ -105,7 +105,7 @@ for x in l: print >>debug, x debug.flush() - except sysex: + except sysex: raise except: traceback.print_exc() @@ -128,8 +128,8 @@ msg = Message.readfrom(self._io) self._trace("received <- %r" % msg) msg.received(self) - except sysex: - break # XXX? + except sysex: + break except EOFError: break except: From hpk at codespeak.net Fri Jul 25 23:56:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 25 Jul 2008 23:56:18 +0200 (CEST) Subject: [py-svn] r56794 - in py/branch/event/py/test2: rsession rsession/testing testing Message-ID: <20080725215618.1AB584980CA@codespeak.net> Author: hpk Date: Fri Jul 25 23:56:16 2008 New Revision: 56794 Modified: py/branch/event/py/test2/rsession/master.py py/branch/event/py/test2/rsession/slave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/testing/test_runner.py Log: cleanup master/slave code for dist testing a bit Modified: py/branch/event/py/test2/rsession/master.py ============================================================================== --- py/branch/event/py/test2/rsession/master.py (original) +++ py/branch/event/py/test2/rsession/master.py Fri Jul 25 23:56:16 2008 @@ -9,7 +9,7 @@ self.host = host self.config = config self.notify = config.bus.notify - self.channel = setup_slave(host, config) + self.channel = install_slave(host, config) self.channel.setcallback(self._callback) self.pending = [] @@ -37,12 +37,12 @@ raise # setting up slave code -from slave import setup defaultconftestnames = ['dist_nicelevel'] -def setup_slave(host, config): - channel = host.gw.remote_exec(str(py.code.Source(setup, "setup()"))) +from py.__.test2.rsession import slave +def install_slave(host, config): + channel = host.gw.remote_exec( + py.code.Source(slave, "setup_at_slave_side(channel)")) configrepr = config._makerepr(defaultconftestnames) - #print "sending configrepr", configrepr topdir = host.gw_remotepath if topdir is None: assert host.inplacelocal @@ -50,5 +50,3 @@ channel.send(str(topdir)) channel.send(configrepr) return channel - - Modified: py/branch/event/py/test2/rsession/slave.py ============================================================================== --- py/branch/event/py/test2/rsession/slave.py (original) +++ py/branch/event/py/test2/rsession/slave.py Fri Jul 25 23:56:16 2008 @@ -2,21 +2,12 @@ Code that is running on the slave side for executing tests remotely. """ -import py -from py.__.test2 import repevent +### +### +# XXX be careful with importing, the setup_at_slave_side +# function runs on the other side. -def slave_main(receive, send, config): - while 1: - itemspec = receive() - if itemspec is None: - send(None) - break - item = config._getcollector(itemspec) - runner = item._getrunner() - testrep = runner(item, item._config._setupstate, item._config._getcapture) - send(testrep.dumps()) - -def setup(): +def setup_at_slave_side(channel): # our current dir is the topdir import os, sys basedir = channel.receive() @@ -32,15 +23,19 @@ os.nice(nice_level) if not config.option.nomagic: py.magic.invoke(assertion=1) - from py.__.test2.rsession.slave import slave_main - - def send(msg): - channel.send(msg) - def receive(): - msg = channel.receive() - #print >>debug, "reveived", msg - return msg - slave_main(receive, send, config) + slave_main(channel.receive, channel.send, config) if not config.option.nomagic: py.magic.revoke(assertion=1) channel.close() + +def slave_main(receive, send, config): + while 1: + itemspec = receive() + if itemspec is None: + send(None) + break + item = config._getcollector(itemspec) + runner = item._getrunner() + testrep = runner(item, item._config._setupstate, item._config._getcapture) + send(testrep.dumps()) + Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Fri Jul 25 23:56:16 2008 @@ -50,5 +50,3 @@ event = queue.get(timeout=2.0) assert getattr(event, outcome) assert not node.pending - - Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Fri Jul 25 23:56:16 2008 @@ -236,3 +236,6 @@ assert testrep.repr_run.find("CRASHED") != -1 assert testrep.failed_crashed assert testrep.failed + +def test_assertion(): + py.test.skip("XXX need to test that assertion reinterp is turned on and working for py function test items. ") From hpk at codespeak.net Sat Jul 26 14:22:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 14:22:36 +0200 (CEST) Subject: [py-svn] r56796 - py/branch/event/py/test2/testing Message-ID: <20080726122236.B48CF169F76@codespeak.net> Author: hpk Date: Sat Jul 26 14:22:34 2008 New Revision: 56796 Added: py/branch/event/py/test2/testing/acceptance_test.py Log: experimenting with writing "acceptance" tests. a first failing one for the missing --collectonly reporting. Added: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/testing/acceptance_test.py Sat Jul 26 14:22:34 2008 @@ -0,0 +1,71 @@ +import py + +def setup_module(mod): + mod.modtmpdir = py.test2.ensuretemp(mod.__name__) + +class TestPyTest: + def setup_method(self, method): + name = self.__class__.__name__ + "_" + method.__name__ + self.old = modtmpdir.mkdir(name).chdir() + def teardown_method(self, method): + self.old.chdir() + + def test_collectonly_simple(self): + p = py.path.local("test_one.py") + p.write(py.code.Source(""" + def test_func1(): + pass + class TestClass: + def test_method(self): + pass + """)) + outlines, errlines = self.runpytest("--collectonly", p) + assert errlines == [''] + extra = assert_lines_contain_lines(outlines, py.code.Source(""" + + + + + + """).strip()) + + def runpytest(self, *args): + pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") + cmdargs = [py.std.sys.executable, pytestcmd] + list(args) + cmdargs = map(str, cmdargs) + capture = py.io.StdCaptureFD() + try: + popen = py.std.subprocess.Popen(cmdargs) + ret = popen.wait() + finally: + out, err = capture.reset() + if ret: + raise ExecutionFailed(ret, out, err) + return out.split("\n"), err.split("\n") + + +class ExecutionFailed(Exception): + pass + +def assert_lines_contain_lines(lines1, lines2): + """ assert that lines2 are contained (linearly) in lines1. + return a list of extralines found. + """ + if isinstance(lines2, py.code.Source): + lines2 = lines2.lines + extralines = [] + for line in lines2: + while lines1: + nextline = lines1.pop(0) + print "comparing", repr(line), repr(nextline) + if line == nextline: + print "match:", repr(line) + break + extralines.append(nextline) + else: + if line != nextline: + #__tracebackhide__ = True + raise AssertionError("expected line not found: %r" % line) + extralines.extend(lines1) + return extralines + From hpk at codespeak.net Sat Jul 26 14:24:15 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 14:24:15 +0200 (CEST) Subject: [py-svn] r56797 - py/branch/event/py/bin Message-ID: <20080726122415.2078D169FAF@codespeak.net> Author: hpk Date: Sat Jul 26 14:24:14 2008 New Revision: 56797 Modified: py/branch/event/py/bin/_findpy.py Log: don't print about inserttion to sys.path by default Modified: py/branch/event/py/bin/_findpy.py ============================================================================== --- py/branch/event/py/bin/_findpy.py (original) +++ py/branch/event/py/bin/_findpy.py Sat Jul 26 14:24:14 2008 @@ -19,7 +19,7 @@ # if p == current: # return True if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current + #print >>sys.stderr, "inserting into sys.path:", current sys.path.insert(0, current) return True current = opd(current) From hpk at codespeak.net Sat Jul 26 14:34:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 14:34:11 +0200 (CEST) Subject: [py-svn] r56798 - py/branch/event/py/test2/testing Message-ID: <20080726123411.E0BBB169FC7@codespeak.net> Author: hpk Date: Sat Jul 26 14:34:11 2008 New Revision: 56798 Modified: py/branch/event/py/test2/testing/acceptance_test.py Log: refactor machinery so that it works on win32. Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sat Jul 26 14:34:11 2008 @@ -20,7 +20,7 @@ pass """)) outlines, errlines = self.runpytest("--collectonly", p) - assert errlines == [''] + assert not "".join(errlines) extra = assert_lines_contain_lines(outlines, py.code.Source(""" @@ -33,15 +33,15 @@ pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") cmdargs = [py.std.sys.executable, pytestcmd] + list(args) cmdargs = map(str, cmdargs) - capture = py.io.StdCaptureFD() - try: - popen = py.std.subprocess.Popen(cmdargs) - ret = popen.wait() - finally: - out, err = capture.reset() + p1 = py.path.local("stdout") + p2 = py.path.local("stderr") + popen = py.std.subprocess.Popen(cmdargs, + stdout=p1.open("w"), stderr=p2.open("w")) + ret = popen.wait() + out, err = p1.readlines(cr=0), p2.readlines(cr=0) if ret: raise ExecutionFailed(ret, out, err) - return out.split("\n"), err.split("\n") + return out, err class ExecutionFailed(Exception): From hpk at codespeak.net Sat Jul 26 16:13:58 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 16:13:58 +0200 (CEST) Subject: [py-svn] r56799 - py/branch/event/py/test2 Message-ID: <20080726141358.CD7BE168545@codespeak.net> Author: hpk Date: Sat Jul 26 16:13:57 2008 New Revision: 56799 Modified: py/branch/event/py/test2/genitem.py Log: removing unused and untested parameter Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Sat Jul 26 16:13:57 2008 @@ -1,14 +1,14 @@ - import py from py.__.test2 import repevent +Item = py.test2.collect.Item +Collector = py.test2.collect.Collector -def genitems(config, colitems, keywordexpr=None, - stopitems=(py.test2.collect.Item,)): +def genitems(config, colitems, keywordexpr=None): """ yield Items from iterating over the given colitems. """ bus = config.bus while colitems: next = colitems.pop(0) - if isinstance(next, stopitems): + if isinstance(next, Item): if next._skipbykeyword(keywordexpr): bus.notify(repevent.DeselectedTest(next, keywordexpr)) if config.option.keyword_oneshot: @@ -16,7 +16,7 @@ else: yield next else: - assert isinstance(next, py.test2.collect.Collector), next + assert isinstance(next, Collector), next bus.notify(repevent.CollectionStart(next)) excinfo = None try: From hpk at codespeak.net Sat Jul 26 16:18:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 16:18:23 +0200 (CEST) Subject: [py-svn] r56800 - py/branch/event/py/doc Message-ID: <20080726141823.5EED916855B@codespeak.net> Author: hpk Date: Sat Jul 26 16:18:23 2008 New Revision: 56800 Modified: py/branch/event/py/doc/impl-test.txt Log: add XXX about docs that need writing Modified: py/branch/event/py/doc/impl-test.txt ============================================================================== --- py/branch/event/py/doc/impl-test.txt (original) +++ py/branch/event/py/doc/impl-test.txt Sat Jul 26 16:18:23 2008 @@ -233,6 +233,16 @@ which themselves create ``ReSTSyntaxTest`` and ``LinkCheckerMaker`` items. All of this instances (need to) follow the `collector API`_. +Customizing the reporting of Test Failures +-------------------------------------------- + +XXX implement Item.repr_run and Item.repr_path for your test items + +Writing new assertion methods +------------------------------------- + +XXX __tracebackhide__, and use "print" + Customizing the collection process in a module ---------------------------------------------- From hpk at codespeak.net Sat Jul 26 16:42:25 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 16:42:25 +0200 (CEST) Subject: [py-svn] r56801 - in py/branch/event/py/test2: . testing Message-ID: <20080726144225.0CB9E169F3E@codespeak.net> Author: hpk Date: Sat Jul 26 16:42:24 2008 New Revision: 56801 Modified: py/branch/event/py/test2/pypresent.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_pypresent.py Log: add a bit of testing regarding reporting for gen-tests Modified: py/branch/event/py/test2/pypresent.py ============================================================================== --- py/branch/event/py/test2/pypresent.py (original) +++ py/branch/event/py/test2/pypresent.py Sat Jul 26 16:42:24 2008 @@ -13,25 +13,27 @@ style=config.option.tbstyle, ) - def header(self, item, title=None): + def header(self, runinfo, title=None): + item = runinfo.item if title is None: - modpath = getmodpath(item) + modpath = item.repr_path()[1] title = "entrypoint: %s" % modpath self.out.sep("_", title) self.out.line("") - if item.name[-1] == "]": # print extra info for generated tests - if not self._config.option.fulltrace: + + if isinstance(item, py.test2.collect.Function) and item.name[-1] == "]": + if runinfo.outcome == "failed" and not self._config.option.fulltrace: + # we are the header of a failed generated function args = self._saferepr(item._args) line = "%s%s -> %s%s" %(item.parent.name, item.name, item.obj.__name__, args) self.out.line(line) - def repr_run(self, runinfo, title=None): item = runinfo.item excinfo = runinfo.excinfo outerr = runinfo.outerr - self.header(item, title) + self.header(runinfo, title) if excinfo: excinfo.traceback = item.prunetraceback(excinfo.traceback) self.repr_tb(excinfo) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sat Jul 26 16:42:24 2008 @@ -1,4 +1,5 @@ import py +from suptest import assert_lines_contain_lines def setup_module(mod): mod.modtmpdir = py.test2.ensuretemp(mod.__name__) @@ -47,25 +48,3 @@ class ExecutionFailed(Exception): pass -def assert_lines_contain_lines(lines1, lines2): - """ assert that lines2 are contained (linearly) in lines1. - return a list of extralines found. - """ - if isinstance(lines2, py.code.Source): - lines2 = lines2.lines - extralines = [] - for line in lines2: - while lines1: - nextline = lines1.pop(0) - print "comparing", repr(line), repr(nextline) - if line == nextline: - print "match:", repr(line) - break - extralines.append(nextline) - else: - if line != nextline: - #__tracebackhide__ = True - raise AssertionError("expected line not found: %r" % line) - extralines.extend(lines1) - return extralines - Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Sat Jul 26 16:42:24 2008 @@ -126,6 +126,30 @@ assert len(failevents) == 1 return failevents[0],tfile +def assert_lines_contain_lines(lines1, lines2): + """ assert that lines2 are contained (linearly) in lines1. + return a list of extralines found. + """ + if isinstance(lines2, py.code.Source): + lines2 = lines2.lines + extralines = [] + lines1 = lines1[:] + for line in lines2: + while lines1: + nextline = lines1.pop(0) + print "comparing", repr(line), repr(nextline) + if line == nextline: + print "match:", repr(line) + break + extralines.append(nextline) + else: + if line != nextline: + #__tracebackhide__ = True + raise AssertionError("expected line not found: %r" % line) + extralines.extend(lines1) + return extralines + + # XXX below some code to help with inlining examples # as source code. Modified: py/branch/event/py/test2/testing/test_pypresent.py ============================================================================== --- py/branch/event/py/test2/testing/test_pypresent.py (original) +++ py/branch/event/py/test2/testing/test_pypresent.py Sat Jul 26 16:42:24 2008 @@ -4,6 +4,7 @@ import suptest, setupdata import sys + class TestPresenter: def setup_class(cls): cls.tmpdir = py.test2.ensuretemp(cls.__name__) @@ -25,6 +26,12 @@ s = itemtestreport.repr_run print s lines = s.split("\n") + + suptest.assert_lines_contain_lines(lines, py.code.Source(""" + test_gen[0] -> check(0,) + def check(x): + """).strip()) + assert lines[0].find("test_0.test_0.test_gen[0]") != -1 assert lines[2].find("test_gen[0] -> check(0,)") != -1 assert lines[3].find("def check(x):") != -1 From hpk at codespeak.net Sat Jul 26 17:53:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 26 Jul 2008 17:53:23 +0200 (CEST) Subject: [py-svn] r56802 - in py/branch/event/py/test2/rep: . testing Message-ID: <20080726155323.AD8602A8006@codespeak.net> Author: hpk Date: Sat Jul 26 17:53:21 2008 New Revision: 56802 Added: py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/testing/test_basereporter.py Log: first draft of a new slim basereporter with tests Added: py/branch/event/py/test2/rep/base.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rep/base.py Sat Jul 26 17:53:21 2008 @@ -0,0 +1,44 @@ +from py.__.test2 import repevent + +class BaseReporter(object): + def __init__(self): + self._outcome2rep = {} + + def activate(self, session): + session.bus.subscribe(self.processevent) + def deactivate(self, session): + session.bus.unsubscribe(self.processevent) + + def processevent(self, ev): + evname = ev.__class__.__name__ + repmethod = getattr(self, "rep_%s" % evname, None) + if repmethod is None: + self.rep(ev) + else: + repmethod(ev) + + def rep(self, ev): + pass + + def rep_ItemTestReport(self, ev): + outcome = ev.outcome + l = self._outcome2rep.setdefault(outcome, []) + l.append(ev) + + def getreports(self, outcome=None, category=None): + """ return a list with all reports matching + the given outcome or (if outcome is None) + the category. + """ + if outcome is not None: + if outcome not in repevent.ItemTestReport._outcomes: + raise ValueError("not a valid outcome %r" %(outcome)) + l = self._outcome2rep.setdefault(outcome, []) + return l[:] + if category is not None: + if category == "failed": + l = self.getreports("failed") + l += self.getreports("failed_setup") + l += self.getreports("failed_crashed") + return l + return self.getreports(category) Added: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Sat Jul 26 17:53:21 2008 @@ -0,0 +1,92 @@ +import py +from py.__.test2.rep.base import BaseReporter +from py.__.test2.eventbus import EventBus +from py.__.test2 import repevent + +class MockSession: + def __init__(self): + self.bus = EventBus() + +class TestBaseReporter: + def test_activate(self): + session = MockSession() + rep = BaseReporter() + rep.activate(session) + assert session.bus._subscribers + assert rep.processevent in session.bus._subscribers + rep.deactivate(session) + assert not session.bus._subscribers + + def test_dispatch_to_matching_method(self): + l = [] + class MyReporter(BaseReporter): + def rep_SessionStart(self, ev): + l.append(ev) + rep = MyReporter() + ev = repevent.SessionStart(None) + rep.processevent(ev) + assert len(l) == 1 + assert l[0] is ev + + def test_dispatch_to_default(self): + l = [] + class MyReporter(BaseReporter): + def rep(self, ev): + l.append(ev) + rep = MyReporter() + ev = repevent.SessionStart(None) + rep.processevent(ev) + assert len(l) == 1 + assert l[0] is ev + + def test_default_TestItemReport_one(self): + rep = BaseReporter() + ev = repevent.ItemTestReport(None, "failed", None, None) + rep.processevent(ev) + assert rep.getreports("failed") == [ev] + + def test_default_TestItemReport_all_outcomes(self): + rep = BaseReporter() + + for outcome in repevent.ItemTestReport._outcomes: + ev = repevent.ItemTestReport(None, outcome, None, None) + rep.processevent(ev) + rep.processevent(ev) + for outcome in repevent.ItemTestReport._outcomes: + l = rep.getreports(outcome) + assert len(l) == 2 + assert l[0] is l[1] + # ensure that we got a copy, not an internal list + l.pop() + l = rep.getreports(outcome) + assert len(l) == 2 + + def test_TestItemReport_getreports_unknown_outcome(self): + rep = BaseReporter() + py.test.raises(ValueError, "rep.getreports('qwe')") + + def test_TestItemReport_getreports(self): + rep = BaseReporter() + ev1 = repevent.ItemTestReport(None, "failed_setup", None, None) + rep.processevent(ev1) + ev2 = repevent.ItemTestReport(None, "failed_crashed", None, None) + rep.processevent(ev2) + ev3 = repevent.ItemTestReport(None, "passed", None, None) + rep.processevent(ev3) + ev4 = repevent.ItemTestReport(None, "failed", None, None) + rep.processevent(ev4) + ev5 = repevent.ItemTestReport(None, "skipped", None, None) + rep.processevent(ev5) + l = rep.getreports(category="failed") + assert len(l) == 3 + assert ev1 in l + assert ev2 in l + assert ev4 in l + l = rep.getreports(category="passed") + assert l == [ev3] + l = rep.getreports("passed") + assert l == [ev3] + l = rep.getreports(category="skipped") + assert l == [ev5] + l = rep.getreports("skipped") + assert l == [ev5] From hpk at codespeak.net Mon Jul 28 11:08:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 11:08:04 +0200 (CEST) Subject: [py-svn] r56810 - in py/branch/event/py: . io io/testing Message-ID: <20080728090804.E313C2A8089@codespeak.net> Author: hpk Date: Mon Jul 28 11:08:02 2008 New Revision: 56810 Added: py/branch/event/py/io/forkedfunc.py - copied, changed from r56729, py/branch/event/py/io/box.py py/branch/event/py/io/testing/test_forkedfunc.py - copied, changed from r56729, py/branch/event/py/io/testing/test_boxing.py Removed: py/branch/event/py/io/box.py py/branch/event/py/io/testing/test_boxing.py Modified: py/branch/event/py/__init__.py Log: rename + ref exit code Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Mon Jul 28 11:08:02 2008 @@ -67,6 +67,7 @@ 'test2.collect.Item' : ('./test2/item.py', 'Item'), 'test.collect.Function' : ('./test/item.py', 'Function'), 'test2.collect.Function' : ('./test2/item.py', 'Function'), + 'test2.collect.GeneratedFunction' : ('./test2/item.py', 'GeneratedFunction'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), @@ -132,7 +133,7 @@ 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), 'io.StdCaptureFD' : ('./io/stdcapture.py', 'StdCaptureFD'), 'io.TerminalWriter' : ('./io/terminalwriter.py', 'TerminalWriter'), - 'io.ForkedFunc' : ('./io/box.py', 'ForkedFunc'), + 'io.ForkedFunc' : ('./io/forkedfunc.py', 'ForkedFunc'), # error module, defining all errno's as Classes 'error' : ('./misc/error.py', 'error'), Deleted: /py/branch/event/py/io/box.py ============================================================================== --- /py/branch/event/py/io/box.py Mon Jul 28 11:08:02 2008 +++ (empty file) @@ -1,107 +0,0 @@ - -""" - ForkedFunc provides a way to run a function in a forked process - and get at its return value, stdout and stderr output as well - as signals and exitstatusus. - - XXX see if tempdir handling is sane -""" - -import py -import os -import sys -import marshal - -class ForkedFunc(object): - def __init__(self, fun, args=None, kwargs=None, nice_level=0): - if args is None: - args = [] - if kwargs is None: - kwargs = {} - self.fun = fun - self.args = args - self.kwargs = kwargs - self.tempdir = tempdir = py.path.local.mkdtemp() - self.RETVAL = tempdir.join('retval') - self.STDOUT = tempdir.join('stdout') - self.STDERR = tempdir.join('stderr') - - pid = os.fork() - if pid: # in parent process - self.pid = pid - else: # in child process - self._child(nice_level) - - def _child(self, nice_level): - # right now we need to call a function, but first we need to - # map all IO that might happen - # make sure sys.stdout points to file descriptor one - sys.stdout = stdout = self.STDOUT.open('w') - sys.stdout.flush() - fdstdout = stdout.fileno() - if fdstdout != 1: - os.dup2(fdstdout, 1) - sys.stderr = stderr = self.STDERR.open('w') - fdstderr = stderr.fileno() - if fdstderr != 2: - os.dup2(fdstderr, 2) - retvalf = self.RETVAL.open("w") - EXITSTATUS = 0 - try: - if nice_level: - os.nice(nice_level) - try: - retval = self.fun(*self.args, **self.kwargs) - retvalf.write(marshal.dumps(retval)) - except: - excinfo = py.code.ExceptionInfo() - stderr.write(excinfo.exconly()) - EXITSTATUS = 3 - finally: - stdout.close() - stderr.close() - retvalf.close() - os.close(1) - os.close(2) - os._exit(EXITSTATUS) - - def waitfinish(self, waiter=os.waitpid): - pid, systemstatus = waiter(self.pid, 0) - if systemstatus: - if os.WIFSIGNALED(systemstatus): - exitstatus = os.WTERMSIG(systemstatus) + 128 - else: - exitstatus = os.WEXITSTATUS(systemstatus) - #raise ExecutionFailed(status, systemstatus, cmd, - # ''.join(out), ''.join(err)) - else: - exitstatus = 0 - signal = systemstatus & 0x7f - if not exitstatus and not signal: - retval = self.RETVAL.open() - try: - retval_data = retval.read() - finally: - retval.close() - retval = marshal.loads(retval_data) - else: - retval = None - stdout = self.STDOUT.read() - stderr = self.STDERR.read() - self._removetemp() - return Result(exitstatus, signal, retval, stdout, stderr) - - def _removetemp(self): - if self.tempdir.check(): - self.tempdir.remove() - - def __del__(self): - self._removetemp() - -class Result(object): - def __init__(self, exitstatus, signal, retval, stdout, stderr): - self.exitstatus = exitstatus - self.signal = signal - self.retval = retval - self.out = stdout - self.err = stderr Copied: py/branch/event/py/io/forkedfunc.py (from r56729, py/branch/event/py/io/box.py) ============================================================================== --- py/branch/event/py/io/box.py (original) +++ py/branch/event/py/io/forkedfunc.py Mon Jul 28 11:08:02 2008 @@ -13,6 +13,7 @@ import marshal class ForkedFunc(object): + EXITSTATUS_EXCEPTION = 3 def __init__(self, fun, args=None, kwargs=None, nice_level=0): if args is None: args = [] @@ -56,7 +57,7 @@ except: excinfo = py.code.ExceptionInfo() stderr.write(excinfo.exconly()) - EXITSTATUS = 3 + EXITSTATUS = self.EXITSTATUS_EXCEPTION finally: stdout.close() stderr.close() Deleted: /py/branch/event/py/io/testing/test_boxing.py ============================================================================== --- /py/branch/event/py/io/testing/test_boxing.py Mon Jul 28 11:08:02 2008 +++ (empty file) @@ -1,137 +0,0 @@ -import py, sys, os - -def setup_module(mod): - if not hasattr(os, 'fork'): - py.test.skip("forkedfunc requires os.fork") - mod.tmpdir = py.test2.ensuretemp(mod.__file__) - -def test_waitfinish_removes_tempdir(): - ff = py.io.ForkedFunc(boxf1) - assert ff.tempdir.check() - ff.waitfinish() - assert not ff.tempdir.check() - -def test_tempdir_gets_gc_collected(): - ff = py.io.ForkedFunc(boxf1) - assert ff.tempdir.check() - ff.__del__() - assert not ff.tempdir.check() - os.waitpid(ff.pid, 0) - -def test_basic_forkedfunc(): - result = py.io.ForkedFunc(boxf1).waitfinish() - assert result.out == "some out\n" - assert result.err == "some err\n" - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 1 - -def test_exitstatus(): - def func(): - os._exit(3) - result = py.io.ForkedFunc(func).waitfinish() - assert result.exitstatus == 3 - assert result.signal == 0 - assert not result.out - assert not result.err - -def test_execption_in_func(): - def fun(): - raise ValueError(42) - result = py.io.ForkedFunc(fun).waitfinish() - assert result.exitstatus == 3 - assert result.err.find("ValueError: 42") != -1 - assert result.signal == 0 - assert not result.retval - -def test_forkedfunc_on_fds(): - result = py.io.ForkedFunc(boxf2).waitfinish() - assert result.out == "someout" - assert result.err == "someerr" - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 2 - -def test_forkedfunc_signal(): - result = py.io.ForkedFunc(boxseg).waitfinish() - assert result.retval is None - if py.std.sys.version_info < (2,4): - py.test.skip("signal detection does not work with python prior 2.4") - assert result.signal == 11 - -def test_forkedfunc_huge_data(): - result = py.io.ForkedFunc(boxhuge).waitfinish() - assert result.out - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 3 - -def test_box_seq(): - # we run many boxes with huge data, just one after another - for i in xrange(50): - result = py.io.ForkedFunc(boxhuge).waitfinish() - assert result.out - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 3 - -def test_box_in_a_box(): - def boxfun(): - result = py.io.ForkedFunc(boxf2).waitfinish() - print result.out - print >>sys.stderr, result.err - return result.retval - - result = py.io.ForkedFunc(boxfun).waitfinish() - assert result.out == "someout\n" - assert result.err == "someerr\n" - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 2 - -def test_kill_func_forked(): - class A: - pass - info = A() - import time - - def box_fun(): - time.sleep(10) # we don't want to last forever here - - ff = py.io.ForkedFunc(box_fun) - os.kill(ff.pid, 15) - result = ff.waitfinish() - if py.std.sys.version_info < (2,4): - py.test.skip("signal detection does not work with python prior 2.4") - assert result.signal == 15 - - - -# ====================================================================== -# examples -# ====================================================================== -# - -def boxf1(): - print "some out" - print >>sys.stderr, "some err" - return 1 - -def boxf2(): - os.write(1, "someout") - os.write(2, "someerr") - return 2 - -def boxseg(): - os.kill(os.getpid(), 11) - -def boxhuge(): - os.write(1, " " * 10000) - os.write(2, " " * 10000) - os.write(1, " " * 10000) - - os.write(1, " " * 10000) - os.write(2, " " * 10000) - os.write(2, " " * 10000) - os.write(1, " " * 10000) - return 3 Copied: py/branch/event/py/io/testing/test_forkedfunc.py (from r56729, py/branch/event/py/io/testing/test_boxing.py) ============================================================================== --- py/branch/event/py/io/testing/test_boxing.py (original) +++ py/branch/event/py/io/testing/test_forkedfunc.py Mon Jul 28 11:08:02 2008 @@ -28,9 +28,9 @@ def test_exitstatus(): def func(): - os._exit(3) + os._exit(4) result = py.io.ForkedFunc(func).waitfinish() - assert result.exitstatus == 3 + assert result.exitstatus == 4 assert result.signal == 0 assert not result.out assert not result.err @@ -38,8 +38,9 @@ def test_execption_in_func(): def fun(): raise ValueError(42) - result = py.io.ForkedFunc(fun).waitfinish() - assert result.exitstatus == 3 + ff = py.io.ForkedFunc(fun) + result = ff.waitfinish() + assert result.exitstatus == ff.EXITSTATUS_EXCEPTION assert result.err.find("ValueError: 42") != -1 assert result.signal == 0 assert not result.retval From hpk at codespeak.net Mon Jul 28 15:42:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 15:42:00 +0200 (CEST) Subject: [py-svn] r56812 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080728134200.3C7442A8087@codespeak.net> Author: hpk Date: Mon Jul 28 15:41:59 2008 New Revision: 56812 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/suptest.py Log: implementing CollectOnly reporting + some (test and otherwise) refactoring Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Jul 28 15:41:59 2008 @@ -142,14 +142,8 @@ cls = self._getsessionclass() session = cls(self) session.fixoptions() - reporter = self._getreporterclass() - session.reporter = reporter(self) return session - def _getreporterclass(self): - from py.__.test2.rep.terminal import TerminalReporter - return TerminalReporter - def _getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Mon Jul 28 15:41:59 2008 @@ -4,10 +4,10 @@ def __init__(self): self._outcome2rep = {} - def activate(self, session): - session.bus.subscribe(self.processevent) - def deactivate(self, session): - session.bus.unsubscribe(self.processevent) + def activate(self, bus): + bus.subscribe(self.processevent) + def deactivate(self, bus): + bus.unsubscribe(self.processevent) def processevent(self, ev): evname = ev.__class__.__name__ Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Mon Jul 28 15:41:59 2008 @@ -1,18 +1,18 @@ import py import sys from py.__.test2 import repevent +from py.__.test2.rep.base import BaseReporter +from py.__.test2.outcome import Skipped as Skipped2 +from py.__.test.outcome import Skipped -class TerminalReporter(object): +class TerminalReporter(BaseReporter): def __init__(self, config, out=None): + super(TerminalReporter, self).__init__() self.config = config self.currentfspath = None self.out = py.io.TerminalWriter(sys.stdout) - self._failed = [] - self._numskipped = 0 - self._numpassed = 0 - config.bus.subscribe(self.consume) - - def consume(self, ev): + + def processevent(self, ev): if isinstance(ev, repevent.ItemTestReport): fspath, modpath = ev.repr_path if fspath != self.currentfspath: @@ -20,24 +20,15 @@ self.out.write(fspath + " ") self.currentfspath = fspath self.out.write(self._getletter(ev)) - self.record(ev) elif isinstance(ev, repevent.SessionFinish): self.out.line() - self.print_failures() - self.out.sep("=", "%d tests of which %d failed, %d skipped" %( - len(self._failed) + self._numskipped + self._numpassed, - len(self._failed), self._numskipped)) + #self.print_failures() + #self.out.sep("=", "%d tests of which %d failed, %d skipped" %( + # len(self._failed) + self._numskipped + self._numpassed, + # len(self._failed), self._numskipped)) elif isinstance(ev, repevent.SessionStart): self.out.sep("=", "session starts") - def record(self, ev): - if ev.failed: - self._failed.append(ev) - elif ev.skipped: - self._numskipped += 1 - else: - self._numpassed += 1 - def print_failures(self): if not self._failed: return @@ -52,5 +43,37 @@ return "s" elif ev.failed_setup: return "f" + elif ev.failed_crashed: + return "C" elif ev.failed: return "F" + +class CollectonlyReporter(BaseReporter): + INDENT = " " + + def __init__(self, config, out): + super(CollectonlyReporter, self).__init__() + self.config = config + self.out = py.io.TerminalWriter(out) + self.indent = "" + + def rep_CollectionStart(self, ev): + self.out.line(str(ev.collector)) + self.indent += self.INDENT + + def rep_ItemStart(self, event): + self.out.line(self.indent + str(event.item)) + + def rep_CollectionFinish(self, ev): + excinfo = ev.excinfo + if excinfo is not None: + self.out.line(self.indent + "!!! %s !!!" % excinfo.exconly()) + self.indent = self.indent[:-len(self.INDENT)] + + #def rep_DeselectedTest(self, event): + # self.out.line(" " * self.indent + "- skipped -") + + #def summary(self): + # self.out.sep("=", "Total time: %.1f" % (self.timeend - self.timestart)) + + Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Mon Jul 28 15:41:59 2008 @@ -3,19 +3,15 @@ from py.__.test2.eventbus import EventBus from py.__.test2 import repevent -class MockSession: - def __init__(self): - self.bus = EventBus() - class TestBaseReporter: def test_activate(self): - session = MockSession() + bus = EventBus() rep = BaseReporter() - rep.activate(session) - assert session.bus._subscribers - assert rep.processevent in session.bus._subscribers - rep.deactivate(session) - assert not session.bus._subscribers + rep.activate(bus) + assert bus._subscribers + assert rep.processevent in bus._subscribers + rep.deactivate(bus) + assert not bus._subscribers def test_dispatch_to_matching_method(self): l = [] Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Mon Jul 28 15:41:59 2008 @@ -1,35 +1,96 @@ import py -from py.__.test2.rep.terminal import TerminalReporter +from py.__.test2.rep.terminal import TerminalReporter, CollectonlyReporter from py.__.test2 import repevent +from py.__.test2.testing import suptest +from py.__.test2.genitem import genitems -class TestInit: - def test_is_default(self): - config = py.test2.config._reparse(['xxx']) - assert config._getreporterclass() is TerminalReporter +def setup_module(mod): + mod.tmpdir = py.test.ensuretemp(mod.__name__) - def test_session_has_reporter(self): - config = py.test2.config._reparse(['xxx']) - session = config.initsession() - assert isinstance(session.reporter, TerminalReporter) - assert session.reporter.config is config +class InlineCollect: + def setup_class(cls): + cls.clstmpdir = tmpdir.join(cls.__name__) + + def setup_method(self, method): + self.tmpdir = self.clstmpdir.join(method.__name__) - def test_reporter_is_subscribed(self): - config = py.test2.config._reparse(['xxx']) - session = config.initsession() - rep = session.reporter - for subscriber in config.bus._subscribers: - if subscriber == rep.consume: - break - else: - assert 0, "not subscribed" + def getmodulecol(self, configargs, source): + self.tmpdir.ensure("__init__.py") + path = self.tmpdir.ensure(self.tmpdir.purebasename + ".py") + path.write(py.code.Source(source)) + config = py.test2.config._reparse([path] + configargs) + return config._getcollector(path) -class TestEvents: - def test_session_starts(self): +class TestTerminal: + def test_session_reporter_subscription(self): config = py.test2.config._reparse(['xxx']) session = config.initsession() + session.sessionstarts() rep = session.reporter - rep.consume(repevent.SessionStart(session)) - - py.test.skip("xxx") - - + assert isinstance(rep, TerminalReporter) + assert rep.processevent in config.bus._subscribers + session.sessionfinishes() + assert rep.processevent not in config.bus._subscribers + +class TestCollectonly(InlineCollect): + def test_collectonly(self): + modcol = self.getmodulecol(['--collectonly'], """ + def test_func(): + pass + """) + stringio = py.std.cStringIO.StringIO() + rep = CollectonlyReporter(modcol._config, out=stringio) + indent = rep.indent + rep.processevent(repevent.CollectionStart(modcol)) + s = popvalue(stringio) + assert s == "" + + item = modcol.join("test_func") + rep.processevent(repevent.ItemStart(item)) + s = popvalue(stringio) + assert s == " " + + rep.processevent(repevent.CollectionFinish(modcol)) + assert rep.indent == indent + + def test_collectonly_skipped_module(self): + modcol = self.getmodulecol(['--collectonly'], """ + import py + py.test2.skip("nomod") + """) + stringio = py.std.cStringIO.StringIO() + rep = CollectonlyReporter(modcol._config, out=stringio) + rep.activate(modcol._config.bus) + cols = list(genitems(modcol._config, [modcol])) + assert len(cols) == 0 + stringio.seek(0) + lines = stringio.readlines() + lines = map(str.rstrip, lines) + print lines + suptest.assert_lines_contain_lines(lines, """ + + !!! Skipped: 'nomod' !!! + """) + + def test_collectonly_failed_module(self): + modcol = self.getmodulecol(['--collectonly'], """ + raise ValueError(0) + """) + stringio = py.std.cStringIO.StringIO() + rep = CollectonlyReporter(modcol._config, out=stringio) + rep.activate(modcol._config.bus) + cols = list(genitems(modcol._config, [modcol])) + assert len(cols) == 0 + stringio.seek(0) + lines = stringio.readlines() + lines = map(str.rstrip, lines) + print lines + suptest.assert_lines_contain_lines(lines, """ + + !!! ValueError: 0 !!! + """) + +def popvalue(stringio): + value = stringio.getvalue().rstrip() + stringio.truncate(0) + return value Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Jul 28 15:41:59 2008 @@ -44,6 +44,10 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ + from py.__.test2.rep.terminal import TerminalReporter + rep = TerminalReporter(self) + rep.activate(self.config.bus) + self.reporter = rep self.config.bus.notify(repevent.SessionStart(self)) if not self.config.option.nomagic: py.magic.invoke(assertion=1) @@ -63,10 +67,7 @@ py.magic.revoke(assertion=1) self.config.bus.notify(repevent.SessionFinish(self)) self.config.bus.unsubscribe(self._processfailures) - try: - self.config.bus.unsubscribe(self.reporter.consume) - except AttributeError: - pass + self.reporter.deactivate(self.config.bus) return self._failurelist def main(self): Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Mon Jul 28 15:41:59 2008 @@ -130,10 +130,14 @@ """ assert that lines2 are contained (linearly) in lines1. return a list of extralines found. """ + if isinstance(lines2, str): + lines2 = py.code.Source(lines2) if isinstance(lines2, py.code.Source): - lines2 = lines2.lines + lines2 = lines2.strip().lines + extralines = [] lines1 = lines1[:] + nextline = None for line in lines2: while lines1: nextline = lines1.pop(0) @@ -148,14 +152,11 @@ raise AssertionError("expected line not found: %r" % line) extralines.extend(lines1) return extralines - - # XXX below some code to help with inlining examples # as source code. -class XXXBaseInlineCollectionTests: - +class TestBaseInlineCollection: def setup_method(self, method): self.tmpdir = tmpdir.join("%s_%s_%s" % (__name__, self.__class__.__name__, method.__name__)) From hpk at codespeak.net Mon Jul 28 15:46:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 15:46:52 +0200 (CEST) Subject: [py-svn] r56813 - in py/branch/event/py/test2: . rep/testing testing Message-ID: <20080728134652.47EB02A8087@codespeak.net> Author: hpk Date: Mon Jul 28 15:46:50 2008 New Revision: 56813 Modified: py/branch/event/py/test2/genitem.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_collect.py Log: remove config arg from genitems helper Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Mon Jul 28 15:46:50 2008 @@ -3,15 +3,15 @@ Item = py.test2.collect.Item Collector = py.test2.collect.Collector -def genitems(config, colitems, keywordexpr=None): +def genitems(colitems, keywordexpr=None): """ yield Items from iterating over the given colitems. """ - bus = config.bus while colitems: next = colitems.pop(0) + bus = next._config.bus if isinstance(next, Item): if next._skipbykeyword(keywordexpr): bus.notify(repevent.DeselectedTest(next, keywordexpr)) - if config.option.keyword_oneshot: + if next._config.option.keyword_oneshot: keywordexpr = None else: yield next @@ -21,7 +21,7 @@ excinfo = None try: cols = [next.join(x) for x in next.listdir()] - for x in genitems(config, cols, keywordexpr): + for x in genitems(cols, keywordexpr): yield x except py.builtin.sysex: raise Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Mon Jul 28 15:46:50 2008 @@ -61,7 +61,7 @@ stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) rep.activate(modcol._config.bus) - cols = list(genitems(modcol._config, [modcol])) + cols = list(genitems([modcol])) assert len(cols) == 0 stringio.seek(0) lines = stringio.readlines() @@ -79,7 +79,7 @@ stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) rep.activate(modcol._config.bus) - cols = list(genitems(modcol._config, [modcol])) + cols = list(genitems([modcol])) assert len(cols) == 0 stringio.seek(0) lines = stringio.readlines() Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Jul 28 15:46:50 2008 @@ -39,7 +39,7 @@ def collect(self): colitems = self.config.getcolitems() keyword = self.config.option.keyword - for x in genitems(self.config, colitems, keyword): + for x in genitems(colitems, keyword): yield x def sessionstarts(self): Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Mon Jul 28 15:46:50 2008 @@ -274,7 +274,7 @@ print "using tempdir", tmp config = py.test2.config._reparse([tmp]) l = suptest.eventappender(config) - items = list(genitems(config, config.getcolitems())) + items = list(genitems(config.getcolitems())) return items, l def test_check_collect_hashes(self): From hpk at codespeak.net Mon Jul 28 16:02:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 16:02:04 +0200 (CEST) Subject: [py-svn] r56814 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080728140204.B3F1F39B5AB@codespeak.net> Author: hpk Date: Mon Jul 28 16:02:03 2008 New Revision: 56814 Added: py/branch/event/py/test2/rep/testing/test_collectonly.py - copied, changed from r56813, py/branch/event/py/test2/rep/testing/test_terminal.py Modified: py/branch/event/py/test2/genitem.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/reporter.py py/branch/event/py/test2/rep/rest.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rep/web.py py/branch/event/py/test2/rep/webjs.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/test_session.py Log: * rename event DeselectedTest -> DeselectedItem * add DeselectedItem support to collectonly * remove some unneeded events Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Mon Jul 28 16:02:03 2008 @@ -10,7 +10,7 @@ bus = next._config.bus if isinstance(next, Item): if next._skipbykeyword(keywordexpr): - bus.notify(repevent.DeselectedTest(next, keywordexpr)) + bus.notify(repevent.DeselectedItem(next, keywordexpr)) if next._config.option.keyword_oneshot: keywordexpr = None else: Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Mon Jul 28 16:02:03 2008 @@ -24,7 +24,7 @@ def report_CollectionFailed(self, event): self.out.line(" " * self.indent + "- FAILED TO LOAD MODULE -") - def report_DeselectedTest(self, event): + def report_DeselectedItem(self, event): self.out.line(" " * self.indent + "- skipped -") def summary(self): Modified: py/branch/event/py/test2/rep/reporter.py ============================================================================== --- py/branch/event/py/test2/rep/reporter.py (original) +++ py/branch/event/py/test2/rep/reporter.py Mon Jul 28 16:02:03 2008 @@ -239,7 +239,7 @@ outcome = event.outcome text = outcome.skipped.value itemname = repr(outcome.skipped.traceback[-2]).split("\n")[0] - elif isinstance(event, repevent.DeselectedTest): + elif isinstance(event, repevent.DeselectedItem): text = str(event.excinfo.value) itemname = "/".join(colitem.listnames()) if text not in texts: @@ -285,7 +285,7 @@ (total, skipped_str, failed_str, self.timeend - self.timestart, self.timersync - self.timestart)) - def report_DeselectedTest(self, event): + def report_DeselectedItem(self, event): #event.outcome.excinfo.source = self.skipped_tests_outcome.append(event) @@ -345,7 +345,7 @@ # argh! bad hack, need to fix it self.failed[self.hosts[0]] += 1 - def report_DeselectedTest(self, event): + def report_DeselectedItem(self, event): self.out.line("Skipped (%s) %s\n" % (str(event.excinfo.value), "/". join(event.item.listnames()))) @@ -384,7 +384,7 @@ self.out.sep("=", " %d test run%s%s in %.2fs" % (total, skipped_str, failed_str, self.timeend - self.timestart)) - def report_DeselectedTest(self, event): + def report_DeselectedItem(self, event): #self.show_item(event.item, False) if isinstance(event.item, py.test2.collect.Module): self.out.write("- skipped (%s)" % event.excinfo.value) Modified: py/branch/event/py/test2/rep/rest.py ============================================================================== --- py/branch/event/py/test2/rep/rest.py (original) +++ py/branch/event/py/test2/rep/rest.py Mon Jul 28 16:02:03 2008 @@ -132,7 +132,7 @@ outcome = event.outcome text = outcome.skipped itemname = self.get_item_name(event, colitem) - elif isinstance(event, repevent.DeselectedTest): + elif isinstance(event, repevent.DeselectedItem): text = str(event.excinfo.value) itemname = "/".join(colitem.listnames()) if text not in texts: Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Mon Jul 28 16:02:03 2008 @@ -64,16 +64,12 @@ def rep_ItemStart(self, event): self.out.line(self.indent + str(event.item)) + def rep_DeselectedItem(self, event): + msg = "DESELECTED: " + self.out.line(self.indent + msg + str(event.item)) + def rep_CollectionFinish(self, ev): excinfo = ev.excinfo if excinfo is not None: self.out.line(self.indent + "!!! %s !!!" % excinfo.exconly()) self.indent = self.indent[:-len(self.INDENT)] - - #def rep_DeselectedTest(self, event): - # self.out.line(" " * self.indent + "- skipped -") - - #def summary(self): - # self.out.sep("=", "Total time: %.1f" % (self.timeend - self.timestart)) - - Copied: py/branch/event/py/test2/rep/testing/test_collectonly.py (from r56813, py/branch/event/py/test2/rep/testing/test_terminal.py) ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Mon Jul 28 16:02:03 2008 @@ -7,6 +7,11 @@ def setup_module(mod): mod.tmpdir = py.test.ensuretemp(mod.__name__) +def popvalue(stringio): + value = stringio.getvalue().rstrip() + stringio.truncate(0) + return value + class InlineCollect: def setup_class(cls): cls.clstmpdir = tmpdir.join(cls.__name__) @@ -21,19 +26,8 @@ config = py.test2.config._reparse([path] + configargs) return config._getcollector(path) -class TestTerminal: - def test_session_reporter_subscription(self): - config = py.test2.config._reparse(['xxx']) - session = config.initsession() - session.sessionstarts() - rep = session.reporter - assert isinstance(rep, TerminalReporter) - assert rep.processevent in config.bus._subscribers - session.sessionfinishes() - assert rep.processevent not in config.bus._subscribers - class TestCollectonly(InlineCollect): - def test_collectonly(self): + def test_collectonly_basic(self): modcol = self.getmodulecol(['--collectonly'], """ def test_func(): pass @@ -43,13 +37,16 @@ indent = rep.indent rep.processevent(repevent.CollectionStart(modcol)) s = popvalue(stringio) - assert s == "" + assert s == "" item = modcol.join("test_func") rep.processevent(repevent.ItemStart(item)) s = popvalue(stringio) assert s == " " - + rep.processevent(repevent.DeselectedItem(item, None)) + s = popvalue(stringio) + assert s == " DESELECTED: " + rep.processevent(repevent.CollectionFinish(modcol)) assert rep.indent == indent @@ -90,7 +87,3 @@ !!! ValueError: 0 !!! """) -def popvalue(stringio): - value = stringio.getvalue().rstrip() - stringio.truncate(0) - return value Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Mon Jul 28 16:02:03 2008 @@ -178,7 +178,7 @@ class FakeOutcome(Container, repevent.ItemFinish): pass - class FakeTryiter(Container, repevent.DeselectedTest): + class FakeTryiter(Container, repevent.DeselectedItem): pass reporter.skips() Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Mon Jul 28 16:02:03 2008 @@ -4,23 +4,6 @@ from py.__.test2.testing import suptest from py.__.test2.genitem import genitems -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - -class InlineCollect: - def setup_class(cls): - cls.clstmpdir = tmpdir.join(cls.__name__) - - def setup_method(self, method): - self.tmpdir = self.clstmpdir.join(method.__name__) - - def getmodulecol(self, configargs, source): - self.tmpdir.ensure("__init__.py") - path = self.tmpdir.ensure(self.tmpdir.purebasename + ".py") - path.write(py.code.Source(source)) - config = py.test2.config._reparse([path] + configargs) - return config._getcollector(path) - class TestTerminal: def test_session_reporter_subscription(self): config = py.test2.config._reparse(['xxx']) @@ -32,65 +15,3 @@ session.sessionfinishes() assert rep.processevent not in config.bus._subscribers -class TestCollectonly(InlineCollect): - def test_collectonly(self): - modcol = self.getmodulecol(['--collectonly'], """ - def test_func(): - pass - """) - stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) - indent = rep.indent - rep.processevent(repevent.CollectionStart(modcol)) - s = popvalue(stringio) - assert s == "" - - item = modcol.join("test_func") - rep.processevent(repevent.ItemStart(item)) - s = popvalue(stringio) - assert s == " " - - rep.processevent(repevent.CollectionFinish(modcol)) - assert rep.indent == indent - - def test_collectonly_skipped_module(self): - modcol = self.getmodulecol(['--collectonly'], """ - import py - py.test2.skip("nomod") - """) - stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) - rep.activate(modcol._config.bus) - cols = list(genitems([modcol])) - assert len(cols) == 0 - stringio.seek(0) - lines = stringio.readlines() - lines = map(str.rstrip, lines) - print lines - suptest.assert_lines_contain_lines(lines, """ - - !!! Skipped: 'nomod' !!! - """) - - def test_collectonly_failed_module(self): - modcol = self.getmodulecol(['--collectonly'], """ - raise ValueError(0) - """) - stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) - rep.activate(modcol._config.bus) - cols = list(genitems([modcol])) - assert len(cols) == 0 - stringio.seek(0) - lines = stringio.readlines() - lines = map(str.rstrip, lines) - print lines - suptest.assert_lines_contain_lines(lines, """ - - !!! ValueError: 0 !!! - """) - -def popvalue(stringio): - value = stringio.getvalue().rstrip() - stringio.truncate(0) - return value Modified: py/branch/event/py/test2/rep/web.py ============================================================================== --- py/branch/event/py/test2/rep/web.py (original) +++ py/branch/event/py/test2/rep/web.py Mon Jul 28 16:02:03 2008 @@ -250,7 +250,7 @@ args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid} elif isinstance(event, repevent.FailedTryiter): args = add_item(event) - elif isinstance(event, repevent.DeselectedTest): + elif isinstance(event, repevent.DeselectedItem): args = add_item(event) args['reason'] = str(event.excinfo.value) else: Modified: py/branch/event/py/test2/rep/webjs.py ============================================================================== --- py/branch/event/py/test2/rep/webjs.py (original) +++ py/branch/event/py/test2/rep/webjs.py Mon Jul 28 16:02:03 2008 @@ -222,7 +222,7 @@ module_part.appendChild(tr) item_name = msg['fullitemname'] exported_methods.show_fail(item_name, fail_come_back) - elif msg['type'] == 'DeselectedTest': + elif msg['type'] == 'DeselectedItem': module_part = get_elem(msg['fullitemname']) if not module_part: glob.pending.append(msg) Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Mon Jul 28 16:02:03 2008 @@ -1,13 +1,12 @@ -""" Reporter classes for showing asynchronous and synchronous status events +""" + test collection and execution events """ import py import time from py.__.test2.outcome import Skipped -# XXX the below line helps to deal with the py/test py/test2 situation from py.__.test.outcome import Skipped as Skipped2 - class BaseEvent(object): def __repr__(self): l = ["%s=%s" %(key, value) @@ -31,6 +30,10 @@ self.session = session self.timeend = time.time() +# ---------------------------------------------------------------------- +# Events related to collecting Collectors +# ---------------------------------------------------------------------- + class CollectionStart(BaseEvent): def __init__(self, collector): self.collector = collector @@ -40,19 +43,20 @@ self.collector = collector self.excinfo = excinfo -class DeselectedTest(BaseEvent): - def __init__(self, item, keywordexpr): - self.item = item - self.keywordexpr = keywordexpr +# ---------------------------------------------------------------------- +# Events related to collecting and executing test Items +# ---------------------------------------------------------------------- class ItemStart(BaseEvent): def __init__(self, item): self.item = item self.time = timestamp() -# ---------------------------------------------------------------------- -# Report of the run of a single test (might come from a disttributed run) -# ---------------------------------------------------------------------- +class DeselectedItem(BaseEvent): + def __init__(self, item, keywordexpr): + self.item = item + self.keywordexpr = keywordexpr + import marshal @@ -126,19 +130,3 @@ self.host = host self.root = root -# ---------------------------------------------------------------------- -# XXX Extra Events XXX -# ---------------------------------------------------------------------- - -class ImmediateFailure(BaseEvent): - def __init__(self, item, outcome): - self.item = item - self.outcome = outcome - -class InterruptedExecution(BaseEvent): - def __init__(self): - self.timeend = timestamp() - -class CrashedExecution(BaseEvent): - def __init__(self): - self.timeend = timestamp() Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Mon Jul 28 16:02:03 2008 @@ -13,7 +13,7 @@ passed, skipped, failed = sorter.listoutcomes() assert len(failed) == 1 assert failed[0].repr_path[1].endswith(name) - des = sorter.get(repevent.DeselectedTest) + des = sorter.get(repevent.DeselectedItem) assert len(des) == 1 for keyword in ['test_one', 'est_on']: @@ -43,7 +43,7 @@ assert len(passed) == 1 assert passed[0].repr_path[1].endswith('test_2') assert not skipped - deslist = sorter.get(repevent.DeselectedTest) + deslist = sorter.get(repevent.DeselectedItem) assert len(deslist) == 1 assert deslist[0].item.name == 'test_1' @@ -53,7 +53,7 @@ passed, skipped, failed = sorter.countoutcomes() assert passed == 2 assert skipped == failed == 0 - deslist = sorter.get(repevent.DeselectedTest) + deslist = sorter.get(repevent.DeselectedItem) assert len(deslist) == 1 assert deslist[0].item.name == "test_one" From hpk at codespeak.net Mon Jul 28 16:19:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 16:19:41 +0200 (CEST) Subject: [py-svn] r56815 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080728141941.2C73239B5BB@codespeak.net> Author: hpk Date: Mon Jul 28 16:19:40 2008 New Revision: 56815 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/genitem.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_session.py Log: make --collectonly acceptance test pass :) Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Jul 28 16:19:40 2008 @@ -137,6 +137,15 @@ except AttributeError: return self._conftest.rget(name, path) + def initreporter(self): + if self.option.collectonly: + from py.__.test2.rep.collectonly import Reporter + else: + from py.__.test2.rep.terminal import Reporter + rep = Reporter(self) + rep.activate(self.bus) + return rep + def initsession(self): """ return an initialized session object. """ cls = self._getsessionclass() Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Mon Jul 28 16:19:40 2008 @@ -14,6 +14,7 @@ if next._config.option.keyword_oneshot: keywordexpr = None else: + bus.notify(repevent.ItemStart(next)) yield next else: assert isinstance(next, Collector), next Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Mon Jul 28 16:19:40 2008 @@ -1,31 +1,38 @@ """ --collectonly session, not to spread logic all over the place """ - import py -from py.__.test2.session import Session -from py.__.test2.reporter import LocalReporter - -class CollectReporter(LocalReporter): - def __init__(self, *args, **kwds): - super(LocalReporter, self).__init__(*args, **kwds) - self.indent = 0 - - def report_ItemFinish(self, event): - pass +from py.__.test2.rep.base import BaseReporter - def report_ItemStart(self, event): - self.out.line(" " * self.indent + str(event.item)) - self.indent += 2 +class CollectonlyReporter(BaseReporter): + INDENT = " " - def report_ItemFinish(self, event): - self.indent -= 2 - - def report_CollectionFailed(self, event): - self.out.line(" " * self.indent + "- FAILED TO LOAD MODULE -") + def __init__(self, config, out=None): + super(CollectonlyReporter, self).__init__() + self.config = config + if out is None: + out = py.std.sys.stdout + self.out = py.io.TerminalWriter(out) + self.indent = "" + + def outindent(self, line): + self.out.line(self.indent + str(line)) + + def rep_CollectionStart(self, ev): + self.outindent(ev.collector) + self.indent += self.INDENT + + def rep_ItemStart(self, event): + self.outindent(event.item) - def report_DeselectedItem(self, event): - self.out.line(" " * self.indent + "- skipped -") + def rep_DeselectedItem(self, event): + msg = "DESELECTED: " + self.outindent(msg + str(event.item)) + + def rep_CollectionFinish(self, ev): + excinfo = ev.excinfo + if excinfo is not None: + self.outindent("!!! %s !!!" % excinfo.exconly()) + self.indent = self.indent[:-len(self.INDENT)] - def summary(self): - self.out.sep("=", "Total time: %.1f" % (self.timeend - self.timestart)) +Reporter = CollectonlyReporter Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Mon Jul 28 16:19:40 2008 @@ -48,28 +48,5 @@ elif ev.failed: return "F" -class CollectonlyReporter(BaseReporter): - INDENT = " " - def __init__(self, config, out): - super(CollectonlyReporter, self).__init__() - self.config = config - self.out = py.io.TerminalWriter(out) - self.indent = "" - - def rep_CollectionStart(self, ev): - self.out.line(str(ev.collector)) - self.indent += self.INDENT - - def rep_ItemStart(self, event): - self.out.line(self.indent + str(event.item)) - - def rep_DeselectedItem(self, event): - msg = "DESELECTED: " - self.out.line(self.indent + msg + str(event.item)) - - def rep_CollectionFinish(self, ev): - excinfo = ev.excinfo - if excinfo is not None: - self.out.line(self.indent + "!!! %s !!!" % excinfo.exconly()) - self.indent = self.indent[:-len(self.INDENT)] +Reporter = TerminalReporter Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Mon Jul 28 16:19:40 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2.rep.terminal import TerminalReporter, CollectonlyReporter +from py.__.test2.rep.collectonly import CollectonlyReporter from py.__.test2 import repevent from py.__.test2.testing import suptest from py.__.test2.genitem import genitems Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Mon Jul 28 16:19:40 2008 @@ -1,8 +1,8 @@ import py -from py.__.test2.rep.terminal import TerminalReporter, CollectonlyReporter -from py.__.test2 import repevent -from py.__.test2.testing import suptest -from py.__.test2.genitem import genitems +from py.__.test2.rep.terminal import TerminalReporter +#from py.__.test2 import repevent +#from py.__.test2.testing import suptest +#from py.__.test2.genitem import genitems class TestTerminal: def test_session_reporter_subscription(self): Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Jul 28 16:19:40 2008 @@ -44,10 +44,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - from py.__.test2.rep.terminal import TerminalReporter - rep = TerminalReporter(self) - rep.activate(self.config.bus) - self.reporter = rep + self.reporter = self.config.initreporter() self.config.bus.notify(repevent.SessionStart(self)) if not self.config.option.nomagic: py.magic.invoke(assertion=1) Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Mon Jul 28 16:19:40 2008 @@ -205,7 +205,8 @@ sorter = suptest.events_from_cmdline([p.dirpath(), '--collectonly']) - assert not sorter.get(repevent.ItemStart) + itemstarted = sorter.get(repevent.ItemStart) + assert len(itemstarted) == 3 assert not sorter.get(repevent.ItemTestReport) started = sorter.get(repevent.CollectionStart) finished = sorter.get(repevent.CollectionFinish) From hpk at codespeak.net Mon Jul 28 16:27:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 16:27:08 +0200 (CEST) Subject: [py-svn] r56816 - py/branch/event/py Message-ID: <20080728142708.8D8DB2A80A1@codespeak.net> Author: hpk Date: Mon Jul 28 16:27:08 2008 New Revision: 56816 Modified: py/branch/event/py/__init__.py Log: remove trace of a discarded experiment Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Mon Jul 28 16:27:08 2008 @@ -67,7 +67,6 @@ 'test2.collect.Item' : ('./test2/item.py', 'Item'), 'test.collect.Function' : ('./test/item.py', 'Function'), 'test2.collect.Function' : ('./test2/item.py', 'Function'), - 'test2.collect.GeneratedFunction' : ('./test2/item.py', 'GeneratedFunction'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), From hpk at codespeak.net Mon Jul 28 17:26:43 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 17:26:43 +0200 (CEST) Subject: [py-svn] r56817 - in py/branch/event/py/test2: . rep Message-ID: <20080728152643.5FC1A39B5B5@codespeak.net> Author: hpk Date: Mon Jul 28 17:26:41 2008 New Revision: 56817 Modified: py/branch/event/py/test2/compat.py py/branch/event/py/test2/genitem.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/session.py Log: streamlining py.test2 runs, fixing compat collection issue. Modified: py/branch/event/py/test2/compat.py ============================================================================== --- py/branch/event/py/test2/compat.py (original) +++ py/branch/event/py/test2/compat.py Mon Jul 28 17:26:41 2008 @@ -1,12 +1,12 @@ import py -from py.__.test2.outcome import Failed, Passed +from _mypytest import Function -class TestCaseUnit(py.test.collect.Function): +class TestCaseUnit(Function): """ compatibility Unit executor for TestCase methods honouring setUp and tearDown semantics. """ - def execute(self, session): + def execute(self, _deprecated=None): boundmethod = self.obj instance = boundmethod.im_self instance.setUp() @@ -14,7 +14,6 @@ boundmethod() finally: instance.tearDown() - return Passed() class TestCase(object): """compatibility class of unittest's TestCase. """ @@ -28,7 +27,7 @@ def fail(self, msg=None): """ fail immediate with given message. """ - raise Failed(msg=msg) + py.test2.fail(msg) def assertRaises(self, excclass, func, *args, **kwargs): py.test2.raises(excclass, func, *args, **kwargs) @@ -50,7 +49,7 @@ def %(name)s(self, %(sig)s, msg=""): __tracebackhide__ = True if %(expr)s: - raise Failed(msg=msg + (%(sigsubst)r %% (%(sig)s))) + py.test2.fail(msg=msg + (%(sigsubst)r %% (%(sig)s))) """ % locals() ) source = "".join(items) Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Mon Jul 28 17:26:41 2008 @@ -1,7 +1,7 @@ import py from py.__.test2 import repevent -Item = py.test2.collect.Item -Collector = py.test2.collect.Collector +Item = (py.test.collect.Item, py.test2.collect.Item) +Collector = (py.test.collect.Collector, py.test2.collect.Collector) def genitems(colitems, keywordexpr=None): """ yield Items from iterating over the given colitems. """ @@ -17,7 +17,7 @@ bus.notify(repevent.ItemStart(next)) yield next else: - assert isinstance(next, Collector), next + assert isinstance(next, Collector) bus.notify(repevent.CollectionStart(next)) excinfo = None try: Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Mon Jul 28 17:26:41 2008 @@ -42,3 +42,4 @@ l += self.getreports("failed_crashed") return l return self.getreports(category) + Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Mon Jul 28 17:26:41 2008 @@ -3,6 +3,8 @@ """ import py from py.__.test2.rep.base import BaseReporter +from py.__.test2.outcome import Skipped as Skipped2 +from py.__.test.outcome import Skipped class CollectonlyReporter(BaseReporter): INDENT = " " @@ -14,6 +16,7 @@ out = py.std.sys.stdout self.out = py.io.TerminalWriter(out) self.indent = "" + self._failed = [] def outindent(self, line): self.out.line(self.indent + str(line)) @@ -33,6 +36,17 @@ excinfo = ev.excinfo if excinfo is not None: self.outindent("!!! %s !!!" % excinfo.exconly()) + self._failed.append(ev) self.indent = self.indent[:-len(self.INDENT)] + def rep_SessionFinish(self, session): + from py.__.code.tbpresent import TBPresenter + # XXX add tests + # XXX re-use runner for collection? + tb = TBPresenter(out=self.out, showlocals=self.config.option.showlocals) + for ev in self._failed: + if not ev.excinfo.errisinstance((Skipped, Skipped2)): + tb.out.sep("*", "failed collection") + tb.repr_tb(ev.excinfo) + Reporter = CollectonlyReporter Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Mon Jul 28 17:26:41 2008 @@ -12,29 +12,31 @@ self.currentfspath = None self.out = py.io.TerminalWriter(sys.stdout) - def processevent(self, ev): - if isinstance(ev, repevent.ItemTestReport): - fspath, modpath = ev.repr_path - if fspath != self.currentfspath: - self.out.line() - self.out.write(fspath + " ") - self.currentfspath = fspath - self.out.write(self._getletter(ev)) - elif isinstance(ev, repevent.SessionFinish): + def rep_ItemTestReport(self, ev): + super(TerminalReporter, self).rep_ItemTestReport(ev) + fspath, modpath = ev.repr_path + if fspath != self.currentfspath: self.out.line() - #self.print_failures() - #self.out.sep("=", "%d tests of which %d failed, %d skipped" %( - # len(self._failed) + self._numskipped + self._numpassed, - # len(self._failed), self._numskipped)) - elif isinstance(ev, repevent.SessionStart): - self.out.sep("=", "session starts") + self.out.write(fspath + " ") + self.currentfspath = fspath + self.out.write(self._getletter(ev)) - def print_failures(self): - if not self._failed: - return - for ev in self._failed: + def rep_SessionStart(self, ev): + self.out.sep("=", "session starts") + + def rep_SessionFinish(self, ev): + self.out.line("") + for ev in self.getreports(category="failed"): + self.out.line(str(ev)) #self.out.sep("_", "entrypoint: %s" %(ev.repr_path[1],)) - self.out.line(ev.repr_run) + #self.out.line(ev.repr_run) + #self.out.sep("=") + numfailed = len(self.getreports(category="failed")) + numskipped = len(self.getreports(category="skipped")) + numpassed = len(self.getreports(category="passed")) + sum = numfailed + numskipped + numpassed + self.out.sep("=", "%d tests of which %d failed, %d skipped" %( + sum, numfailed, numskipped)) def _getletter(self, ev): if ev.passed: Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Jul 28 17:26:41 2008 @@ -93,3 +93,4 @@ setupstate=item._config._setupstate, getcapture=item._config._getcapture, pdb=pdb) + From hpk at codespeak.net Mon Jul 28 17:27:42 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 17:27:42 +0200 (CEST) Subject: [py-svn] r56818 - in py/branch/event: . py/test2 Message-ID: <20080728152742.518A039B5AE@codespeak.net> Author: hpk Date: Mon Jul 28 17:27:41 2008 New Revision: 56818 Added: py/branch/event/py/test2/_mypytest.py Modified: py/branch/event/NOTES_FOR_MERGE Log: forgot helper file Modified: py/branch/event/NOTES_FOR_MERGE ============================================================================== --- py/branch/event/NOTES_FOR_MERGE (original) +++ py/branch/event/NOTES_FOR_MERGE Mon Jul 28 17:27:41 2008 @@ -1,7 +1,9 @@ remove misc/terminal_helper.py (now py.io.TerminalWriter) -merge py.process.cmdexec to py.io apigen executor / execution? --- after merge? -- +-- after merge -- for TODO +remove _mypytest.py review py.io.ForkedFunc and see if to provide a function that throws ExecutionFailed aka py.process.cmdexec +merge py.process.cmdexec to py.io +split collect into collect.py and pycollect.py? Added: py/branch/event/py/test2/_mypytest.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/_mypytest.py Mon Jul 28 17:27:41 2008 @@ -0,0 +1,8 @@ +import py + +if py.test.config._initialized: + Function = py.test.collect.Function +else: + Function = py.test2.collect.Function + + From hpk at codespeak.net Mon Jul 28 23:36:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 28 Jul 2008 23:36:12 +0200 (CEST) Subject: [py-svn] r56824 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080728213612.17D39168453@codespeak.net> Author: hpk Date: Mon Jul 28 23:36:11 2008 New Revision: 56824 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/genitem.py py/branch/event/py/test2/item.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_runner.py py/branch/event/py/test2/testing/test_session.py Log: * a first go at refactoring runner to also care for collection * extending reporters and tests to report about failed collection * making terminal reporter do some more (tested) work Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Mon Jul 28 23:36:11 2008 @@ -25,6 +25,7 @@ """ from __future__ import generators import py +from py.__.test2 import pypresent def configproperty(name): def fget(self): @@ -209,6 +210,9 @@ if path.check(dir=1, dotfile=0): return path.basename not in ('CVS', '_darcs', '{arch}') + def repr_path(self): + return (self.fspath, None) + def listdir(self): files = [] dirs = [] @@ -255,6 +259,11 @@ def _getobj(self): return getattr(self.parent.obj, self.name) + def repr_path(self): + fspath = pypresent.getrelpath(self._config.topdir, self.fspath) + modpath = pypresent.getmodpath(self) + return (fspath, modpath) + class PyCollectorMixin(PyobjMixin, Collector): def funcnamefilter(self, name): @@ -312,10 +321,12 @@ self._prepare() return self._name2items.get(name, None) - class Module(FSCollector, PyCollectorMixin): _stickyfailure = None + def repr_path(self): + return (self.fspath, None) + def listdir(self): if getattr(self.obj, 'disabled', 0): return [] Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Mon Jul 28 23:36:11 2008 @@ -1,8 +1,11 @@ import py from py.__.test2 import repevent +from py.__.test2.outcome import Exit Item = (py.test.collect.Item, py.test2.collect.Item) Collector = (py.test.collect.Collector, py.test2.collect.Collector) +from runner import basic_collect + def genitems(colitems, keywordexpr=None): """ yield Items from iterating over the given colitems. """ while colitems: @@ -19,13 +22,9 @@ else: assert isinstance(next, Collector) bus.notify(repevent.CollectionStart(next)) - excinfo = None - try: - cols = [next.join(x) for x in next.listdir()] - for x in genitems(cols, keywordexpr): - yield x - except py.builtin.sysex: - raise - except: - excinfo = py.code.ExceptionInfo() - bus.notify(repevent.CollectionFinish(next, excinfo)) + runinfo = basic_collect(next, next._config._getcapture) + if runinfo.hasresult(): + for x in genitems(runinfo.result, keywordexpr): + yield x + bus.notify(repevent.CollectionFinish(next, runinfo)) + Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Mon Jul 28 23:36:11 2008 @@ -73,7 +73,3 @@ """ return a textual representation of run info. """ return pypresent.python_repr_run(runinfo) - def repr_path(self): - fspath = pypresent.getrelpath(self._config.topdir, self.fspath) - modpath = pypresent.getmodpath(self) - return (fspath, modpath) Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Mon Jul 28 23:36:11 2008 @@ -1,4 +1,5 @@ from py.__.test2 import repevent +from py.__.test2.runner import RunInfo class BaseReporter(object): def __init__(self): @@ -25,21 +26,28 @@ l = self._outcome2rep.setdefault(outcome, []) l.append(ev) + def rep_CollectionFinish(self, ev): + outcome = ev.runinfo.outcome + if outcome == "failed": + l = self._outcome2rep.setdefault("failed_collection", []) + l.append(ev) + def getreports(self, outcome=None, category=None): """ return a list with all reports matching the given outcome or (if outcome is None) the category. """ if outcome is not None: - if outcome not in repevent.ItemTestReport._outcomes: + if outcome not in repevent.validoutcomes: raise ValueError("not a valid outcome %r" %(outcome)) l = self._outcome2rep.setdefault(outcome, []) return l[:] if category is not None: if category == "failed": l = self.getreports("failed") - l += self.getreports("failed_setup") l += self.getreports("failed_crashed") + l += self.getreports("failed_setup") + l += self.getreports("failed_collection") return l return self.getreports(category) Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Mon Jul 28 23:36:11 2008 @@ -33,8 +33,8 @@ self.outindent(msg + str(event.item)) def rep_CollectionFinish(self, ev): - excinfo = ev.excinfo - if excinfo is not None: + if ev.runinfo is not None and not ev.runinfo.hasresult(): + excinfo = ev.runinfo.excinfo self.outindent("!!! %s !!!" % excinfo.exconly()) self._failed.append(ev) self.indent = self.indent[:-len(self.INDENT)] @@ -45,8 +45,8 @@ # XXX re-use runner for collection? tb = TBPresenter(out=self.out, showlocals=self.config.option.showlocals) for ev in self._failed: - if not ev.excinfo.errisinstance((Skipped, Skipped2)): + if not ev.runinfo.excinfo.errisinstance((Skipped, Skipped2)): tb.out.sep("*", "failed collection") - tb.repr_tb(ev.excinfo) + tb.repr_tb(ev.runinfo.excinfo) Reporter = CollectonlyReporter Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Mon Jul 28 23:36:11 2008 @@ -6,11 +6,13 @@ from py.__.test.outcome import Skipped class TerminalReporter(BaseReporter): - def __init__(self, config, out=None): + def __init__(self, config, file=None): super(TerminalReporter, self).__init__() self.config = config self.currentfspath = None - self.out = py.io.TerminalWriter(sys.stdout) + if file is None: + file = py.std.sys.stdout + self.out = py.io.TerminalWriter(file) def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) @@ -21,15 +23,24 @@ self.currentfspath = fspath self.out.write(self._getletter(ev)) + def rep_CollectionFinish(self, ev): + super(TerminalReporter, self).rep_CollectionFinish(ev) + if ev.runinfo.excinfo: + fspath, modpath = ev.runinfo.item.repr_path() + self.out.line() + self.out.write(fspath + " - ImportError -") + self.currentfspath = fspath + def rep_SessionStart(self, ev): self.out.sep("=", "session starts") def rep_SessionFinish(self, ev): self.out.line("") for ev in self.getreports(category="failed"): - self.out.line(str(ev)) #self.out.sep("_", "entrypoint: %s" %(ev.repr_path[1],)) - #self.out.line(ev.repr_run) + try: self.out.line(ev.repr_run) + except AttributeError: + self.out.line(ev.runinfo.repr_run()) #self.out.sep("=") numfailed = len(self.getreports(category="failed")) numskipped = len(self.getreports(category="skipped")) Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Mon Jul 28 23:36:11 2008 @@ -2,6 +2,7 @@ from py.__.test2.rep.base import BaseReporter from py.__.test2.eventbus import EventBus from py.__.test2 import repevent +from py.__.test2.runner import RunInfo class TestBaseReporter: def test_activate(self): @@ -44,11 +45,11 @@ def test_default_TestItemReport_all_outcomes(self): rep = BaseReporter() - for outcome in repevent.ItemTestReport._outcomes: + for outcome in repevent.validoutcomes: ev = repevent.ItemTestReport(None, outcome, None, None) rep.processevent(ev) rep.processevent(ev) - for outcome in repevent.ItemTestReport._outcomes: + for outcome in repevent.validoutcomes: l = rep.getreports(outcome) assert len(l) == 2 assert l[0] is l[1] @@ -86,3 +87,13 @@ assert l == [ev5] l = rep.getreports("skipped") assert l == [ev5] + + def test_CollectionFinish_failed(self): + rep = BaseReporter() + runinfo = RunInfo(None, "failed", None, None) + ev = repevent.CollectionFinish(None, runinfo) + rep.processevent(ev) + l = rep.getreports(category="failed") + assert l == [ev] + l = rep.getreports("failed_collection") + assert l == [ev] Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Mon Jul 28 23:36:11 2008 @@ -4,16 +4,20 @@ from py.__.test2.testing import suptest from py.__.test2.genitem import genitems -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp(mod.__name__) - def popvalue(stringio): value = stringio.getvalue().rstrip() stringio.truncate(0) return value +def assert_stringio_contains_lines(stringio, tomatchlines): + stringio.seek(0) + l = stringio.readlines() + l = map(str.rstrip, l) + suptest.assert_lines_contain_lines(l, tomatchlines) + class InlineCollect: def setup_class(cls): + tmpdir = py.test.ensuretemp(__name__) cls.clstmpdir = tmpdir.join(cls.__name__) def setup_method(self, method): @@ -23,6 +27,7 @@ self.tmpdir.ensure("__init__.py") path = self.tmpdir.ensure(self.tmpdir.purebasename + ".py") path.write(py.code.Source(source)) + print "wrote InlineCollect example to", path config = py.test2.config._reparse([path] + configargs) return config._getcollector(path) @@ -60,11 +65,7 @@ rep.activate(modcol._config.bus) cols = list(genitems([modcol])) assert len(cols) == 0 - stringio.seek(0) - lines = stringio.readlines() - lines = map(str.rstrip, lines) - print lines - suptest.assert_lines_contain_lines(lines, """ + assert_stringio_contains_lines(stringio, """ !!! Skipped: 'nomod' !!! """) @@ -78,11 +79,7 @@ rep.activate(modcol._config.bus) cols = list(genitems([modcol])) assert len(cols) == 0 - stringio.seek(0) - lines = stringio.readlines() - lines = map(str.rstrip, lines) - print lines - suptest.assert_lines_contain_lines(lines, """ + assert_stringio_contains_lines(stringio, """ !!! ValueError: 0 !!! """) Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Mon Jul 28 23:36:11 2008 @@ -1,10 +1,12 @@ import py from py.__.test2.rep.terminal import TerminalReporter -#from py.__.test2 import repevent +from py.__.test2 import repevent #from py.__.test2.testing import suptest -#from py.__.test2.genitem import genitems +from py.__.test2.genitem import genitems +from py.__.test2.runner import basic_run_report +from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines -class TestTerminal: +class TestTerminal(InlineCollect): def test_session_reporter_subscription(self): config = py.test2.config._reparse(['xxx']) session = config.initsession() @@ -15,3 +17,47 @@ session.sessionfinishes() assert rep.processevent not in config.bus._subscribers + def test_pass_skip_fail(self): + modcol = self.getmodulecol([], """ + import py + def test_ok(): + pass + def test_skip(): + py.test2.skip("xx") + def test_func(): + assert 0 + """) + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(modcol._config, file=stringio) + for item in genitems([modcol]): + ev = basic_run_report(item, + setupstate=modcol._config._setupstate, + getcapture=modcol._config._getcapture + ) + rep.processevent(ev) + s = popvalue(stringio) + assert s.find("test_pass_skip_fail.py .sF") != -1 + rep.processevent(repevent.SessionFinish(None)) + assert_stringio_contains_lines(stringio, [ + " def test_func():", + "> assert 0", + "E assert 0", + ]) + + def test_collect_fail(self): + modcol = self.getmodulecol([], """ + import xyz + """) + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(modcol._config, file=stringio) + rep.activate(modcol._config.bus) + l = list(genitems([modcol])) + assert len(l) == 0 + s = popvalue(stringio) + print s + assert s.find("test_collect_fail.py - ImportError -") != -1 + rep.processevent(repevent.SessionFinish(None)) + assert_stringio_contains_lines(stringio, [ + "> import xyz", + "E ImportError: No module named xyz" + ]) Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Mon Jul 28 23:36:11 2008 @@ -7,6 +7,9 @@ from py.__.test2.outcome import Skipped from py.__.test.outcome import Skipped as Skipped2 +validoutcomes = ("passed skipped failed failed_setup failed_collection " + "failed_teardown failed_crashed").split() + class BaseEvent(object): def __repr__(self): l = ["%s=%s" %(key, value) @@ -39,9 +42,9 @@ self.collector = collector class CollectionFinish(BaseEvent): - def __init__(self, collector, excinfo=None): - self.collector = collector - self.excinfo = excinfo + def __init__(self, collector, runinfo=None): + self.collector = collector + self.runinfo = runinfo # ---------------------------------------------------------------------- # Events related to collecting and executing test Items @@ -57,12 +60,11 @@ self.item = item self.keywordexpr = keywordexpr - import marshal class ItemTestReport(BaseEvent): - _outcomes = "passed skipped failed failed_setup failed_crashed".split() - for _ in _outcomes: + for _ in validoutcomes: + if _ == "failed_collection": continue exec "%s = property(lambda self: self.outcome == %r)\n" %(_,_) failed = property(lambda self: self.outcome.startswith("failed")) @@ -74,7 +76,7 @@ def __init__(self, trail, outcome, repr_run, repr_path, outerr=None): self.trail = trail - assert outcome in self._outcomes + assert outcome in validoutcomes self.outcome = outcome self.repr_run = repr_run self.repr_path = repr_path @@ -96,6 +98,7 @@ d[name] = value return marshal.dumps(d) + # ---------------------------------------------------------------------- # Distributed Testing Events # ---------------------------------------------------------------------- Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Mon Jul 28 23:36:11 2008 @@ -10,53 +10,103 @@ import py.__.test2.custompdb from py.__.test2 import pypresent -class RunInfo: - """ info on test runs. """ - def __init__(self, item, outcome, excinfo, outerr): - if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): - assert outcome in ('failed_setup', 'failed') - outcome = "skipped" - self.item = item - self.outcome = outcome - self.excinfo = excinfo - self.outerr = outerr - -def setup_and_execute(item, setupstate, getcapture): - """ returns RunInfo after setup and execution of test item. """ - capture = getcapture() - excinfo = None - try: +# +# internal classes (search for "public" below) +# + +class RobustRun: + """ a robust setup/execute/teardown protocol. """ + def run(self): + """ return result of running setup, execution, teardown procedures. """ + excinfo = None + res = NORESULT + capture = self.getcapture() try: - outcome = "failed_setup" - setupstate.prepare(item) try: - item.execute() - finally: outcome = "failed_setup" - setupstate.teardown_exact(item) - outcome = "failed" - outcome = "passed" - finally: - outerr = capture.reset() - except (Exit, KeyboardInterrupt): - raise - except: - excinfo = py.code.ExceptionInfo() - return RunInfo(item, outcome, excinfo, outerr) + self.setup() + try: + res = self.execute() + finally: + outcome = "failed_teardown" + self.teardown() + outcome = "failed" + outcome = "passed" + finally: + outerr = capture.reset() + except (Exit, KeyboardInterrupt): + raise + except: + excinfo = py.code.ExceptionInfo() + return self.makeruninfo(res, outcome, excinfo, outerr) + +class ItemRunner(RobustRun): + def __init__(self, item, setupstate, getcapture): + self.item = item + self.setupstate = setupstate + self.getcapture = getcapture + def setup(self): + self.setupstate.prepare(self.item) + def teardown(self): + self.setupstate.teardown_exact(self.item) + def execute(self): + self.item.execute() + def makeruninfo(self, res, outcome, excinfo, outerr): + return RunInfo(self.item, outcome, excinfo, outerr) + +class CollectorRunner(RobustRun): + def __init__(self, col, getcapture): + self.col = col + self.getcapture = getcapture + def setup(self): + pass + def teardown(self): + pass + def execute(self): + return [self.col.join(x) for x in self.col.listdir()] + def makeruninfo(self, res, outcome, excinfo, outerr): + #outcome += "_incollection" + return RunInfo(self.col, outcome, excinfo, outerr, result=res) def makereport(runinfo): item = runinfo.item trail = item._get_collector_trail() repr_path = item.repr_path() - if runinfo.outcome != "failed_setup": + if runinfo.outcome not in ("failed_setup", "failed_teardown"): repr_run = item.repr_run(runinfo) else: repr_run = pypresent.python_repr_run(runinfo, title="failure during setup/teardown") return repevent.ItemTestReport(trail, runinfo.outcome, repr_run, repr_path) +NORESULT = object() +# +# public entrypoints / objects +# +class RunInfo: + """ info on test or collector runs. """ + def __init__(self, item, outcome, excinfo, outerr, result=NORESULT): + assert outcome in repevent.validoutcomes + if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): + assert outcome.split('_')[0] == "failed" + outcome = "skipped" + self.item = item + self.outcome = outcome + self.excinfo = excinfo + self.outerr = outerr + self.result = result + def hasresult(self): + return self.result is not NORESULT + + def repr_run(self): + return pypresent.python_repr_run(self, + title=self.outcome.replace("_", " ")) + +def basic_collect(collector, getcapture): + return CollectorRunner(collector, getcapture).run() + def basic_run_report(item, setupstate, getcapture, pdb=None): - runinfo = setup_and_execute(item, setupstate, getcapture) + runinfo = ItemRunner(item, setupstate, getcapture).run() if pdb is not None and runinfo.outcome in ('failed', 'failed_setup'): pdb(runinfo) return makereport(runinfo) @@ -66,7 +116,7 @@ def runforked(): try: - runinfo = setup_and_execute(item, setupstate, getcapture) + runinfo = ItemRunner(item, setupstate, getcapture).run() except (KeyboardInterrupt, Exit): os._exit(EXITSTATUS_TESTEXIT) testrep = makereport(runinfo) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Jul 28 23:36:11 2008 @@ -51,9 +51,10 @@ self._failurelist = [] self.config.bus.subscribe(self._processfailures) - def _processfailures(self, event): - if isinstance(event, repevent.ItemTestReport) and event.failed: - self._failurelist.append(event) + def _processfailures(self, ev): + if ((isinstance(ev, repevent.ItemTestReport) and ev.failed) or + (isinstance(ev, repevent.CollectionFinish) and ev.runinfo.excinfo)): + self._failurelist.append(ev) if self.config.option.exitfirst: self.shouldstop = True Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Mon Jul 28 23:36:11 2008 @@ -20,9 +20,10 @@ def test_method(self): pass """)) - outlines, errlines = self.runpytest("--collectonly", p) - assert not "".join(errlines) - extra = assert_lines_contain_lines(outlines, py.code.Source(""" + result = self.runpytest("--collectonly", p) + assert not "".join(result.errlines) + assert result.ret == 0 + extra = assert_lines_contain_lines(result.outlines, py.code.Source(""" @@ -30,6 +31,22 @@ """).strip()) + def test_nested_import_error(self): + p = py.path.local("test_one.py") + p.write(py.code.Source(""" + import import_fails + def test_this(): + assert import_fails.a == 1 + """)) + p2 = py.path.local("import_fails.py") + p2.write("import does_not_work") + result = self.runpytest(p) + extra = assert_lines_contain_lines(result.outlines, [ + "> import import_fails", + "E ImportError: No module named does_not_work", + ]) + assert result.ret == 1 + def runpytest(self, *args): pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") cmdargs = [py.std.sys.executable, pytestcmd] + list(args) @@ -40,11 +57,10 @@ stdout=p1.open("w"), stderr=p2.open("w")) ret = popen.wait() out, err = p1.readlines(cr=0), p2.readlines(cr=0) - if ret: - raise ExecutionFailed(ret, out, err) - return out, err - + return Result(ret, out, err) -class ExecutionFailed(Exception): - pass - +class Result: + def __init__(self, ret, outlines, errlines): + self.ret = ret + self.outlines = outlines + self.errlines = errlines Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Mon Jul 28 23:36:11 2008 @@ -90,7 +90,7 @@ def getfailedcollections(self): l = [] for ev in self.get(repevent.CollectionFinish): - if ev.excinfo: + if ev.runinfo.excinfo: l.append(ev) return l @@ -141,7 +141,8 @@ for line in lines2: while lines1: nextline = lines1.pop(0) - print "comparing", repr(line), repr(nextline) + print "comparing", repr(line) + print "and :", repr(nextline) if line == nextline: print "match:", repr(line) break Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Mon Jul 28 23:36:11 2008 @@ -309,7 +309,7 @@ if isinstance(x, repevent.CollectionFinish) and x.collector.name == 'subdir'] assert len(skips) == 1 - assert skips[0].excinfo.errisinstance(outcome.Skipped) + assert skips[0].runinfo.outcome == "skipped" def test_root_conftest_syntax_error(self): # do we want to unify behaviour with @@ -321,11 +321,11 @@ self.tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") items, events = self._genitems() failures = [x for x in events - if isinstance(x, repevent.CollectionFinish) - and x.excinfo] + if isinstance(x, repevent.CollectionFinish) + and x.runinfo.excinfo] assert len(failures) == 1 ev = failures[0] - assert ev.excinfo.errisinstance(SyntaxError) + assert ev.runinfo.excinfo.errisinstance(SyntaxError) def XXXtest_keyboardinterrupt(self): self.tmp.ensure("sub", "conftest.py").write("raise KeyboardInterrupt\n") @@ -346,7 +346,7 @@ and x.collector.name == 'test_module.py'] assert len(l) == 1 ev = l[0] - assert ev.excinfo.errisinstance(outcome.Skipped) + assert ev.runinfo.outcome == "skipped" def test_example_items1(self): self.tmp.ensure("test_example.py").write(py.code.Source(''' Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Mon Jul 28 23:36:11 2008 @@ -186,7 +186,7 @@ testrep = self.run(testfunc, setupstate=MySetupState()) finally: py.magic.revert(pypresent, "python_repr_run") - assert testrep.failed_setup + assert testrep.failed_teardown assert testrep.failed assert not testrep.passed assert len(l) == 2 # means that testfunc didnt run @@ -239,3 +239,4 @@ def test_assertion(): py.test.skip("XXX need to test that assertion reinterp is turned on and working for py function test items. ") + Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Mon Jul 28 23:36:11 2008 @@ -77,7 +77,7 @@ sorter = suptest.events_run_example("syntax_error.py") l = sorter.getfailedcollections() assert len(l) == 1 - out = l[0].excinfo.exconly() + out = l[0].runinfo.excinfo.exconly() assert out.find(str('syntax_error.py')) != -1 assert out.find(str('not python')) != -1 @@ -93,7 +93,7 @@ yield None """) failures = sorter.getfailedcollections() - out = failures[0].excinfo.exconly() + out = failures[0].runinfo.excinfo.exconly() i = out.find('TypeError') assert i != -1 @@ -173,7 +173,7 @@ sorter = suptest.events_from_cmdline([tfile]) l = sorter.getfailedcollections() assert len(l) == 1 - out = l[0].excinfo.exconly() + out = l[0].runinfo.excinfo.exconly() assert out.find('does_not_work') != -1 def test_safe_repr(self): From guido at codespeak.net Tue Jul 29 12:07:42 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 29 Jul 2008 12:07:42 +0200 (CEST) Subject: [py-svn] r56835 - in py/trunk/py/path: local/testing svn Message-ID: <20080729100742.32ABA398003@codespeak.net> Author: guido Date: Tue Jul 29 12:07:41 2008 New Revision: 56835 Modified: py/trunk/py/path/local/testing/test_local.py py/trunk/py/path/svn/wccommand.py Log: Refactored the wcpath.status() method, moved the parsing of the regexp to the WCStatus class. Modified: py/trunk/py/path/local/testing/test_local.py ============================================================================== --- py/trunk/py/path/local/testing/test_local.py (original) +++ py/trunk/py/path/local/testing/test_local.py Tue Jul 29 12:07:41 2008 @@ -177,6 +177,19 @@ assert l3.strpath == wc.strpath assert not hasattr(l3, 'commit') + def test_long_filenames(self): + tmpdir = self.tmpdir + # testing paths > 260 chars (which is Windows' limitation, but + # depending on how the paths are used), but > 4096 (which is the + # Linux' limitation) - the behaviour of paths with names > 4096 chars + # is undetermined + newfilename = '/test' * 60 + l = tmpdir.join(newfilename) + l.ensure(file=True) + l.write('foo') + l2 = tmpdir.join(newfilename) + assert l2.read() == 'foo' + class TestExecutionOnWindows(LocalSetup): disabled = py.std.sys.platform != 'win32' Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Tue Jul 29 12:07:41 2008 @@ -208,11 +208,6 @@ """ rename this path to target. """ py.process.cmdexec("svn move --force %s %s" %(str(self), str(target))) - # XXX a bit scary to assume there's always 2 spaces between username and - # path, however with win32 allowing spaces in user names there doesn't - # seem to be a more solid approach :( - _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') - def lock(self): """ set a lock (exclusive) on the resource """ out = self._authsvn('lock').strip() @@ -260,90 +255,9 @@ else: updates = '' - update_rev = None - cmd = 'status -v %s %s %s' % (updates, rec, externals) out = self._authsvn(cmd) - rootstatus = WCStatus(self) - for line in out.split('\n'): - if not line.strip(): - continue - #print "processing %r" % line - flags, rest = line[:8], line[8:] - # first column - c0,c1,c2,c3,c4,c5,x6,c7 = flags - #if '*' in line: - # print "flags", repr(flags), "rest", repr(rest) - - if c0 in '?XI': - fn = line.split(None, 1)[1] - if c0 == '?': - wcpath = self.join(fn, abs=1) - rootstatus.unknown.append(wcpath) - elif c0 == 'X': - wcpath = self.__class__(self.localpath.join(fn, abs=1), - auth=self.auth) - rootstatus.external.append(wcpath) - elif c0 == 'I': - wcpath = self.join(fn, abs=1) - rootstatus.ignored.append(wcpath) - - continue - - #elif c0 in '~!' or c4 == 'S': - # raise NotImplementedError("received flag %r" % c0) - - m = self._rex_status.match(rest) - if not m: - if c7 == '*': - fn = rest.strip() - wcpath = self.join(fn, abs=1) - rootstatus.update_available.append(wcpath) - continue - if line.lower().find('against revision:')!=-1: - update_rev = int(rest.split(':')[1].strip()) - continue - # keep trying - raise ValueError, "could not parse line %r" % line - else: - rev, modrev, author, fn = m.groups() - wcpath = self.join(fn, abs=1) - #assert wcpath.check() - if c0 == 'M': - assert wcpath.check(file=1), "didn't expect a directory with changed content here" - rootstatus.modified.append(wcpath) - elif c0 == 'A' or c3 == '+' : - rootstatus.added.append(wcpath) - elif c0 == 'D': - rootstatus.deleted.append(wcpath) - elif c0 == 'C': - rootstatus.conflict.append(wcpath) - elif c0 == '~': - rootstatus.kindmismatch.append(wcpath) - elif c0 == '!': - rootstatus.incomplete.append(wcpath) - elif c0 == 'R': - rootstatus.replaced.append(wcpath) - elif not c0.strip(): - rootstatus.unchanged.append(wcpath) - else: - raise NotImplementedError("received flag %r" % c0) - - if c1 == 'M': - rootstatus.prop_modified.append(wcpath) - # XXX do we cover all client versions here? - if c2 == 'L' or c5 == 'K': - rootstatus.locked.append(wcpath) - if c7 == '*': - rootstatus.update_available.append(wcpath) - - if wcpath == self: - rootstatus.rev = rev - rootstatus.modrev = modrev - rootstatus.author = author - if update_rev: - rootstatus.update_rev = update_rev - continue + rootstatus = WCStatus(self).fromstring(out, self) return rootstatus def diff(self, rev=None): @@ -609,6 +523,99 @@ l.sort() return l + # XXX a bit scary to assume there's always 2 spaces between username and + # path, however with win32 allowing spaces in user names there doesn't + # seem to be a more solid approach :( + _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') + + @staticmethod + def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): + """ return a new WCStatus object from data 's' + """ + rootstatus = WCStatus(rootwcpath, rev, modrev, author) + update_rev = None + for line in data.split('\n'): + if not line.strip(): + continue + #print "processing %r" % line + flags, rest = line[:8], line[8:] + # first column + c0,c1,c2,c3,c4,c5,x6,c7 = flags + #if '*' in line: + # print "flags", repr(flags), "rest", repr(rest) + + if c0 in '?XI': + fn = line.split(None, 1)[1] + if c0 == '?': + wcpath = rootwcpath.join(fn, abs=1) + rootstatus.unknown.append(wcpath) + elif c0 == 'X': + wcpath = rootwcpath.__class__( + rootwcpath.localpath.join(fn, abs=1), + auth=rootwcpath.auth) + rootstatus.external.append(wcpath) + elif c0 == 'I': + wcpath = rootwcpath.join(fn, abs=1) + rootstatus.ignored.append(wcpath) + + continue + + #elif c0 in '~!' or c4 == 'S': + # raise NotImplementedError("received flag %r" % c0) + + m = WCStatus._rex_status.match(rest) + if not m: + if c7 == '*': + fn = rest.strip() + wcpath = rootwcpath.join(fn, abs=1) + rootstatus.update_available.append(wcpath) + continue + if line.lower().find('against revision:')!=-1: + update_rev = int(rest.split(':')[1].strip()) + continue + # keep trying + raise ValueError, "could not parse line %r" % line + else: + rev, modrev, author, fn = m.groups() + wcpath = rootwcpath.join(fn, abs=1) + #assert wcpath.check() + if c0 == 'M': + assert wcpath.check(file=1), "didn't expect a directory with changed content here" + rootstatus.modified.append(wcpath) + elif c0 == 'A' or c3 == '+' : + rootstatus.added.append(wcpath) + elif c0 == 'D': + rootstatus.deleted.append(wcpath) + elif c0 == 'C': + rootstatus.conflict.append(wcpath) + elif c0 == '~': + rootstatus.kindmismatch.append(wcpath) + elif c0 == '!': + rootstatus.incomplete.append(wcpath) + elif c0 == 'R': + rootstatus.replaced.append(wcpath) + elif not c0.strip(): + rootstatus.unchanged.append(wcpath) + else: + raise NotImplementedError("received flag %r" % c0) + + if c1 == 'M': + rootstatus.prop_modified.append(wcpath) + # XXX do we cover all client versions here? + if c2 == 'L' or c5 == 'K': + rootstatus.locked.append(wcpath) + if c7 == '*': + rootstatus.update_available.append(wcpath) + + if wcpath == rootwcpath: + rootstatus.rev = rev + rootstatus.modrev = modrev + rootstatus.author = author + if update_rev: + rootstatus.update_rev = update_rev + continue + return rootstatus + class InfoSvnWCCommand: def __init__(self, output): # Path: test From guido at codespeak.net Tue Jul 29 14:22:23 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 29 Jul 2008 14:22:23 +0200 (CEST) Subject: [py-svn] r56843 - py/branch/guido-svnwc-xml-status Message-ID: <20080729122223.9B19F2A00DD@codespeak.net> Author: guido Date: Tue Jul 29 14:22:22 2008 New Revision: 56843 Added: py/branch/guido-svnwc-xml-status/ - copied from r56842, py/trunk/ Log: Creating branch for implementing support for reading XML status reports from SVN (as opposed to the plain-text status reports currently parsed, which is rather messy). From guido at codespeak.net Tue Jul 29 15:28:46 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 29 Jul 2008 15:28:46 +0200 (CEST) Subject: [py-svn] r56844 - in py/branch/guido-svnwc-xml-status/py/path/svn: . testing Message-ID: <20080729132846.DF15D168571@codespeak.net> Author: guido Date: Tue Jul 29 15:28:45 2008 New Revision: 56844 Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/svntestbase.py py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Log: Added some 'missing tests', fixed problem with ignores in WCStatus objects (missing --no-ignore switch to command-line svn client made that ignores weren't in the .ignored set, I hope the switch works in all SVN versions though). Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/svntestbase.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/testing/svntestbase.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/testing/svntestbase.py Tue Jul 29 15:28:45 2008 @@ -73,7 +73,7 @@ def setup_method(self, meth): bn = meth.func_name - for x in 'test_remove', 'test_move': + for x in 'test_remove', 'test_move', 'test_status_deleted': if bn.startswith(x): self._savedrepowc = save_repowc() Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py Tue Jul 29 15:28:45 2008 @@ -141,6 +141,67 @@ finally: self.root.revert(rec=1) + def test_status_ignored(self): + try: + d = self.root.join('sampledir') + p = py.path.local(d).join('ignoredfile') + p.ensure(file=True) + s = d.status() + assert [x.basename for x in s.unknown] == ['ignoredfile'] + assert [x.basename for x in s.ignored] == [] + d.propset('svn:ignore', 'ignoredfile') + s = d.status() + assert [x.basename for x in s.unknown] == [] + assert [x.basename for x in s.ignored] == ['ignoredfile'] + finally: + self.root.revert(rec=1) + + def test_status_conflict(self): + repo, wc = getrepowc('conflicttestrepo', 'conflictwc') + wccopy = py.path.svnwc(py.test.ensuretemp('conflictwccopy')) + wccopy.checkout(wc.url) + p = wc.ensure('samplefile', file=1) + p.write('foo') + wc.commit('added samplefile') + wccopy.update() + assert wccopy.join('samplefile').check() + p.write('bar') + wc.commit('wrote some data') + wccopy.join('samplefile').write('baz') + wccopy.update() + s = wccopy.status() + assert [x.basename for x in s.conflict] == ['samplefile'] + + def test_status_external(self): + otherrepo, otherwc = getrepowc('externalrepo', 'externalwc') + d = self.root.ensure('sampledir', dir=1) + try: + d.remove() + d.add() + d.update() + d.propset('svn:externals', 'otherwc %s' % (otherwc.url,)) + d.update() + s = d.status() + assert [x.basename for x in s.external] == ['otherwc'] + finally: + self.root.revert(rec=1) + + def test_status_deleted(self): + d = self.root.ensure('sampledir', dir=1) + d.remove() + d.add() + self.root.commit() + d.ensure('deletefile', dir=0) + d.commit() + s = d.status() + assert 'deletefile' in [x.basename for x in s.unchanged] + assert not s.deleted + p = d.join('deletefile') + p.remove() + s = d.status() + assert 'deletefile' not in s.unchanged + assert [x.basename for x in s.deleted] == ['deletefile'] + def test_diff(self): p = self.root / 'anotherfile' out = p.diff(rev=2) Modified: py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Tue Jul 29 15:28:45 2008 @@ -9,6 +9,8 @@ """ import os, sys, time, re, calendar +from xml.dom import minidom +from xml.parsers.expat import ExpatError import py from py.__.path import common from py.__.path.svn import cache @@ -255,7 +257,7 @@ else: updates = '' - cmd = 'status -v %s %s %s' % (updates, rec, externals) + cmd = 'status -v --no-ignore %s %s %s' % (updates, rec, externals) out = self._authsvn(cmd) rootstatus = WCStatus(self).fromstring(out, self) return rootstatus From guido at codespeak.net Tue Jul 29 16:03:45 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 29 Jul 2008 16:03:45 +0200 (CEST) Subject: [py-svn] r56845 - in py/branch/guido-svnwc-xml-status/py/path/svn: . testing Message-ID: <20080729140345.1179D169E98@codespeak.net> Author: guido Date: Tue Jul 29 16:03:44 2008 New Revision: 56845 Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Log: Fixed broken recursive status reporting on externals. Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py Tue Jul 29 16:03:44 2008 @@ -183,6 +183,10 @@ d.update() s = d.status() assert [x.basename for x in s.external] == ['otherwc'] + assert 'otherwc' not in [x.basename for x in s.unchanged] + s = d.status(rec=1) + assert [x.basename for x in s.external] == ['otherwc'] + assert 'otherwc' in [x.basename for x in s.unchanged] finally: self.root.revert(rec=1) Modified: py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Tue Jul 29 16:03:44 2008 @@ -575,6 +575,13 @@ if line.lower().find('against revision:')!=-1: update_rev = int(rest.split(':')[1].strip()) continue + if line.lower().find('status on external') > -1: + # XXX not sure what to do here... perhaps we want to + # store some state instead of just continuing, as right + # now it makes the top-level external get added twice + # (once as external, once as 'normal' unchanged item) + # because of the way SVN presents external items + continue # keep trying raise ValueError, "could not parse line %r" % line else: @@ -618,6 +625,7 @@ continue return rootstatus + class InfoSvnWCCommand: def __init__(self, output): # Path: test From guido at codespeak.net Tue Jul 29 22:57:03 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 29 Jul 2008 22:57:03 +0200 (CEST) Subject: [py-svn] r56850 - py/branch/guido-svnwc-xml-status/py/path/svn Message-ID: <20080729205703.2404D2A00E2@codespeak.net> Author: guido Date: Tue Jul 29 22:57:00 2008 New Revision: 56850 Modified: py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Log: Added some code that uses the XML output (--xml option) from 'svn st' to generate a status report. Currently all the tests pass with the XML version... Modified: py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Tue Jul 29 22:57:00 2008 @@ -257,9 +257,17 @@ else: updates = '' - cmd = 'status -v --no-ignore %s %s %s' % (updates, rec, externals) - out = self._authsvn(cmd) - rootstatus = WCStatus(self).fromstring(out, self) + try: + cmd = 'status -v --xml --no-ignore %s %s %s' % ( + updates, rec, externals) + out = self._authsvn(cmd) + except py.process.cmdexec.Error: + cmd = 'status -v --no-ignore %s %s %s' % ( + updates, rec, externals) + out = self._authsvn(cmd) + rootstatus = WCStatus(self).fromstring(out, self) + else: + rootstatus = XMLWCStatus(self).fromstring(out, self) return rootstatus def diff(self, rev=None): @@ -626,6 +634,105 @@ return rootstatus +class XMLWCStatus(WCStatus): + @staticmethod + def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): + """ parse 'data' (XML string as outputted by svn st) into a status obj + """ + # XXX for externals, the path is shown twice: once + # with external information, and once with full info as if + # the item was a normal non-external... the current way of + # dealing with this issue is by ignoring it - this does make + # externals appear as external items as well as 'normal', + # unchanged ones in the status object so this is far from ideal + rootstatus = WCStatus(rootwcpath, rev, modrev, author) + update_rev = None + try: + doc = minidom.parseString(data) + except ExpatError, e: + raise ValueError(str(e)) + urevels = doc.getElementsByTagName('against') + if urevels: + rootstatus.update_rev = urevels[-1].getAttribute('revision') + for entryel in doc.getElementsByTagName('entry'): + path = entryel.getAttribute('path') + statusel = entryel.getElementsByTagName('wc-status')[0] + itemstatus = statusel.getAttribute('item') + + if itemstatus == 'unversioned': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.unknown.append(wcpath) + continue + elif itemstatus == 'external': + wcpath = rootwcpath.__class__( + rootwcpath.localpath.join(path, abs=1), + auth=rootwcpath.auth) + rootstatus.external.append(wcpath) + continue + elif itemstatus == 'ignored': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.ignored.append(wcpath) + continue + + rev = statusel.getAttribute('revision') + if itemstatus == 'added' or itemstatus == 'none': + rev = '0' + modrev = '?' + author = '?' + date = '' + else: + print entryel.toxml() + commitel = entryel.getElementsByTagName('commit')[0] + if commitel: + modrev = commitel.getAttribute('revision') + author = '' + for c in commitel.getElementsByTagName('author')[0]\ + .childNodes: + author += c.nodeValue + date = '' + for c in commitel.getElementsByTagName('date')[0]\ + .childNodes: + date += c.nodeValue + + wcpath = rootwcpath.join(path, abs=1) + + assert itemstatus != 'modified' or wcpath.check(file=1), ( + 'did\'t expect a directory with changed content here') + + itemattrname = { + 'normal': 'unchanged', + 'unversioned': 'unknown', + 'conflicted': 'conflict', + 'none': 'added', + }.get(itemstatus, itemstatus) + + attr = getattr(rootstatus, itemattrname) + attr.append(wcpath) + + propsstatus = statusel.getAttribute('props') + if propsstatus not in ('none', 'normal'): + rootstatus.prop_modified.append(wcpath) + + if wcpath == rootwcpath: + rootstatus.rev = rev + rootstatus.modrev = modrev + rootstatus.author = author + rootstatus.date = date + + # handle repos-status element (remote info) + rstatusels = entryel.getElementsByTagName('repos-status') + if rstatusels: + rstatusel = rstatusels[0] + ritemstatus = rstatusel.getAttribute('item') + if ritemstatus in ('added', 'modified'): + rootstatus.update_available.append(wcpath) + + lockels = entryel.getElementsByTagName('lock') + if len(lockels): + rootstatus.locked.append(wcpath) + + return rootstatus + class InfoSvnWCCommand: def __init__(self, output): # Path: test