mirror of
https://github.com/samba-team/samba.git
synced 2025-01-26 10:04:02 +03:00
testtools: Merge in new upstream.
This commit is contained in:
parent
49dc973586
commit
145868db15
@ -127,7 +127,8 @@ Release tasks
|
||||
1. Create a release on the newly-renamed 'X.Y.Z' milestone
|
||||
1. Upload the tarball and asc file to Launchpad
|
||||
1. Merge the release branch testtools-X.Y.Z into trunk. Before the commit,
|
||||
add a NEXT heading to the top of NEWS. Push trunk to Launchpad.
|
||||
add a NEXT heading to the top of NEWS and bump the version in __init__.py.
|
||||
Push trunk to Launchpad
|
||||
1. If a new series has been created (e.g. 0.10.0), make the series on Launchpad.
|
||||
1. Make a new milestone for the *next release*.
|
||||
1. During release we rename NEXT to $version.
|
||||
|
@ -7,6 +7,31 @@ NEXT
|
||||
Changes
|
||||
-------
|
||||
|
||||
* The timestamps generated by ``TestResult`` objects when no timing data has
|
||||
been received are now datetime-with-timezone, which allows them to be
|
||||
sensibly serialised and transported. (Robert Collins, #692297)
|
||||
|
||||
Improvements
|
||||
------------
|
||||
|
||||
* ``MultiTestResult`` now forwards the ``time`` API. (Robert Collins, #692294)
|
||||
|
||||
0.9.8
|
||||
~~~~~
|
||||
|
||||
In this release we bring some very interesting improvements:
|
||||
|
||||
* new matchers for exceptions, sets, lists, dicts and more.
|
||||
|
||||
* experimental (works but the contract isn't supported) twisted reactor
|
||||
support.
|
||||
|
||||
* The built in runner can now list tests and filter tests (the -l and
|
||||
--load-list options).
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
* addUnexpectedSuccess is translated to addFailure for test results that don't
|
||||
know about addUnexpectedSuccess. Further, it fails the entire result for
|
||||
all testtools TestResults (i.e. wasSuccessful() returns False after
|
||||
@ -30,6 +55,10 @@ Changes
|
||||
Improvements
|
||||
------------
|
||||
|
||||
* ``assertIsInstance`` supports a custom error message to be supplied, which
|
||||
is necessary when using ``assertDictEqual`` on Python 2.7 with a
|
||||
``testtools.TestCase`` base class. (Jelmer Vernooij)
|
||||
|
||||
* Experimental support for running tests that return Deferreds.
|
||||
(Jonathan Lange, Martin [gz])
|
||||
|
||||
@ -50,6 +79,9 @@ Improvements
|
||||
* ``MatchesException`` added to the ``testtools.matchers`` module - matches
|
||||
an exception class and parameters. (Robert Collins)
|
||||
|
||||
* ``MismatchesAll.describe`` no longer appends a trailing newline.
|
||||
(Michael Hudson-Doyle, #686790)
|
||||
|
||||
* New ``KeysEqual`` matcher. (Jonathan Lange)
|
||||
|
||||
* New helpers for conditionally importing modules, ``try_import`` and
|
||||
@ -63,7 +95,11 @@ Improvements
|
||||
supplied callable raises and delegates to ``MatchesException`` to validate
|
||||
the exception. (Jonathan Lange)
|
||||
|
||||
* ``testools.TestCase.useFixture`` has been added to glue with fixtures nicely.
|
||||
* Tests will now pass on Python 2.6.4 : an ``Exception`` change made only in
|
||||
2.6.4 and reverted in Python 2.6.5 was causing test failures on that version.
|
||||
(Martin [gz], #689858).
|
||||
|
||||
* ``testtools.TestCase.useFixture`` has been added to glue with fixtures nicely.
|
||||
(Robert Collins)
|
||||
|
||||
* ``testtools.run`` now supports ``-l`` to list tests rather than executing
|
||||
|
@ -69,4 +69,4 @@ from testtools.testsuite import (
|
||||
# If the releaselevel is 'final', then the tarball will be major.minor.micro.
|
||||
# Otherwise it is major.minor.micro~$(revno).
|
||||
|
||||
__version__ = (0, 9, 8, 'dev', 0)
|
||||
__version__ = (0, 9, 9, 'dev', 0)
|
||||
|
@ -232,7 +232,6 @@ class Spinner(object):
|
||||
# we aren't going to bother.
|
||||
junk.append(selectable)
|
||||
if IReactorThreads.providedBy(self._reactor):
|
||||
self._reactor.suggestThreadPoolSize(0)
|
||||
if self._reactor.threadpool is not None:
|
||||
self._reactor._stopThreadPool()
|
||||
self._junk.extend(junk)
|
||||
|
@ -65,6 +65,34 @@ else:
|
||||
_u.__doc__ = __u_doc
|
||||
|
||||
|
||||
if sys.version_info > (2, 5):
|
||||
all = all
|
||||
_error_repr = BaseException.__repr__
|
||||
def isbaseexception(exception):
|
||||
"""Return whether exception inherits from BaseException only"""
|
||||
return (isinstance(exception, BaseException)
|
||||
and not isinstance(exception, Exception))
|
||||
else:
|
||||
def all(iterable):
|
||||
"""If contents of iterable all evaluate as boolean True"""
|
||||
for obj in iterable:
|
||||
if not obj:
|
||||
return False
|
||||
return True
|
||||
def _error_repr(exception):
|
||||
"""Format an exception instance as Python 2.5 and later do"""
|
||||
return exception.__class__.__name__ + repr(exception.args)
|
||||
def isbaseexception(exception):
|
||||
"""Return whether exception would inherit from BaseException only
|
||||
|
||||
This approximates the hierarchy in Python 2.5 and later, compare the
|
||||
difference between the diagrams at the bottom of the pages:
|
||||
<http://docs.python.org/release/2.4.4/lib/module-exceptions.html>
|
||||
<http://docs.python.org/release/2.5.4/lib/module-exceptions.html>
|
||||
"""
|
||||
return isinstance(exception, (KeyboardInterrupt, SystemExit))
|
||||
|
||||
|
||||
def unicode_output_stream(stream):
|
||||
"""Get wrapper for given stream that writes any unicode without exception
|
||||
|
||||
|
@ -32,6 +32,8 @@ import operator
|
||||
from pprint import pformat
|
||||
import sys
|
||||
|
||||
from testtools.compat import classtypes, _error_repr, isbaseexception
|
||||
|
||||
|
||||
class Matcher(object):
|
||||
"""A pattern matcher.
|
||||
@ -314,7 +316,7 @@ class MismatchesAll(Mismatch):
|
||||
descriptions = ["Differences: ["]
|
||||
for mismatch in self.mismatches:
|
||||
descriptions.append(mismatch.describe())
|
||||
descriptions.append("]\n")
|
||||
descriptions.append("]")
|
||||
return '\n'.join(descriptions)
|
||||
|
||||
|
||||
@ -359,25 +361,24 @@ class MatchesException(Matcher):
|
||||
"""
|
||||
Matcher.__init__(self)
|
||||
self.expected = exception
|
||||
|
||||
def _expected_type(self):
|
||||
if type(self.expected) is type:
|
||||
return self.expected
|
||||
return type(self.expected)
|
||||
self._is_instance = type(self.expected) not in classtypes()
|
||||
|
||||
def match(self, other):
|
||||
if type(other) != tuple:
|
||||
return Mismatch('%r is not an exc_info tuple' % other)
|
||||
if not issubclass(other[0], self._expected_type()):
|
||||
return Mismatch('%r is not a %r' % (
|
||||
other[0], self._expected_type()))
|
||||
if (type(self.expected) is not type and
|
||||
other[1].args != self.expected.args):
|
||||
return Mismatch('%r has different arguments to %r.' % (
|
||||
other[1], self.expected))
|
||||
expected_class = self.expected
|
||||
if self._is_instance:
|
||||
expected_class = expected_class.__class__
|
||||
if not issubclass(other[0], expected_class):
|
||||
return Mismatch('%r is not a %r' % (other[0], expected_class))
|
||||
if self._is_instance and other[1].args != self.expected.args:
|
||||
return Mismatch('%s has different arguments to %s.' % (
|
||||
_error_repr(other[1]), _error_repr(self.expected)))
|
||||
|
||||
def __str__(self):
|
||||
return "MatchesException(%r)" % self.expected
|
||||
if self._is_instance:
|
||||
return "MatchesException(%s)" % _error_repr(self.expected)
|
||||
return "MatchesException(%s)" % repr(self.expected)
|
||||
|
||||
|
||||
class StartsWith(Matcher):
|
||||
@ -501,7 +502,6 @@ class Raises(Matcher):
|
||||
# Catch all exceptions: Raises() should be able to match a
|
||||
# KeyboardInterrupt or SystemExit.
|
||||
except:
|
||||
exc_info = sys.exc_info()
|
||||
if self.exception_matcher:
|
||||
mismatch = self.exception_matcher.match(sys.exc_info())
|
||||
if not mismatch:
|
||||
@ -510,9 +510,9 @@ class Raises(Matcher):
|
||||
mismatch = None
|
||||
# The exception did not match, or no explicit matching logic was
|
||||
# performed. If the exception is a non-user exception (that is, not
|
||||
# a subclass of Exception) then propogate it.
|
||||
if not issubclass(exc_info[0], Exception):
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
# a subclass of Exception on Python 2.5+) then propogate it.
|
||||
if isbaseexception(sys.exc_info()[1]):
|
||||
raise
|
||||
return mismatch
|
||||
|
||||
def __str__(self):
|
||||
|
@ -132,6 +132,8 @@ class TestProgram(object):
|
||||
self.module = module
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
if stdout is None:
|
||||
stdout = sys.stdout
|
||||
|
||||
self.exit = exit
|
||||
self.failfast = failfast
|
||||
|
@ -300,10 +300,11 @@ class TestCase(unittest.TestCase):
|
||||
self.assertTrue(
|
||||
needle not in haystack, '%r in %r' % (needle, haystack))
|
||||
|
||||
def assertIsInstance(self, obj, klass):
|
||||
self.assertTrue(
|
||||
isinstance(obj, klass),
|
||||
'%r is not an instance of %s' % (obj, self._formatTypes(klass)))
|
||||
def assertIsInstance(self, obj, klass, msg=None):
|
||||
if msg is None:
|
||||
msg = '%r is not an instance of %s' % (
|
||||
obj, self._formatTypes(klass))
|
||||
self.assertTrue(isinstance(obj, klass), msg)
|
||||
|
||||
def assertRaises(self, excClass, callableObj, *args, **kwargs):
|
||||
"""Fail unless an exception of class excClass is thrown
|
||||
|
@ -14,7 +14,26 @@ import datetime
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from testtools.compat import _format_exc_info, str_is_unicode, _u
|
||||
from testtools.compat import all, _format_exc_info, str_is_unicode, _u
|
||||
|
||||
# From http://docs.python.org/library/datetime.html
|
||||
_ZERO = datetime.timedelta(0)
|
||||
|
||||
# A UTC class.
|
||||
|
||||
class UTC(datetime.tzinfo):
|
||||
"""UTC"""
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return _ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return "UTC"
|
||||
|
||||
def dst(self, dt):
|
||||
return _ZERO
|
||||
|
||||
utc = UTC()
|
||||
|
||||
|
||||
class TestResult(unittest.TestResult):
|
||||
@ -149,7 +168,7 @@ class TestResult(unittest.TestResult):
|
||||
time() method.
|
||||
"""
|
||||
if self.__now is None:
|
||||
return datetime.datetime.now()
|
||||
return datetime.datetime.now(utc)
|
||||
else:
|
||||
return self.__now
|
||||
|
||||
@ -238,6 +257,9 @@ class MultiTestResult(TestResult):
|
||||
def stopTestRun(self):
|
||||
return self._dispatch('stopTestRun')
|
||||
|
||||
def time(self, a_datetime):
|
||||
return self._dispatch('time', a_datetime)
|
||||
|
||||
def done(self):
|
||||
return self._dispatch('done')
|
||||
|
||||
|
@ -183,8 +183,7 @@ class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface):
|
||||
MatchesException(Exception('foo')))
|
||||
]
|
||||
describe_examples = [
|
||||
("<type 'exceptions.Exception'> is not a "
|
||||
"<type 'exceptions.ValueError'>",
|
||||
("%r is not a %r" % (Exception, ValueError),
|
||||
error_base_foo,
|
||||
MatchesException(ValueError("foo"))),
|
||||
("ValueError('bar',) has different arguments to ValueError('foo',).",
|
||||
@ -203,12 +202,11 @@ class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface):
|
||||
matches_mismatches = [error_base_foo]
|
||||
|
||||
str_examples = [
|
||||
("MatchesException(<type 'exceptions.Exception'>)",
|
||||
("MatchesException(%r)" % Exception,
|
||||
MatchesException(Exception))
|
||||
]
|
||||
describe_examples = [
|
||||
("<type 'exceptions.Exception'> is not a "
|
||||
"<type 'exceptions.ValueError'>",
|
||||
("%r is not a %r" % (Exception, ValueError),
|
||||
error_base_foo,
|
||||
MatchesException(ValueError)),
|
||||
]
|
||||
@ -249,8 +247,7 @@ Expected:
|
||||
Got:
|
||||
3
|
||||
|
||||
]
|
||||
""",
|
||||
]""",
|
||||
"3", MatchesAny(DocTestMatches("1"), DocTestMatches("2")))]
|
||||
|
||||
|
||||
@ -266,8 +263,7 @@ class TestMatchesAllInterface(TestCase, TestMatchersInterface):
|
||||
|
||||
describe_examples = [("""Differences: [
|
||||
1 == 1
|
||||
]
|
||||
""",
|
||||
]""",
|
||||
1, MatchesAll(NotEquals(1), NotEquals(2)))]
|
||||
|
||||
|
||||
@ -364,7 +360,12 @@ class TestRaisesBaseTypes(TestCase):
|
||||
# Exception, it is propogated.
|
||||
match_keyb = Raises(MatchesException(KeyboardInterrupt))
|
||||
def raise_keyb_from_match():
|
||||
matcher = Raises(MatchesException(Exception))
|
||||
if sys.version_info > (2, 5):
|
||||
matcher = Raises(MatchesException(Exception))
|
||||
else:
|
||||
# On Python 2.4 KeyboardInterrupt is a StandardError subclass
|
||||
# but should propogate from less generic exception matchers
|
||||
matcher = Raises(MatchesException(EnvironmentError))
|
||||
matcher.match(self.raiser)
|
||||
self.assertThat(raise_keyb_from_match, match_keyb)
|
||||
|
||||
|
@ -2,10 +2,9 @@
|
||||
|
||||
"""Tests for the test runner logic."""
|
||||
|
||||
import StringIO
|
||||
|
||||
from testtools.helpers import try_import
|
||||
from testtools.helpers import try_import, try_imports
|
||||
fixtures = try_import('fixtures')
|
||||
StringIO = try_imports(['StringIO.StringIO', 'io.StringIO'])
|
||||
|
||||
import testtools
|
||||
from testtools import TestCase, run
|
||||
@ -43,7 +42,7 @@ class TestRun(TestCase):
|
||||
if fixtures is None:
|
||||
self.skipTest("Need fixtures")
|
||||
package = self.useFixture(SampleTestFixture())
|
||||
out = StringIO.StringIO()
|
||||
out = StringIO()
|
||||
run.main(['prog', '-l', 'testtools.runexample.test_suite'], out)
|
||||
self.assertEqual("""testtools.runexample.TestFoo.test_bar
|
||||
testtools.runexample.TestFoo.test_quux
|
||||
@ -53,7 +52,7 @@ testtools.runexample.TestFoo.test_quux
|
||||
if fixtures is None:
|
||||
self.skipTest("Need fixtures")
|
||||
package = self.useFixture(SampleTestFixture())
|
||||
out = StringIO.StringIO()
|
||||
out = StringIO()
|
||||
# We load two tests - one that exists and one that doesn't, and we
|
||||
# should get the one that exists and neither the one that doesn't nor
|
||||
# the unmentioned one that does.
|
||||
|
@ -244,7 +244,14 @@ class TestRunInReactor(NeedsTwistedTestCase):
|
||||
timeout = self.make_timeout()
|
||||
spinner = self.make_spinner(reactor)
|
||||
spinner.run(timeout, reactor.callInThread, time.sleep, timeout / 2.0)
|
||||
self.assertThat(list(threading.enumerate()), Equals(current_threads))
|
||||
# Python before 2.5 has a race condition with thread handling where
|
||||
# join() does not remove threads from enumerate before returning - the
|
||||
# thread being joined does the removal. This was fixed in Python 2.5
|
||||
# but we still support 2.4, so we have to workaround the issue.
|
||||
# http://bugs.python.org/issue1703448.
|
||||
self.assertThat(
|
||||
[thread for thread in threading.enumerate() if thread.isAlive()],
|
||||
Equals(current_threads))
|
||||
|
||||
def test_leftover_junk_available(self):
|
||||
# If 'run' is given a function that leaves the reactor dirty in some
|
||||
|
@ -45,6 +45,7 @@ from testtools.tests.helpers import (
|
||||
ExtendedTestResult,
|
||||
an_exc_info
|
||||
)
|
||||
from testtools.testresult.real import utc
|
||||
|
||||
StringIO = try_imports(['StringIO.StringIO', 'io.StringIO'])
|
||||
|
||||
@ -305,10 +306,10 @@ class TestTestResult(TestCase):
|
||||
self.addCleanup(restore)
|
||||
class Module:
|
||||
pass
|
||||
now = datetime.datetime.now()
|
||||
now = datetime.datetime.now(utc)
|
||||
stubdatetime = Module()
|
||||
stubdatetime.datetime = Module()
|
||||
stubdatetime.datetime.now = lambda: now
|
||||
stubdatetime.datetime.now = lambda tz: now
|
||||
testresult.real.datetime = stubdatetime
|
||||
# Calling _now() looks up the time.
|
||||
self.assertEqual(now, result._now())
|
||||
@ -323,7 +324,7 @@ class TestTestResult(TestCase):
|
||||
|
||||
def test_now_datetime_time(self):
|
||||
result = self.makeResult()
|
||||
now = datetime.datetime.now()
|
||||
now = datetime.datetime.now(utc)
|
||||
result.time(now)
|
||||
self.assertEqual(now, result._now())
|
||||
|
||||
@ -424,6 +425,11 @@ class TestMultiTestResult(TestWithFakeExceptions):
|
||||
result = multi_result.stopTestRun()
|
||||
self.assertEqual(('foo', 'foo'), result)
|
||||
|
||||
def test_time(self):
|
||||
# the time call is dispatched, not eaten by the base class
|
||||
self.multiResult.time('foo')
|
||||
self.assertResultLogsEqual([('time', 'foo')])
|
||||
|
||||
|
||||
class TestTextTestResult(TestCase):
|
||||
"""Tests for `TextTestResult`."""
|
||||
@ -501,7 +507,7 @@ class TestTextTestResult(TestCase):
|
||||
|
||||
def test_stopTestRun_current_time(self):
|
||||
test = self.make_test()
|
||||
now = datetime.datetime.now()
|
||||
now = datetime.datetime.now(utc)
|
||||
self.result.time(now)
|
||||
self.result.startTestRun()
|
||||
self.result.startTest(test)
|
||||
@ -1230,6 +1236,8 @@ class TestNonAsciiResults(TestCase):
|
||||
"class UnprintableError(Exception):\n"
|
||||
" def __str__(self):\n"
|
||||
" raise RuntimeError\n"
|
||||
" def __unicode__(self):\n"
|
||||
" raise RuntimeError\n"
|
||||
" def __repr__(self):\n"
|
||||
" raise RuntimeError\n")
|
||||
textoutput = self._test_external_case(
|
||||
|
@ -375,6 +375,10 @@ class TestAssertions(TestCase):
|
||||
'42 is not an instance of %s' % self._formatTypes([Foo, Bar]),
|
||||
self.assertIsInstance, 42, (Foo, Bar))
|
||||
|
||||
def test_assertIsInstance_overridden_message(self):
|
||||
# assertIsInstance(obj, klass, msg) permits a custom message.
|
||||
self.assertFails("foo", self.assertIsInstance, 42, str, "foo")
|
||||
|
||||
def test_assertIs(self):
|
||||
# assertIs asserts that an object is identical to another object.
|
||||
self.assertIs(None, None)
|
||||
|
Loading…
x
Reference in New Issue
Block a user