← Back to team overview

testtools-dev team mailing list archive

[Merge] lp:~jml/testtools/build-scripts into lp:testtools

 

Jonathan Lange has proposed merging lp:~jml/testtools/build-scripts into lp:testtools.

Requested reviews:
  testtools developers (testtools-dev)

For more details, see:
https://code.launchpad.net/~jml/testtools/build-scripts/+merge/69372

Adds a 'scripts' directory. Moves the lp_release script into that.

Adds a script 'all-pythons', that will run the testtools tests against all Python versions that are supported. Missing Pythons are treated as skips, import errors (or indeed any error to stderr) is treated as an error.

Bundled in this are several fixes for testtools running against older versions of Python, as well as a couple of fixes related to the tests being too environment specific. 

Sadly, the tests fail in Python 3 because fixtures doesn't support it.

Can confirm this works on Jenkins.

Aim is to have all-pythons be default testr runner.


-- 
https://code.launchpad.net/~jml/testtools/build-scripts/+merge/69372
Your team testtools developers is requested to review the proposed merge of lp:~jml/testtools/build-scripts into lp:testtools.
=== modified file 'Makefile'
--- Makefile	2011-07-06 23:07:10 +0000
+++ Makefile	2011-07-26 22:31:22 +0000
@@ -22,7 +22,7 @@
 
 release:
 	./setup.py sdist upload --sign
-	$(PYTHON) _lp_release.py
+	$(PYTHON) scripts/_lp_release.py
 
 snapshot: prerelease
 	./setup.py sdist

=== added directory 'scripts'
=== added file 'scripts/README'
--- scripts/README	1970-01-01 00:00:00 +0000
+++ scripts/README	2011-07-26 22:31:22 +0000
@@ -0,0 +1,3 @@
+These are scripts to help with building, maintaining and releasing testtools.
+
+There is little here for anyone except a testtools contributor.

=== renamed file '_lp_release.py' => 'scripts/_lp_release.py'
--- _lp_release.py	2011-07-01 17:15:45 +0000
+++ scripts/_lp_release.py	2011-07-26 22:31:22 +0000
@@ -10,10 +10,10 @@
  5. Create a new 'next' milestone
  6. Mark all "Fix committed" bugs in the milestone as "Fix released"
 
-Assumes that NEWS is in the same directory, that the release sections are
+Assumes that NEWS is in the parent directory, that the release sections are
 underlined with '~' and the subsections are underlined with '-'.
 
