[SCM] Samba Shared Repository - branch master updated

Jelmer Vernooij jelmer at samba.org
Fri Jan 15 23:57:58 MST 2010


The branch, master has been updated
       via  2ec5792... subunit/testtools: Include newer version.
      from  28577aa... Import testtools as well, required for subunit.

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 2ec5792a4ba0cefa079a6d7e1b0ec2472151e794
Author: Jelmer Vernooij <jelmer at samba.org>
Date:   Sat Jan 16 19:56:21 2010 +1300

    subunit/testtools: Include newer version.

-----------------------------------------------------------------------

Summary of changes:
 lib/subunit/python/subunit/__init__.py             |   17 ++++-
 lib/subunit/python/subunit/run.py                  |    5 +-
 .../python/subunit/tests/test_test_protocol.py     |    4 +
 lib/subunit/python/testtools/content.py            |    2 +-
 lib/subunit/python/testtools/content_type.py       |    2 +-
 lib/subunit/python/testtools/matchers.py           |   82 ++++++++++++++++++++
 lib/subunit/python/testtools/testcase.py           |   50 +++++++++---
 lib/subunit/python/testtools/tests/__init__.py     |    2 +
 .../python/testtools/tests/test_matchers.py        |   45 +++++++++++
 .../python/testtools/tests/test_testtools.py       |   16 ++++-
 lib/subunit/python/testtools/utils.py              |    2 +-
 11 files changed, 205 insertions(+), 22 deletions(-)


Changeset truncated at 500 lines:

diff --git a/lib/subunit/python/subunit/__init__.py b/lib/subunit/python/subunit/__init__.py
index 6e8df90..6b65ae4 100644
--- a/lib/subunit/python/subunit/__init__.py
+++ b/lib/subunit/python/subunit/__init__.py
@@ -213,10 +213,10 @@ class _ParserState(object):
     def lineReceived(self, line):
         """a line has been received."""
         parts = line.split(None, 1)
-        if len(parts) == 2:
+        if len(parts) == 2 and line.startswith(parts[0]):
             cmd, rest = parts
             offset = len(cmd) + 1
-            cmd = cmd.strip(':')
+            cmd = cmd.rstrip(':')
             if cmd in ('test', 'testing'):
                 self.startTest(offset, line)
             elif cmd == 'error':
@@ -1111,3 +1111,16 @@ class TestResultStats(unittest.TestResult):
     def wasSuccessful(self):
         """Tells whether or not this result was a success"""
         return self.failed_tests == 0
+
+
+def get_default_formatter():
+    """Obtain the default formatter to write to.
+    
+    :return: A file-like object.
+    """
+    formatter = os.getenv("SUBUNIT_FORMATTER")
+    if formatter:
+        return os.popen(formatter, "w")
+    else:
+        return sys.stdout
+
diff --git a/lib/subunit/python/subunit/run.py b/lib/subunit/python/subunit/run.py
index 2b90791..01c0b0e 100755
--- a/lib/subunit/python/subunit/run.py
+++ b/lib/subunit/python/subunit/run.py
@@ -22,7 +22,7 @@
 
 import sys
 
-from subunit import TestProtocolClient
+from subunit import TestProtocolClient, get_default_formatter
 
 
 class SubunitTestRunner(object):
@@ -41,6 +41,7 @@ if __name__ == '__main__':
     from unittest import TestProgram
     parser = optparse.OptionParser(__doc__)
     args = parser.parse_args()[1]
-    runner = SubunitTestRunner()
+    stream = get_default_formatter()
+    runner = SubunitTestRunner(stream)
     program = TestProgram(module=None, argv=[sys.argv[0]] + args,
                           testRunner=runner)
diff --git a/lib/subunit/python/subunit/tests/test_test_protocol.py b/lib/subunit/python/subunit/tests/test_test_protocol.py
index 9e9db18..f10380b 100644
--- a/lib/subunit/python/subunit/tests/test_test_protocol.py
+++ b/lib/subunit/python/subunit/tests/test_test_protocol.py
@@ -124,6 +124,10 @@ class TestTestProtocolServerStartTest(unittest.TestCase):
         self.assertEqual(self.client._events,
             [('startTest', subunit.RemotedTestCase("old mcdonald"))])
 
