summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_gc.py2
-rw-r--r--Lib/test/test_struct.py1
-rw-r--r--Lib/test/test_unittest.py464
-rw-r--r--Lib/unittest.py467
4 files changed, 865 insertions, 69 deletions
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index e9af550..5a35dec 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -244,7 +244,7 @@ class GCTests(unittest.TestCase):
# - the call to assertEqual somehow avoids building its args tuple
def test_get_count(self):
# Avoid future allocation of method object
- assertEqual = self.assertEqual
+ assertEqual = self._baseAssertEqual
gc.collect()
assertEqual(gc.get_count(), (0, 0, 0))
a = dict()
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index 6e35db8..1cf3484 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -227,6 +227,7 @@ class StructTest(unittest.TestCase):
BUGGY_RANGE_CHECK = "bBhHiIlL"
def __init__(self, formatpair, bytesize):
+ super(IntTester, self).__init__(methodName='test_one')
self.assertEqual(len(formatpair), 2)
self.formatpair = formatpair
for direction in "<>!=":
diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py
index b0b86be..85ebf46 100644
--- a/Lib/test/test_unittest.py
+++ b/Lib/test/test_unittest.py
@@ -6,6 +6,7 @@ Still need testing:
TestCase.{assert,fail}* methods (some are tested implicitly)
"""
+import re
from test import test_support
import unittest
from unittest import TestCase
@@ -70,21 +71,20 @@ class TestHashing(object):
def test_hash(self):
for obj_1, obj_2 in self.eq_pairs:
try:
- assert hash(obj_1) == hash(obj_2)
+ if not hash(obj_1) == hash(obj_2):
+ self.fail("%r and %r do not hash equal" % (obj_1, obj_2))
except KeyboardInterrupt:
raise
- except AssertionError:
- self.fail("%s and %s do not hash equal" % (obj_1, obj_2))
except Exception, e:
- self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e))
+ self.fail("Problem hashing %r and %r: %s" % (obj_1, obj_2, e))
for obj_1, obj_2 in self.ne_pairs:
try:
- assert hash(obj_1) != hash(obj_2)
+ if hash(obj_1) == hash(obj_2):
+ self.fail("%s and %s hash equal, but shouldn't" %
+ (obj_1, obj_2))
except KeyboardInterrupt:
raise
- except AssertionError:
- self.fail("%s and %s hash equal, but shouldn't" % (obj_1, obj_2))
except Exception, e:
self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e))
@@ -2237,39 +2237,6 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
self.failUnless(isinstance(Foo().id(), basestring))
- # "Returns a one-line description of the test, or None if no description
- # has been provided. The default implementation of this method returns
- # the first line of the test method's docstring, if available, or None."
- def test_shortDescription__no_docstring(self):
- class Foo(unittest.TestCase):
- def runTest(self):
- pass
-
- self.assertEqual(Foo().shortDescription(), None)
-
- # "Returns a one-line description of the test, or None if no description
- # has been provided. The default implementation of this method returns
- # the first line of the test method's docstring, if available, or None."
- def test_shortDescription__singleline_docstring(self):
- class Foo(unittest.TestCase):
- def runTest(self):
- "this tests foo"
- pass
-
- self.assertEqual(Foo().shortDescription(), "this tests foo")
-
- # "Returns a one-line description of the test, or None if no description
- # has been provided. The default implementation of this method returns
- # the first line of the test method's docstring, if available, or None."
- def test_shortDescription__multiline_docstring(self):
- class Foo(unittest.TestCase):
- def runTest(self):
- """this tests foo
- blah, bar and baz are also tested"""
- pass
-
- self.assertEqual(Foo().shortDescription(), "this tests foo")
-
# "If result is omitted or None, a temporary result object is created
# and used, but is not made available to the caller"
def test_run__uses_defaultTestResult(self):
@@ -2288,6 +2255,409 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
expected = ['startTest', 'test', 'addSuccess', 'stopTest']
self.assertEqual(events, expected)
+ def testShortDescriptionWithoutDocstring(self):
+ self.assertEqual(
+ self.shortDescription(),
+ 'testShortDescriptionWithoutDocstring (' + __name__ +
+ '.Test_TestCase)')
+
+ def testShortDescriptionWithOneLineDocstring(self):
+ """Tests shortDescription() for a method with a docstring."""
+ self.assertEqual(
+ self.shortDescription(),
+ ('testShortDescriptionWithOneLineDocstring '
+ '(' + __name__ + '.Test_TestCase)\n'
+ 'Tests shortDescription() for a method with a docstring.'))
+
+ def testShortDescriptionWithMultiLineDocstring(self):
+ """Tests shortDescription() for a method with a longer docstring.
+
+ This method ensures that only the first line of a docstring is
+ returned used in the short description, no matter how long the
+ whole thing is.
+ """
+ self.assertEqual(
+ self.shortDescription(),
+ ('testShortDescriptionWithMultiLineDocstring '
+ '(' + __name__ + '.Test_TestCase)\n'
+ 'Tests shortDescription() for a method with a longer '
+ 'docstring.'))
+
+
+ def testAddTypeEqualityFunc(self):
+ class SadSnake(object):
+ """Dummy class for test_addTypeEqualityFunc."""
+ s1, s2 = SadSnake(), SadSnake()
+ self.assertFalse(s1 == s2)
+ def AllSnakesCreatedEqual(a, b, msg=None):
+ return type(a) == type(b) == SadSnake
+ self.addTypeEqualityFunc(SadSnake, AllSnakesCreatedEqual)
+ self.assertEqual(s1, s2)
+ # No this doesn't clean up and remove the SadSnake equality func
+ # from this TestCase instance but since its a local nothing else
+ # will ever notice that.
+
+ def testAssertIn(self):
+ animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'}
+
+ self.assertIn('a', 'abc')
+ self.assertIn(2, [1, 2, 3])
+ self.assertIn('monkey', animals)
+
+ self.assertNotIn('d', 'abc')
+ self.assertNotIn(0, [1, 2, 3])
+ self.assertNotIn('otter', animals)
+
+ self.assertRaises(self.failureException, self.assertIn, 'x', 'abc')
+ self.assertRaises(self.failureException, self.assertIn, 4, [1, 2, 3])
+ self.assertRaises(self.failureException, self.assertIn, 'elephant',
+ animals)
+
+ self.assertRaises(self.failureException, self.assertNotIn, 'c', 'abc')
+ self.assertRaises(self.failureException, self.assertNotIn, 1, [1, 2, 3])
+ self.assertRaises(self.failureException, self.assertNotIn, 'cow',
+ animals)
+
+ def testAssertDictContainsSubset(self):
+ self.assertDictContainsSubset({}, {})
+ self.assertDictContainsSubset({}, {'a': 1})
+ self.assertDictContainsSubset({'a': 1}, {'a': 1})
+ self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2})
+ self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2})
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'a': 2}, {'a': 1},
+ '.*Mismatched values:.*')
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'c': 1}, {'a': 1},
+ '.*Missing:.*')
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'a': 1, 'c': 1},
+ {'a': 1}, '.*Missing:.*')
+
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictContainsSubset, {'a': 1, 'c': 1},
+ {'a': 1}, '.*Missing:.*Mismatched values:.*')
+
+ def testAssertEqual(self):
+ equal_pairs = [
+ ((), ()),
+ ({}, {}),
+ ([], []),
+ (set(), set()),
+ (frozenset(), frozenset())]
+ for a, b in equal_pairs:
+ # This mess of try excepts is to test the assertEqual behavior
+ # itself.
+ try:
+ self.assertEqual(a, b)
+ except self.failureException:
+ self.fail('assertEqual(%r, %r) failed' % (a, b))
+ try:
+ self.assertEqual(a, b, msg='foo')
+ except self.failureException:
+ self.fail('assertEqual(%r, %r) with msg= failed' % (a, b))
+ try:
+ self.assertEqual(a, b, 'foo')
+ except self.failureException:
+ self.fail('assertEqual(%r, %r) with third parameter failed' %
+ (a, b))
+
+ unequal_pairs = [
+ ((), []),
+ ({}, set()),
+ (set([4,1]), frozenset([4,2])),
+ (frozenset([4,5]), set([2,3])),
+ (set([3,4]), set([5,4]))]
+ for a, b in unequal_pairs:
+ self.assertRaises(self.failureException, self.assertEqual, a, b)
+ self.assertRaises(self.failureException, self.assertEqual, a, b,
+ 'foo')
+ self.assertRaises(self.failureException, self.assertEqual, a, b,
+ msg='foo')
+
+ def testEquality(self):
+ self.assertListEqual([], [])
+ self.assertTupleEqual((), ())
+ self.assertSequenceEqual([], ())
+
+ a = [0, 'a', []]
+ b = []
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertListEqual, a, b)
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertListEqual, tuple(a), tuple(b))
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertSequenceEqual, a, tuple(b))
+
+ b.extend(a)
+ self.assertListEqual(a, b)
+ self.assertTupleEqual(tuple(a), tuple(b))
+ self.assertSequenceEqual(a, tuple(b))
+ self.assertSequenceEqual(tuple(a), b)
+
+ self.assertRaises(self.failureException, self.assertListEqual,
+ a, tuple(b))
+ self.assertRaises(self.failureException, self.assertTupleEqual,
+ tuple(a), b)
+ self.assertRaises(self.failureException, self.assertListEqual, None, b)
+ self.assertRaises(self.failureException, self.assertTupleEqual, None,
+ tuple(b))
+ self.assertRaises(self.failureException, self.assertSequenceEqual,
+ None, tuple(b))
+ self.assertRaises(self.failureException, self.assertListEqual, 1, 1)
+ self.assertRaises(self.failureException, self.assertTupleEqual, 1, 1)
+ self.assertRaises(self.failureException, self.assertSequenceEqual,
+ 1, 1)
+
+ self.assertDictEqual({}, {})
+
+ c = { 'x': 1 }
+ d = {}
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictEqual, c, d)
+
+ d.update(c)
+ self.assertDictEqual(c, d)
+
+ d['x'] = 0
+ self.assertRaises(unittest.TestCase.failureException,
+ self.assertDictEqual, c, d, 'These are unequal')
+
+ self.assertRaises(self.failureException, self.assertDictEqual, None, d)
+ self.assertRaises(self.failureException, self.assertDictEqual, [], d)
+ self.assertRaises(self.failureException, self.assertDictEqual, 1, 1)
+
+ self.assertSameElements([1, 2, 3], [3, 2, 1])
+ self.assertSameElements([1, 2] + [3] * 100, [1] * 100 + [2, 3])
+ self.assertSameElements(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo'])
+ self.assertRaises(self.failureException, self.assertSameElements,
+ [10], [10, 11])
+ self.assertRaises(self.failureException, self.assertSameElements,
+ [10, 11], [10])
+
+ # Test that sequences of unhashable objects can be tested for sameness:
+ self.assertSameElements([[1, 2], [3, 4]], [[3, 4], [1, 2]])
+ self.assertSameElements([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}])
+ self.assertRaises(self.failureException, self.assertSameElements,
+ [[1]], [[2]])
+
+ def testAssertSetEqual(self):
+ set1 = set()
+ set2 = set()
+ self.assertSetEqual(set1, set2)
+
+ self.assertRaises(self.failureException, self.assertSetEqual, None, set2)
+ self.assertRaises(self.failureException, self.assertSetEqual, [], set2)
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, None)
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, [])
+
+ set1 = set(['a'])
+ set2 = set()
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ set1 = set(['a'])
+ set2 = set(['a'])
+ self.assertSetEqual(set1, set2)
+
+ set1 = set(['a'])
+ set2 = set(['a', 'b'])
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ set1 = set(['a'])
+ set2 = frozenset(['a', 'b'])
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ set1 = set(['a', 'b'])
+ set2 = frozenset(['a', 'b'])
+ self.assertSetEqual(set1, set2)
+
+ set1 = set()
+ set2 = "foo"
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+ self.assertRaises(self.failureException, self.assertSetEqual, set2, set1)
+
+ # make sure any string formatting is tuple-safe
+ set1 = set([(0, 1), (2, 3)])
+ set2 = set([(4, 5)])
+ self.assertRaises(self.failureException, self.assertSetEqual, set1, set2)
+
+ def testInequality(self):
+ # Try ints
+ self.assertGreater(2, 1)
+ self.assertGreaterEqual(2, 1)
+ self.assertGreaterEqual(1, 1)
+ self.assertLess(1, 2)
+ self.assertLessEqual(1, 2)
+ self.assertLessEqual(1, 1)
+ self.assertRaises(self.failureException, self.assertGreater, 1, 2)
+ self.assertRaises(self.failureException, self.assertGreater, 1, 1)
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 1, 2)
+ self.assertRaises(self.failureException, self.assertLess, 2, 1)
+ self.assertRaises(self.failureException, self.assertLess, 1, 1)
+ self.assertRaises(self.failureException, self.assertLessEqual, 2, 1)
+
+ # Try Floats
+ self.assertGreater(1.1, 1.0)
+ self.assertGreaterEqual(1.1, 1.0)
+ self.assertGreaterEqual(1.0, 1.0)
+ self.assertLess(1.0, 1.1)
+ self.assertLessEqual(1.0, 1.1)
+ self.assertLessEqual(1.0, 1.0)
+ self.assertRaises(self.failureException, self.assertGreater, 1.0, 1.1)
+ self.assertRaises(self.failureException, self.assertGreater, 1.0, 1.0)
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 1.0, 1.1)
+ self.assertRaises(self.failureException, self.assertLess, 1.1, 1.0)
+ self.assertRaises(self.failureException, self.assertLess, 1.0, 1.0)
+ self.assertRaises(self.failureException, self.assertLessEqual, 1.1, 1.0)
+
+ # Try Strings
+ self.assertGreater('bug', 'ant')
+ self.assertGreaterEqual('bug', 'ant')
+ self.assertGreaterEqual('ant', 'ant')
+ self.assertLess('ant', 'bug')
+ self.assertLessEqual('ant', 'bug')
+ self.assertLessEqual('ant', 'ant')
+ self.assertRaises(self.failureException, self.assertGreater, 'ant', 'bug')
+ self.assertRaises(self.failureException, self.assertGreater, 'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 'ant', 'bug')
+ self.assertRaises(self.failureException, self.assertLess, 'bug', 'ant')
+ self.assertRaises(self.failureException, self.assertLess, 'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertLessEqual, 'bug', 'ant')
+
+ # Try Unicode
+ self.assertGreater(u'bug', u'ant')
+ self.assertGreaterEqual(u'bug', u'ant')
+ self.assertGreaterEqual(u'ant', u'ant')
+ self.assertLess(u'ant', u'bug')
+ self.assertLessEqual(u'ant', u'bug')
+ self.assertLessEqual(u'ant', u'ant')
+ self.assertRaises(self.failureException, self.assertGreater, u'ant', u'bug')
+ self.assertRaises(self.failureException, self.assertGreater, u'ant', u'ant')
+ self.assertRaises(self.failureException, self.assertGreaterEqual, u'ant',
+ u'bug')
+ self.assertRaises(self.failureException, self.assertLess, u'bug', u'ant')
+ self.assertRaises(self.failureException, self.assertLess, u'ant', u'ant')
+ self.assertRaises(self.failureException, self.assertLessEqual, u'bug', u'ant')
+
+ # Try Mixed String/Unicode
+ self.assertGreater('bug', u'ant')
+ self.assertGreater(u'bug', 'ant')
+ self.assertGreaterEqual('bug', u'ant')
+ self.assertGreaterEqual(u'bug', 'ant')
+ self.assertGreaterEqual('ant', u'ant')
+ self.assertGreaterEqual(u'ant', 'ant')
+ self.assertLess('ant', u'bug')
+ self.assertLess(u'ant', 'bug')
+ self.assertLessEqual('ant', u'bug')
+ self.assertLessEqual(u'ant', 'bug')
+ self.assertLessEqual('ant', u'ant')
+ self.assertLessEqual(u'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertGreater, 'ant', u'bug')
+ self.assertRaises(self.failureException, self.assertGreater, u'ant', 'bug')
+ self.assertRaises(self.failureException, self.assertGreater, 'ant', u'ant')
+ self.assertRaises(self.failureException, self.assertGreater, u'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertGreaterEqual, 'ant',
+ u'bug')
+ self.assertRaises(self.failureException, self.assertGreaterEqual, u'ant',
+ 'bug')
+ self.assertRaises(self.failureException, self.assertLess, 'bug', u'ant')
+ self.assertRaises(self.failureException, self.assertLess, u'bug', 'ant')
+ self.assertRaises(self.failureException, self.assertLess, 'ant', u'ant')
+ self.assertRaises(self.failureException, self.assertLess, u'ant', 'ant')
+ self.assertRaises(self.failureException, self.assertLessEqual, 'bug', u'ant')
+ self.assertRaises(self.failureException, self.assertLessEqual, u'bug', 'ant')
+
+ def testAssertMultiLineEqual(self):
+ sample_text = b"""\
+http://www.python.org/doc/2.3/lib/module-unittest.html
+test case
+ A test case is the smallest unit of testing. [...]
+"""
+ revised_sample_text = b"""\
+http://www.python.org/doc/2.4.1/lib/module-unittest.html
+test case
+ A test case is the smallest unit of testing. [...] You may provide your
+ own implementation that does not subclass from TestCase, of course.
+"""
+ sample_text_error = b"""
+- http://www.python.org/doc/2.3/lib/module-unittest.html
+? ^
++ http://www.python.org/doc/2.4.1/lib/module-unittest.html
+? ^^^
+ test case
+- A test case is the smallest unit of testing. [...]
++ A test case is the smallest unit of testing. [...] You may provide your
+? +++++++++++++++++++++
++ own implementation that does not subclass from TestCase, of course.
+"""
+
+ for type_changer in (lambda x: x, lambda x: x.decode('utf8')):
+ try:
+ self.assertMultiLineEqual(type_changer(sample_text),
+ type_changer(revised_sample_text))
+ except self.failureException, e:
+ # no fair testing ourself with ourself, use assertEqual..
+ self.assertEqual(sample_text_error, str(e).encode('utf8'))
+
+ def testAssertIsNone(self):
+ self.assertIsNone(None)
+ self.assertRaises(self.failureException, self.assertIsNone, False)
+ self.assertIsNotNone('DjZoPloGears on Rails')
+ self.assertRaises(self.failureException, self.assertIsNotNone, None)
+
+ def testAssertRegexpMatches(self):
+ self.assertRegexpMatches('asdfabasdf', r'ab+')
+ self.assertRaises(self.failureException, self.assertRegexpMatches,
+ 'saaas', r'aaaa')
+
+ def testAssertRaisesRegexp(self):
+ class ExceptionMock(Exception):
+ pass
+
+ def Stub():
+ raise ExceptionMock('We expect')
+
+ self.assertRaisesRegexp(ExceptionMock, re.compile('expect$'), Stub)
+ self.assertRaisesRegexp(ExceptionMock, 'expect$', Stub)
+ self.assertRaisesRegexp(ExceptionMock, u'expect$', Stub)
+
+ def testAssertNotRaisesRegexp(self):
+ self.assertRaisesRegexp(
+ self.failureException, '^Exception not raised$',
+ self.assertRaisesRegexp, Exception, re.compile('x'),
+ lambda: None)
+ self.assertRaisesRegexp(
+ self.failureException, '^Exception not raised$',
+ self.assertRaisesRegexp, Exception, 'x',
+ lambda: None)
+ self.assertRaisesRegexp(
+ self.failureException, '^Exception not raised$',
+ self.assertRaisesRegexp, Exception, u'x',
+ lambda: None)
+
+ def testAssertRaisesRegexpMismatch(self):
+ def Stub():
+ raise Exception('Unexpected')
+
+ self.assertRaisesRegexp(
+ self.failureException,
+ r'"\^Expected\$" does not match "Unexpected"',
+ self.assertRaisesRegexp, Exception, '^Expected$',
+ Stub)
+ self.assertRaisesRegexp(
+ self.failureException,
+ r'"\^Expected\$" does not match "Unexpected"',
+ self.assertRaisesRegexp, Exception, u'^Expected$',
+ Stub)
+ self.assertRaisesRegexp(
+ self.failureException,
+ r'"\^Expected\$" does not match "Unexpected"',
+ self.assertRaisesRegexp, Exception,
+ re.compile('^Expected$'), Stub)
+
class Test_TestSkipping(TestCase):
@@ -2386,20 +2756,20 @@ class Test_Assertions(TestCase):
def test_AlmostEqual(self):
self.failUnlessAlmostEqual(1.00000001, 1.0)
self.failIfAlmostEqual(1.0000001, 1.0)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failUnlessAlmostEqual, 1.0000001, 1.0)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failIfAlmostEqual, 1.00000001, 1.0)
self.failUnlessAlmostEqual(1.1, 1.0, places=0)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failUnlessAlmostEqual, 1.1, 1.0, places=1)
self.failUnlessAlmostEqual(0, .1+.1j, places=0)
self.failIfAlmostEqual(0, .1+.1j, places=1)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failUnlessAlmostEqual, 0, .1+.1j, places=1)
- self.assertRaises(AssertionError,
+ self.assertRaises(self.failureException,
self.failIfAlmostEqual, 0, .1+.1j, places=0)
def test_assertRaises(self):
@@ -2409,7 +2779,7 @@ class Test_Assertions(TestCase):
self.assertRaises(KeyError, _raise, KeyError("key"))
try:
self.assertRaises(KeyError, lambda: None)
- except AssertionError as e:
+ except self.failureException as e:
self.assert_("KeyError not raised" in e, str(e))
else:
self.fail("assertRaises() didn't fail")
@@ -2426,7 +2796,7 @@ class Test_Assertions(TestCase):
try:
with self.assertRaises(KeyError):
pass
- except AssertionError as e:
+ except self.failureException as e:
self.assert_("KeyError not raised" in e, str(e))
else:
self.fail("assertRaises() didn't fail")
diff --git a/Lib/unittest.py b/Lib/unittest.py
index 10b68ed..0f2d235 100644
--- a/Lib/unittest.py
+++ b/Lib/unittest.py
@@ -14,11 +14,11 @@ Simple usage:
class IntegerArithmenticTestCase(unittest.TestCase):
def testAdd(self): ## test method names begin 'test*'
- self.assertEquals((1 + 2), 3)
- self.assertEquals(0 + 1, 1)
+ self.assertEqual((1 + 2), 3)
+ self.assertEqual(0 + 1, 1)
def testMultiply(self):
- self.assertEquals((0 * 10), 0)
- self.assertEquals((5 * 8), 40)
+ self.assertEqual((0 * 10), 0)
+ self.assertEqual((5 * 8), 40)
if __name__ == '__main__':
unittest.main()
@@ -45,12 +45,15 @@ AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
'''
-import time
+import difflib
+import functools
+import os
+import pprint
+import re
import sys
+import time
import traceback
-import os
import types
-import functools
##############################################################################
# Exported classes and functions
@@ -251,11 +254,13 @@ class TestResult(object):
len(self.failures))
-class AssertRaisesContext(object):
+class _AssertRaisesContext(object):
+ """A context manager used to implement TestCase.assertRaises* methods."""
- def __init__(self, expected, test_case):
+ def __init__(self, expected, test_case, expected_regexp=None):
self.expected = expected
self.failureException = test_case.failureException
+ self.expected_regex = expected_regexp
def __enter__(self):
pass
@@ -268,10 +273,20 @@ class AssertRaisesContext(object):
exc_name = str(self.expected)
raise self.failureException(
"{0} not raised".format(exc_name))
- if issubclass(exc_type, self.expected):
+ if not issubclass(exc_type, self.expected):
+ # let unexpexted exceptions pass through
+ return False
+ if self.expected_regex is None:
return True
- # Let unexpected exceptions skip through
- return False
+
+ expected_regexp = self.expected_regex
+ if isinstance(expected_regexp, basestring):
+ expected_regexp = re.compile(expected_regexp)
+ if not expected_regexp.search(str(exc_value)):
+ raise self.failureException('"%s" does not match "%s"' %
+ (expected_regexp.pattern, str(exc_value)))
+ return True
+
class TestCase(object):
@@ -315,6 +330,31 @@ class TestCase(object):
(self.__class__, methodName))
self._testMethodDoc = testMethod.__doc__
+ # Map types to custom assertEqual functions that will compare
+ # instances of said type in more detail to generate a more useful
+ # error message.
+ self.__type_equality_funcs = {}
+ self.addTypeEqualityFunc(dict, self.assertDictEqual)
+ self.addTypeEqualityFunc(list, self.assertListEqual)
+ self.addTypeEqualityFunc(tuple, self.assertTupleEqual)
+ self.addTypeEqualityFunc(set, self.assertSetEqual)
+ self.addTypeEqualityFunc(frozenset, self.assertSetEqual)
+
+ def addTypeEqualityFunc(self, typeobj, function):
+ """Add a type specific assertEqual style function to compare a type.
+
+ This method is for use by TestCase subclasses that need to register
+ their own type equality functions to provide nicer error messages.
+
+ Args:
+ typeobj: The data type to call this function on when both values
+ are of the same type in assertEqual().
+ function: The callable taking two arguments and an optional
+ msg= argument that raises self.failureException with a
+ useful error message when the two arguments are not equal.
+ """
+ self.__type_equality_funcs[typeobj] = function
+
def setUp(self):
"Hook method for setting up the test fixture before exercising it."
pass
@@ -330,14 +370,22 @@ class TestCase(object):
return TestResult()
def shortDescription(self):
- """Returns a one-line description of the test, or None if no
- description has been provided.
+ """Returns both the test method name and first line of its docstring.
+
+ If no docstring is given, only returns the method name.
- The default implementation of this method returns the first line of
- the specified test method's docstring.
+ This method overrides unittest.TestCase.shortDescription(), which
+ only returns the first line of the docstring, obscuring the name
+ of the test upon failure.
"""
- doc = self._testMethodDoc
- return doc and doc.split("\n")[0].strip() or None
+ desc = str(self)
+ doc_first_line = None
+
+ if self._testMethodDoc:
+ doc_first_line = self._testMethodDoc.split("\n")[0].strip()
+ if doc_first_line:
+ desc = '\n'.join((desc, doc_first_line))
+ return desc
def id(self):
return "%s.%s" % (_strclass(self.__class__), self._testMethodName)
@@ -443,18 +491,45 @@ class TestCase(object):
with self.failUnlessRaises(some_error_class):
do_something()
"""
- context = AssertRaisesContext(excClass, self)
+ context = _AssertRaisesContext(excClass, self)
if callableObj is None:
return context
with context:
callableObj(*args, **kwargs)
+ def _getAssertEqualityFunc(self, first, second):
+ """Get a detailed comparison function for the types of the two args.
+
+ Returns: A callable accepting (first, second, msg=None) that will
+ raise a failure exception if first != second with a useful human
+ readable error message for those types.
+ """
+ #
+ # NOTE(gregory.p.smith): I considered isinstance(first, type(second))
+ # and vice versa. I opted for the conservative approach in case
+ # subclasses are not intended to be compared in detail to their super
+ # class instances using a type equality func. This means testing
+ # subtypes won't automagically use the detailed comparison. Callers
+ # should use their type specific assertSpamEqual method to compare
+ # subclasses if the detailed comparison is desired and appropriate.
+ # See the discussion in http://bugs.python.org/issue2578.
+ #
+ if type(first) is type(second):
+ return self.__type_equality_funcs.get(type(first),
+ self._baseAssertEqual)
+ return self._baseAssertEqual
+
+ def _baseAssertEqual(self, first, second, msg=None):
+ """The default assertEqual implementation, not type specific."""
+ if not first == second:
+ raise self.failureException(msg or '%r != %r' % (first, second))
+
def failUnlessEqual(self, first, second, msg=None):
"""Fail if the two objects are unequal as determined by the '=='
operator.
"""
- if not first == second:
- raise self.failureException(msg or '%r != %r' % (first, second))
+ assertion_func = self._getAssertEqualityFunc(first, second)
+ assertion_func(first, second, msg=msg)
def failIfEqual(self, first, second, msg=None):
"""Fail if the two objects are equal as determined by the '=='
@@ -504,6 +579,356 @@ class TestCase(object):
assertFalse = failIf
+ def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None):
+ """An equality assertion for ordered sequences (like lists and tuples).
+
+ For the purposes of this function, a valid orderd sequence type is one
+ which can be indexed, has a length, and has an equality operator.
+
+ Args:
+ seq1: The first sequence to compare.
+ seq2: The second sequence to compare.
+ seq_type: The expected datatype of the sequences, or None if no
+ datatype should be enforced.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+ """
+ if seq_type != None:
+ seq_type_name = seq_type.__name__
+ if not isinstance(seq1, seq_type):
+ raise self.failureException('First sequence is not a %s: %r'
+ % (seq_type_name, seq1))
+ if not isinstance(seq2, seq_type):
+ raise self.failureException('Second sequence is not a %s: %r'
+ % (seq_type_name, seq2))
+ else:
+ seq_type_name = "sequence"
+
+ differing = None
+ try:
+ len1 = len(seq1)
+ except (TypeError, NotImplementedError):
+ differing = 'First %s has no length. Non-sequence?' % (
+ seq_type_name)
+
+ if differing is None:
+ try:
+ len2 = len(seq2)
+ except (TypeError, NotImplementedError):
+ differing = 'Second %s has no length. Non-sequence?' % (
+ seq_type_name)
+
+ if differing is None:
+ if seq1 == seq2:
+ return
+
+ for i in xrange(min(len1, len2)):
+ try:
+ item1 = seq1[i]
+ except (TypeError, IndexError, NotImplementedError):
+ differing = ('Unable to index element %d of first %s\n' %
+ (i, seq_type_name))
+ break
+
+ try:
+ item2 = seq2[i]
+ except (TypeError, IndexError, NotImplementedError):
+ differing = ('Unable to index element %d of second %s\n' %
+ (i, seq_type_name))
+ break
+
+ if item1 != item2:
+ differing = ('First differing element %d:\n%s\n%s\n' %
+ (i, item1, item2))
+ break
+ else:
+ if (len1 == len2 and seq_type is None and
+ type(seq1) != type(seq2)):
+ # The sequences are the same, but have differing types.
+ return
+ # A catch-all message for handling arbitrary user-defined
+ # sequences.
+ differing = '%ss differ:\n' % seq_type_name.capitalize()
+ if len1 > len2:
+ differing = ('First %s contains %d additional '
+ 'elements.\n' % (seq_type_name, len1 - len2))
+ try:
+ differing += ('First extra element %d:\n%s\n' %
+ (len2, seq1[len2]))
+ except (TypeError, IndexError, NotImplementedError):
+ differing += ('Unable to index element %d '
+ 'of first %s\n' % (len2, seq_type_name))
+ elif len1 < len2:
+ differing = ('Second %s contains %d additional '
+ 'elements.\n' % (seq_type_name, len2 - len1))
+ try:
+ differing += ('First extra element %d:\n%s\n' %
+ (len1, seq2[len1]))
+ except (TypeError, IndexError, NotImplementedError):
+ differing += ('Unable to index element %d '
+ 'of second %s\n' % (len1, seq_type_name))
+ if not msg:
+ msg = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(),
+ pprint.pformat(seq2).splitlines()))
+ self.fail(differing + msg)
+
+ def assertListEqual(self, list1, list2, msg=None):
+ """A list-specific equality assertion.
+
+ Args:
+ list1: The first list to compare.
+ list2: The second list to compare.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+
+ """
+ self.assertSequenceEqual(list1, list2, msg, seq_type=list)
+
+ def assertTupleEqual(self, tuple1, tuple2, msg=None):
+ """A tuple-specific equality assertion.
+
+ Args:
+ tuple1: The first tuple to compare.
+ tuple2: The second tuple to compare.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+ """
+ self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)
+
+ def assertSetEqual(self, set1, set2, msg=None):
+ """A set-specific equality assertion.
+
+ Args:
+ set1: The first set to compare.
+ set2: The second set to compare.
+ msg: Optional message to use on failure instead of a list of
+ differences.
+
+ For more general containership equality, assertSameElements will work
+ with things other than sets. This uses ducktyping to support
+ different types of sets, and is optimized for sets specifically
+ (parameters must support a difference method).
+ """
+ try:
+ difference1 = set1.difference(set2)
+ except TypeError, e:
+ self.fail('invalid type when attempting set difference: %s' % e)
+ except AttributeError, e:
+ self.fail('first argument does not support set difference: %s' % e)
+
+ try:
+ difference2 = set2.difference(set1)
+ except TypeError, e:
+ self.fail('invalid type when attempting set difference: %s' % e)
+ except AttributeError, e:
+ self.fail('second argument does not support set difference: %s' % e)
+
+ if not (difference1 or difference2):
+ return
+
+ if msg is not None:
+ self.fail(msg)
+
+ lines = []
+ if difference1:
+ lines.append('Items in the first set but not the second:')
+ for item in difference1:
+ lines.append(repr(item))
+ if difference2:
+ lines.append('Items in the second set but not the first:')
+ for item in difference2:
+ lines.append(repr(item))
+ self.fail('\n'.join(lines))
+
+ def assertIn(self, a, b, msg=None):
+ """Just like self.assert_(a in b), but with a nicer default message."""
+ if msg is None:
+ msg = '"%s" not found in "%s"' % (a, b)
+ self.assert_(a in b, msg)
+
+ def assertNotIn(self, a, b, msg=None):
+ """Just like self.assert_(a not in b), but with a nicer default message."""
+ if msg is None:
+ msg = '"%s" unexpectedly found in "%s"' % (a, b)
+ self.assert_(a not in b, msg)
+
+ def assertDictEqual(self, d1, d2, msg=None):
+ self.assert_(isinstance(d1, dict), 'First argument is not a dictionary')
+ self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary')
+
+ if d1 != d2:
+ self.fail(msg or ('\n' + '\n'.join(difflib.ndiff(
+ pprint.pformat(d1).splitlines(),
+ pprint.pformat(d2).splitlines()))))
+
+ def assertDictContainsSubset(self, expected, actual, msg=None):
+ """Checks whether actual is a superset of expected."""
+ missing = []
+ mismatched = []
+ for key, value in expected.iteritems():
+ if key not in actual:
+ missing.append(key)
+ elif value != actual[key]:
+ mismatched.append('%s, expected: %s, actual: %s' % (key, value,
+ actual[key]))
+
+ if not (missing or mismatched):
+ return
+
+ missing_msg = mismatched_msg = ''
+ if missing:
+ missing_msg = 'Missing: %s' % ','.join(missing)
+ if mismatched:
+ mismatched_msg = 'Mismatched values: %s' % ','.join(mismatched)
+
+ if msg:
+ msg = '%s: %s; %s' % (msg, missing_msg, mismatched_msg)
+ else:
+ msg = '%s; %s' % (missing_msg, mismatched_msg)
+ self.fail(msg)
+
+ def assertSameElements(self, expected_seq, actual_seq, msg=None):
+ """An unordered sequence specific comparison.
+
+ Raises with an error message listing which elements of expected_seq
+ are missing from actual_seq and vice versa if any.
+ """
+ try:
+ expected = set(expected_seq)
+ actual = set(actual_seq)
+ missing = list(expected.difference(actual))
+ unexpected = list(actual.difference(expected))
+ missing.sort()
+ unexpected.sort()
+ except TypeError:
+ # Fall back to slower list-compare if any of the objects are
+ # not hashable.
+ expected = list(expected_seq)
+ actual = list(actual_seq)
+ expected.sort()
+ actual.sort()
+ missing, unexpected = _SortedListDifference(expected, actual)
+ errors = []
+ if missing:
+ errors.append('Expected, but missing:\n %r\n' % missing)
+ if unexpected:
+ errors.append('Unexpected, but present:\n %r\n' % unexpected)
+ if errors:
+ self.fail(msg or ''.join(errors))
+
+ def assertMultiLineEqual(self, first, second, msg=None):
+ """Assert that two multi-line strings are equal."""
+ self.assert_(isinstance(first, types.StringTypes), (
+ 'First argument is not a string'))
+ self.assert_(isinstance(second, types.StringTypes), (
+ 'Second argument is not a string'))
+
+ if first != second:
+ raise self.failureException(
+ msg or '\n' + ''.join(difflib.ndiff(first.splitlines(True),
+ second.splitlines(True))))
+
+ def assertLess(self, a, b, msg=None):
+ """Just like self.assert_(a < b), but with a nicer default message."""
+ if msg is None:
+ msg = '"%r" unexpectedly not less than "%r"' % (a, b)
+ self.assert_(a < b, msg)
+
+ def assertLessEqual(self, a, b, msg=None):
+ """Just like self.assert_(a <= b), but with a nicer default message."""
+ if msg is None:
+ msg = '"%r" unexpectedly not less than or equal to "%r"' % (a, b)
+ self.assert_(a <= b, msg)
+
+ def assertGreater(self, a, b, msg=None):
+ """Just like self.assert_(a > b), but with a nicer default message."""
+ if msg is None:
+ msg = '"%r" unexpectedly not greater than "%r"' % (a, b)
+ self.assert_(a > b, msg)
+
+ def assertGreaterEqual(self, a, b, msg=None):
+ """Just like self.assert_(a >= b), but with a nicer default message."""
+ if msg is None:
+ msg = '"%r" unexpectedly not greater than or equal to "%r"' % (a, b)
+ self.assert_(a >= b, msg)
+
+ def assertIsNone(self, obj, msg=None):
+ """Same as self.assert_(obj is None), with a nicer default message."""
+ if msg is None:
+ msg = '"%s" unexpectedly not None' % obj
+ self.assert_(obj is None, msg)
+
+ def assertIsNotNone(self, obj, msg='unexpectedly None'):
+ """Included for symmetry with assertIsNone."""
+ self.assert_(obj is not None, msg)
+
+ def assertRaisesRegexp(self, expected_exception, expected_regexp,
+ callable_obj=None, *args, **kwargs):
+ """Asserts that the message in a raised exception matches a regexp.
+
+ Args:
+ expected_exception: Exception class expected to be raised.
+ expected_regexp: Regexp (re pattern object or string) expected
+ to be found in error message.
+ callable_obj: Function to be called.
+ args: Extra args.
+ kwargs: Extra kwargs.
+ """
+ context = _AssertRaisesContext(expected_exception, self, expected_regexp)
+ if callable_obj is None:
+ return context
+ with context:
+ callable_obj(*args, **kwargs)
+
+ def assertRegexpMatches(self, text, expected_regex, msg=None):
+ if isinstance(expected_regex, basestring):
+ expected_regex = re.compile(expected_regex)
+ if not expected_regex.search(text):
+ msg = msg or "Regexp didn't match"
+ msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text)
+ raise self.failureException(msg)
+
+
+def _SortedListDifference(expected, actual):
+ """Finds elements in only one or the other of two, sorted input lists.
+
+ Returns a two-element tuple of lists. The first list contains those
+ elements in the "expected" list but not in the "actual" list, and the
+ second contains those elements in the "actual" list but not in the
+ "expected" list. Duplicate elements in either input list are ignored.
+ """
+ i = j = 0
+ missing = []
+ unexpected = []
+ while True:
+ try:
+ e = expected[i]
+ a = actual[j]
+ if e < a:
+ missing.append(e)
+ i += 1
+ while expected[i] == e:
+ i += 1
+ elif e > a:
+ unexpected.append(a)
+ j += 1
+ while actual[j] == a:
+ j += 1
+ else:
+ i += 1
+ try:
+ while expected[i] == e:
+ i += 1
+ finally:
+ j += 1
+ while actual[j] == a:
+ j += 1
+ except IndexError:
+ missing.extend(expected[i:])
+ unexpected.extend(actual[j:])
+ break
+ return missing, unexpected
+
class TestSuite(object):
"""A test suite is a composite test consisting of a number of TestCases.