-Assumes that this file is in the top-level of a testtools tree that has
+Assumes that this file is in the 'scripts' directory a testtools tree that has
 already had a tarball built and uploaded with 'python setup.py sdist upload
 --sign'.
 """
@@ -71,7 +71,9 @@
 
 def get_path(relpath):
     """Get the absolute path for something relative to this file."""
-    return os.path.abspath(os.path.join(os.path.dirname(__file__), relpath))
+    return os.path.abspath(
+        os.path.join(
+            os.path.dirname(os.path.dirname(__file__)), relpath))
 
 
 def assign_fix_committed_to_next(testtools, next_milestone):

=== added file 'scripts/all-pythons'
--- scripts/all-pythons	1970-01-01 00:00:00 +0000
+++ scripts/all-pythons	2011-07-26 22:31:22 +0000
@@ -0,0 +1,90 @@
+#!/usr/bin/python
+
+"""Run the testtools test suite for all supported Pythons.
+
+Prints output as a subunit test suite. If anything goes to stderr, that is
+treated as a test error. If a Python is not available, then it is skipped.
+"""
+
+from datetime import datetime
+import os
+import subprocess
+import sys
+
+import subunit
+from subunit import (
+    iso8601,
+    _make_stream_binary,
+    TestProtocolClient,
+    TestProtocolServer,
+    )
+from testtools import (
+    PlaceHolder,
+    TestCase,
+    )
+from testtools.compat import BytesIO
+from testtools.content import text_content
+
+
+ROOT = os.path.dirname(os.path.dirname(__file__))
+
+
+def run_for_python(version, result):
+    # XXX: This could probably be broken up and put into subunit.
+    python = 'python%s' % (version,)
+    # XXX: Correct API, but subunit doesn't support it. :(
+    # result.tags(set(python), set())
+    result.time(now())
+    test = PlaceHolder(''.join(c for c in python if c != '.'))
+    process = subprocess.Popen(
+        '%s -c pass' % (python,), shell=True,
+        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    process.communicate()
+
+    if process.returncode:
+        result.startTest(test)
+        result.addSkip(test, reason='%s not available' % (python,))
+        result.stopTest(test)
+        return
+
+    env = os.environ.copy()
+    if env.get('PYTHONPATH', None):
+        env['PYTHONPATH'] = os.pathsep.join([ROOT, env['PYTHONPATH']])
+    else:
+        env['PYTHONPATH'] = ROOT
+    result.time(now())
+    protocol = TestProtocolServer(result)
+    subunit_path = os.path.join(os.path.dirname(subunit.__file__), 'run.py')
+    cmd = [
+        python,
+        '-W', 'ignore:Module testtools was already imported',
+        subunit_path, 'testtools.tests.test_suite']
+    process = subprocess.Popen(
+        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+    _make_stream_binary(process.stdout)
+    _make_stream_binary(process.stderr)
+    # XXX: This buffers everything. Bad for memory, bad for getting progress
+    # on jenkins.
+    output, error = process.communicate()
+    protocol.readFrom(BytesIO(output))
+    if error:
+        result.startTest(test)
+        result.addError(test, details={
+            'stderr': text_content(error),
+           })
+        result.stopTest(test)
+    result.time(now())
+    # XXX: Correct API, but subunit doesn't support it. :(
+    #result.tags(set(), set(python))
+
+
+def now():
+    return datetime.utcnow().replace(tzinfo=iso8601.Utc())
+
+
+
+if __name__ == '__main__':
+    sys.path.append(ROOT)
+    result = TestProtocolClient(sys.stdout)
+    for version in '2.4 2.5 2.6 2.7 3.0 3.1 3.2'.split():
+        run_for_python(version, result)

=== modified file 'testtools/tests/test_helpers.py'
--- testtools/tests/test_helpers.py	2011-07-20 12:50:43 +0000
+++ testtools/tests/test_helpers.py	2011-07-26 22:31:22 +0000
@@ -235,8 +235,11 @@
     def test_fixture(self):
         current_state = is_stack_hidden()
         fixture = StackHidingFixture(not current_state)
-        with fixture:
+        fixture.setUp()
+        try:
             self.assertThat(self.modules, StackHidden(not current_state))
+        finally:
+            fixture.cleanUp()
         self.assertThat(self.modules, StackHidden(current_state))
 
 

=== modified file 'testtools/tests/test_matchers.py'
--- testtools/tests/test_matchers.py	2011-07-21 09:39:52 +0000
+++ testtools/tests/test_matchers.py	2011-07-26 22:31:22 +0000
@@ -260,8 +260,8 @@
     describe_examples = [
         # XXX: This is kind of a crappy message. Need to change
         # AfterPreproccessing.
-        ("'bar' does not match 'fo.': after <type 'str'> on ValueError('bar',)",
-         error_bar, MatchesException(ValueError, "fo.")),
+        ("'bar' does not match 'fo.': after <type 'str'> on %r"
+         % (error_bar[1],), error_bar, MatchesException(ValueError, "fo.")),
         ]
 
 
@@ -280,7 +280,7 @@
          MatchesException(Exception, Equals('foo')))
         ]
     describe_examples = [
-        ("5 != ValueError('bar',)",
+        ("5 != %r" % (error_bar[1],),
          error_bar, MatchesException(ValueError, Equals(5))),
         ]
 

=== modified file 'testtools/tests/test_testresult.py'
--- testtools/tests/test_testresult.py	2011-07-20 21:02:36 +0000
+++ testtools/tests/test_testresult.py	2011-07-26 22:31:22 +0000
@@ -383,11 +383,11 @@
             result.errors[0][1],
             DocTestMatches(
                 'Traceback (most recent call last):\n'
-                '  File "testtools...runtest.py", line ..., in _run_user\n'
+                '  File "...testtools...runtest.py", line ..., in _run_user\n'
                 '    return fn(*args, **kwargs)\n'
-                '  File "testtools...testcase.py", line ..., in _run_test_method\n'
+                '  File "...testtools...testcase.py", line ..., in _run_test_method\n'
                 '    return self._get_test_method()()\n'
-                '  File "testtools...tests...test_testresult.py", line ..., in error\n'
+                '  File "...testtools...tests...test_testresult.py", line ..., in error\n'
                 '    1/0\n'
                 'ZeroDivisionError: ...\n',
                 doctest.ELLIPSIS | doctest.REPORT_UDIFF))
@@ -395,13 +395,17 @@
     def test_traceback_formatting_with_stack_hidden(self):
         result = self.makeResult()
         test = make_erroring_test()
-        with StackHidingFixture(True):
+        fixture = StackHidingFixture(True)
+        fixture.setUp()
+        try:
             test.run(result)
+        finally:
+            fixture.cleanUp()
         self.assertThat(
             result.errors[0][1],
             DocTestMatches(
                 'Traceback (most recent call last):\n'
-                '  File "testtools/tests/test_testresult.py", line ..., in error\n'
+                '  File "...testtools...tests...test_testresult.py", line ..., in error\n'
                 '    1/0\n'
                 'ZeroDivisionError: ...\n',
                 doctest.ELLIPSIS))
@@ -595,13 +599,17 @@
             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
 
     def test_stopTestRun_shows_details(self):
-        with StackHidingFixture(True):
+        fixture = StackHidingFixture(True)
+        fixture.setUp()
+        try:
             self.result.startTestRun()
             make_erroring_test().run(self.result)
             make_unexpectedly_successful_test().run(self.result)
             make_failing_test().run(self.result)
             self.reset_output()
             self.result.stopTestRun()
+        finally:
+            fixture.cleanUp()
         self.assertThat(self.getvalue(),
             DocTestMatches("""...======================================================================
 ERROR: testtools.tests.test_testresult.Test.error

=== modified file 'testtools/testsuite.py'
--- testtools/testsuite.py	2011-07-20 12:01:05 +0000
+++ testtools/testsuite.py	2011-07-26 22:31:22 +0000
@@ -94,5 +94,8 @@
         self._fixture = fixture
 
     def run(self, result):
-        with self._fixture:
+        self._fixture.setUp()
+        try:
             super(FixtureSuite, self).run(result)
+        finally:
+            self._fixture.cleanUp()


Follow ups