+    def test_indented_test_colon_ignored(self):
+        self.protocol.lineReceived(" test: old mcdonald\n")
+        self.assertEqual([], self.client._events)
+
     def test_start_testing_colon(self):
         self.protocol.lineReceived("testing: old mcdonald\n")
         self.assertEqual(self.client._events,
diff --git a/lib/subunit/python/testtools/content.py b/lib/subunit/python/testtools/content.py
index 00c7823..353e3f0 100644
--- a/lib/subunit/python/testtools/content.py
+++ b/lib/subunit/python/testtools/content.py
@@ -44,7 +44,7 @@ class Content(object):
         no charset parameter is present in the MIME type. (This is somewhat
         arbitrary, but consistent with RFC2617 3.7.1).
 
-        :raises: ValueError If the content type is not text/*.
+        :raises ValueError: If the content type is not text/\*.
         """
         if self.content_type.type != "text":
             raise ValueError("Not a text type %r" % self.content_type)
diff --git a/lib/subunit/python/testtools/content_type.py b/lib/subunit/python/testtools/content_type.py
index e70fa76..aded81b 100644
--- a/lib/subunit/python/testtools/content_type.py
+++ b/lib/subunit/python/testtools/content_type.py
@@ -9,7 +9,7 @@ class ContentType(object):
     :ivar type: The primary type, e.g. "text" or "application"
     :ivar subtype: The subtype, e.g. "plain" or "octet-stream"
     :ivar parameters: A dict of additional parameters specific to the
-    content type.
+        content type.
     """
 
     def __init__(self, primary_type, sub_type, parameters=None):
diff --git a/lib/subunit/python/testtools/matchers.py b/lib/subunit/python/testtools/matchers.py
index 947ef60..244dace 100644
--- a/lib/subunit/python/testtools/matchers.py
+++ b/lib/subunit/python/testtools/matchers.py
@@ -14,7 +14,10 @@ __metaclass__ = type
 __all__ = [
     'DocTestMatches',
     'Equals',
+    'MatchesAll',
     'MatchesAny',
+    'NotEquals',
+    'Not',
     ]
 
 import doctest
@@ -135,6 +138,36 @@ class EqualsMismatch:
         return "%r != %r" % (self.expected, self.other)
 
 
+class NotEquals:
+    """Matches if the items are not equal.
+
+    In most cases, this is equivalent to `Not(Equals(foo))`. The difference
+    only matters when testing `__ne__` implementations.
+    """
+
+    def __init__(self, expected):
+        self.expected = expected
+
+    def __str__(self):
+        return 'NotEquals(%r)' % (self.expected,)
+
+    def match(self, other):
+        if self.expected != other:
+            return None
+        return NotEqualsMismatch(self.expected, other)
+
+
+class NotEqualsMismatch:
+    """Two things are the same."""
+
+    def __init__(self, expected, other):
+        self.expected = expected
+        self.other = other
+
+    def describe(self):
+        return '%r == %r' % (self.expected, self.other)
+
+
 class MatchesAny:
     """Matches if any of the matchers it is created with match."""
 
@@ -155,6 +188,27 @@ class MatchesAny:
             str(matcher) for matcher in self.matchers])
 
 
+class MatchesAll:
+    """Matches if all of the matchers it is created with match."""
+
+    def __init__(self, *matchers):
+        self.matchers = matchers
+
+    def __str__(self):
+        return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
+
+    def match(self, matchee):
+        results = []
+        for matcher in self.matchers:
+            mismatch = matcher.match(matchee)
+            if mismatch is not None:
+                results.append(mismatch)
+        if results:
+            return MismatchesAll(results)
+        else:
+            return None
+
+
 class MismatchesAll:
     """A mismatch with many child mismatches."""
 
@@ -167,3 +221,31 @@ class MismatchesAll:
             descriptions.append(mismatch.describe())
         descriptions.append("]\n")
         return '\n'.join(descriptions)
+
+
+class Not:
+    """Inverts a matcher."""
+
+    def __init__(self, matcher):
+        self.matcher = matcher
+
+    def __str__(self):
+        return 'Not(%s)' % (self.matcher,)
+
+    def match(self, other):
+        mismatch = self.matcher.match(other)
+        if mismatch is None:
+            return MatchedUnexpectedly(self.matcher, other)
+        else:
+            return None
+
+
+class MatchedUnexpectedly:
+    """A thing matched when it wasn't supposed to."""
+
+    def __init__(self, matcher, other):
+        self.matcher = matcher
+        self.other = other
+
+    def describe(self):
+        return "%r matches %s" % (self.other, self.matcher)
diff --git a/lib/subunit/python/testtools/testcase.py b/lib/subunit/python/testtools/testcase.py
index a1d822e..fd70141 100644
--- a/lib/subunit/python/testtools/testcase.py
+++ b/lib/subunit/python/testtools/testcase.py
@@ -203,15 +203,26 @@ class TestCase(unittest.TestCase):
         self.assertTrue(
             needle in haystack, '%r not in %r' % (needle, haystack))
 
-    def assertIs(self, expected, observed):
-        """Assert that `expected` is `observed`."""
+    def assertIs(self, expected, observed, message=''):
+        """Assert that 'expected' is 'observed'.
+
+        :param expected: The expected value.
+        :param observed: The observed value.
+        :param message: An optional message describing the error.
+        """
+        if message:
+            message = ': ' + message
         self.assertTrue(
-            expected is observed, '%r is not %r' % (expected, observed))
+            expected is observed,
+            '%r is not %r%s' % (expected, observed, message))
 
-    def assertIsNot(self, expected, observed):
-        """Assert that `expected` is not `observed`."""
+    def assertIsNot(self, expected, observed, message=''):
+        """Assert that 'expected' is not 'observed'."""
+        if message:
+            message = ': ' + message
         self.assertTrue(
-            expected is not observed, '%r is %r' % (expected, observed))
+            expected is not observed,
+            '%r is %r%s' % (expected, observed, message))
 
     def assertNotIn(self, needle, haystack):
         """Assert that needle is not in haystack."""
@@ -358,7 +369,11 @@ class TestCase(unittest.TestCase):
         """
         self.setUp()
         if not self.__setup_called:
-            raise ValueError("setUp was not called")
+            raise ValueError(
+                "TestCase.setUp was not called. Have you upcalled all the "
+                "way up the hierarchy from your setUp? e.g. Call "
+                "super(%s, self).setUp() from your setUp()."
+                % self.__class__.__name__)
 
     def _run_teardown(self, result):
         """Run the tearDown function for this test.
@@ -369,7 +384,11 @@ class TestCase(unittest.TestCase):
         """
         self.tearDown()
         if not self.__teardown_called:
-            raise ValueError("teardown was not called")
+            raise ValueError(
+                "TestCase.tearDown was not called. Have you upcalled all the "
+                "way up the hierarchy from your tearDown? e.g. Call "
+                "super(%s, self).tearDown() from your tearDown()."
+                % self.__class__.__name__)
 
     def _run_test_method(self, result):
         """Run the test method for this test.
@@ -395,14 +414,19 @@ class TestCase(unittest.TestCase):
         self.__teardown_called = True
 
 
-# Python 2.4 did not know how to deep copy functions.
-if types.FunctionType not in copy._deepcopy_dispatch:
-    copy._deepcopy_dispatch[types.FunctionType] = copy._deepcopy_atomic
+# Python 2.4 did not know how to copy functions.
+if types.FunctionType not in copy._copy_dispatch:
+    copy._copy_dispatch[types.FunctionType] = copy._copy_immutable
+
 
 
 def clone_test_with_new_id(test, new_id):
-    """Copy a TestCase, and give the copied test a new id."""
-    newTest = copy.deepcopy(test)
+    """Copy a TestCase, and give the copied test a new id.
+    
+    This is only expected to be used on tests that have been constructed but
+    not executed.
+    """
+    newTest = copy.copy(test)
     newTest.id = lambda: new_id
     return newTest
 
diff --git a/lib/subunit/python/testtools/tests/__init__.py b/lib/subunit/python/testtools/tests/__init__.py
index e1d1148..2cceba9 100644
--- a/lib/subunit/python/testtools/tests/__init__.py
+++ b/lib/subunit/python/testtools/tests/__init__.py
@@ -1,3 +1,5 @@
+"""Tests for testtools itself."""
+
 # See README for copyright and licensing details.
 
 import unittest
diff --git a/lib/subunit/python/testtools/tests/test_matchers.py b/lib/subunit/python/testtools/tests/test_matchers.py
index a9f4b24..d5fd8ba 100644
--- a/lib/subunit/python/testtools/tests/test_matchers.py
+++ b/lib/subunit/python/testtools/tests/test_matchers.py
@@ -12,6 +12,9 @@ from testtools.matchers import (
     Equals,
     DocTestMatches,
     MatchesAny,
+    MatchesAll,
+    Not,
+    NotEquals,
     )
 
 
@@ -81,6 +84,31 @@ class TestEqualsInterface(TestCase, TestMatchersInterface):
     describe_examples = [("1 != 2", 2, Equals(1))]
 
 
+class TestNotEqualsInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = NotEquals(1)
+    matches_matches = [2]
+    matches_mismatches = [1]
+
+    str_examples = [
+        ("NotEquals(1)", NotEquals(1)), ("NotEquals('1')", NotEquals('1'))]
+
+    describe_examples = [("1 == 1", 1, NotEquals(1))]
+
+
+class TestNotInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = Not(Equals(1))
+    matches_matches = [2]
+    matches_mismatches = [1]
+
+    str_examples = [
+        ("Not(Equals(1))", Not(Equals(1))),
+        ("Not(Equals('1'))", Not(Equals('1')))]
+
+    describe_examples = [('1 matches Equals(1)', 1, Not(Equals(1)))]
+
+
 class TestMatchersAnyInterface(TestCase, TestMatchersInterface):
 
     matches_matcher = MatchesAny(DocTestMatches("1"), DocTestMatches("2"))
@@ -108,6 +136,23 @@ Got:
         "3", MatchesAny(DocTestMatches("1"), DocTestMatches("2")))]
 
 
+class TestMatchesAllInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesAll(NotEquals(1), NotEquals(2))
+    matches_matches = [3, 4]
+    matches_mismatches = [1, 2]
+
+    str_examples = [
+        ("MatchesAll(NotEquals(1), NotEquals(2))",
+         MatchesAll(NotEquals(1), NotEquals(2)))]
+
+    describe_examples = [("""Differences: [
+1 == 1
+]
+""",
+                          1, MatchesAll(NotEquals(1), NotEquals(2)))]
+
+
 def test_suite():
     from unittest import TestLoader
     return TestLoader().loadTestsFromName(__name__)
diff --git a/lib/subunit/python/testtools/tests/test_testtools.py b/lib/subunit/python/testtools/tests/test_testtools.py
index 8cd90de..af1fd79 100644
--- a/lib/subunit/python/testtools/tests/test_testtools.py
+++ b/lib/subunit/python/testtools/tests/test_testtools.py
@@ -223,6 +223,12 @@ class TestAssertions(TestCase):
         self.assertFails('None is not 42', self.assertIs, None, 42)
         self.assertFails('[42] is not [42]', self.assertIs, [42], [42])
 
+    def test_assertIs_fails_with_message(self):
+        # assertIs raises assertion errors if one object is not identical to
+        # another, and includes a user-supplied message, if it's provided.
+        self.assertFails(
+            'None is not 42: foo bar', self.assertIs, None, 42, 'foo bar')
+
     def test_assertIsNot(self):
         # assertIsNot asserts that an object is not identical to another
         # object.
@@ -238,6 +244,12 @@ class TestAssertions(TestCase):
         self.assertFails(
             '[42] is [42]', self.assertIsNot, some_list, some_list)
 
+    def test_assertIsNot_fails_with_message(self):
+        # assertIsNot raises assertion errors if one object is identical to
+        # another, and includes a user-supplied message if it's provided.
+        self.assertFails(
+            'None is None: foo bar', self.assertIsNot, None, None, "foo bar")
+
     def test_assertThat_matches_clean(self):
         class Matcher:
             def match(self, foo):
@@ -303,12 +315,12 @@ class TestAddCleanup(TestCase):
         self.assertEqual(messages, [call[0] for call in self._result_calls])
 
     def assertTestLogEqual(self, messages):
-        """Assert that the call log equals `messages`."""
+        """Assert that the call log equals 'messages'."""
         case = self._result_calls[0][1]
         self.assertEqual(messages, case._calls)
 
     def logAppender(self, message):
-        """A cleanup that appends `message` to the tests log.
+        """A cleanup that appends 'message' to the tests log.
 
         Cleanups are callables that are added to a test by addCleanup. To
         verify that our cleanups run in the right order, we add strings to a
diff --git a/lib/subunit/python/testtools/utils.py b/lib/subunit/python/testtools/utils.py
index 3255722..c0845b6 100644
--- a/lib/subunit/python/testtools/utils.py
+++ b/lib/subunit/python/testtools/utils.py
@@ -28,7 +28,7 @@ else:
 
 
 def iterate_tests(test_suite_or_case):
-    """Iterate through all of the test cases in `test_suite_or_case`."""
+    """Iterate through all of the test cases in 'test_suite_or_case'."""
     try:
         suite = iter(test_suite_or_case)
     except TypeError:


-- 
Samba Shared Repository


More information about the samba-cvs mailing list