summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/testtools/HACKING139
-rw-r--r--lib/testtools/LICENSE19
-rw-r--r--lib/testtools/MANIFEST.in9
-rw-r--r--lib/testtools/MANUAL213
-rw-r--r--lib/testtools/Makefile28
-rw-r--r--lib/testtools/NEWS191
-rw-r--r--lib/testtools/README54
-rwxr-xr-xlib/testtools/setup.py25
-rw-r--r--lib/testtools/testtools/__init__.py (renamed from lib/testtools/__init__.py)0
-rw-r--r--lib/testtools/testtools/content.py (renamed from lib/testtools/content.py)0
-rw-r--r--lib/testtools/testtools/content_type.py (renamed from lib/testtools/content_type.py)0
-rw-r--r--lib/testtools/testtools/matchers.py (renamed from lib/testtools/matchers.py)0
-rwxr-xr-xlib/testtools/testtools/run.py (renamed from lib/testtools/run.py)0
-rw-r--r--lib/testtools/testtools/runtest.py (renamed from lib/testtools/runtest.py)0
-rw-r--r--lib/testtools/testtools/testcase.py (renamed from lib/testtools/testcase.py)0
-rw-r--r--lib/testtools/testtools/testresult/__init__.py (renamed from lib/testtools/testresult/__init__.py)0
-rw-r--r--lib/testtools/testtools/testresult/doubles.py (renamed from lib/testtools/testresult/doubles.py)0
-rw-r--r--lib/testtools/testtools/testresult/real.py (renamed from lib/testtools/testresult/real.py)0
-rw-r--r--lib/testtools/testtools/tests/__init__.py (renamed from lib/testtools/tests/__init__.py)0
-rw-r--r--lib/testtools/testtools/tests/helpers.py (renamed from lib/testtools/tests/helpers.py)0
-rw-r--r--lib/testtools/testtools/tests/test_content.py (renamed from lib/testtools/tests/test_content.py)0
-rw-r--r--lib/testtools/testtools/tests/test_content_type.py (renamed from lib/testtools/tests/test_content_type.py)0
-rw-r--r--lib/testtools/testtools/tests/test_matchers.py (renamed from lib/testtools/tests/test_matchers.py)0
-rw-r--r--lib/testtools/testtools/tests/test_runtest.py (renamed from lib/testtools/tests/test_runtest.py)0
-rw-r--r--lib/testtools/testtools/tests/test_testresult.py (renamed from lib/testtools/tests/test_testresult.py)0
-rw-r--r--lib/testtools/testtools/tests/test_testsuite.py (renamed from lib/testtools/tests/test_testsuite.py)0
-rw-r--r--lib/testtools/testtools/tests/test_testtools.py (renamed from lib/testtools/tests/test_testtools.py)0
-rw-r--r--lib/testtools/testtools/testsuite.py (renamed from lib/testtools/testsuite.py)0
-rw-r--r--lib/testtools/testtools/utils.py (renamed from lib/testtools/utils.py)0
-rwxr-xr-xlib/update-external.sh2
30 files changed, 679 insertions, 1 deletions
diff --git a/lib/testtools/HACKING b/lib/testtools/HACKING
new file mode 100644
index 0000000000..8fe323cadd
--- /dev/null
+++ b/lib/testtools/HACKING
@@ -0,0 +1,139 @@
+===================================
+Notes for contributing to testtools
+===================================
+
+Coding style
+------------
+
+In general, follow PEP 8 <http://www.python.org/dev/peps/pep-0008/>.
+
+For consistency with the standard library's ``unittest`` module, method names
+are generally ``camelCase``.
+
+testtools supports Python 2.4 and later, so avoid any 2.5-only features like
+the ``with`` statement.
+
+
+Copyright assignment
+--------------------
+
+Part of testtools raison d'etre is to provide Python with improvements to the
+testing code it ships. For that reason we require all contributions (that are
+non-trivial) to meet one of the following rules:
+
+ - be inapplicable for inclusion in Python.
+ - be able to be included in Python without further contact with the
+ contributor.
+ - be copyright assigned to Jonathan M. Lange.
+
+Please pick one of these and specify it when contributing code to testtools.
+
+
+Licensing
+---------
+
+All code that is not copyright assigned to Jonathan M. Lange (see Copyright
+Assignment above) needs to be licensed under the MIT license that testtools
+uses, so that testtools can ship it.
+
+
+Testing
+-------
+
+Please write tests for every feature. This project ought to be a model
+example of well-tested Python code!
+
+Take particular care to make sure the *intent* of each test is clear.
+
+You can run tests with ``make check``, or by running ``./run-tests`` directly.
+
+
+Source layout
+-------------
+
+The top-level directory contains the ``testtools/`` package directory, and
+miscellaneous files like README and setup.py.
+
+The ``testtools/`` directory is the Python package itself. It is separated
+into submodules for internal clarity, but all public APIs should be “promoted”
+into the top-level package by importing them in ``testtools/__init__.py``.
+Users of testtools should never import a submodule, they are just
+implementation details.
+
+Tests belong in ``testtools/tests/``.
+
+
+Commiting to trunk
+------------------
+
+Testtools is maintained using bzr, with its trunk at lp:testtools. This gives
+every contributor the ability to commit their work to their own branches.
+However permission must be granted to allow contributors to commit to the trunk
+branch.
+
+Commit access to trunk is obtained by joining the testtools-devs Launchpad
+team. Membership in this team is contingent on obeying the testtools
+contribution policy, including assigning copyright of all the work one creates
+and places in trunk to Jonathan Lange.
+
+
+Code Review
+-----------
+
+All code must be reviewed before landing on trunk. The process is to create a
+branch in launchpad, and submit it for merging to lp:testtools. It will then
+be reviewed before it can be merged to trunk. It will be reviewed by someone:
+
+ * not the author
+ * a committer (member of the testtools-devs team)
+
+As a special exception, while the testtools committers team is small and prone
+to blocking, a merge request from a committer that has not been reviewed after
+24 hours may be merged by that committer. When the team is larger this policy
+will be revisited.
+
+Code reviewers should look for the quality of what is being submitted,
+including conformance with this HACKING file.
+
+Changes which all users should be made aware of should be documented in NEWS.
+
+
+NEWS management
+---------------
+
+The file NEWS is structured as a sorted list of releases. Each release can have
+a free form description and more or more sections with bullet point items.
+Sections in use today are 'Improvements' and 'Changes'. To ease merging between
+branches, the bullet points are kept alphabetically sorted. The release NEXT is
+permanently present at the top of the list.
+
+
+Release tasks
+-------------
+
+In no particular order:
+
+* Choose a version number.
+
+* Ensure __init__ has that version.
+
+* Add a version number to NEWS immediately below NEXT.
+
+* Possibly write a blurb into NEWS.
+
+* Replace any additional references to NEXT with the version being released.
+
+* Create a source distribution and upload to pypi ('make release').
+
+* Upload to Launchpad as well.
+
+* If a new series has been created (e.g. 0.10.0), make the series on Launchpad.
+
+* Merge or push the release branch to trunk.
+
+* Make a new milestone for the *next release*. We don't really know how we want
+ to handle these yet, so this is a suggestion not actual practice:
+
+ * during release we rename NEXT to $version.
+
+ * we call new milestones NEXT.
diff --git a/lib/testtools/LICENSE b/lib/testtools/LICENSE
new file mode 100644
index 0000000000..bdc733fe04
--- /dev/null
+++ b/lib/testtools/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2008 Jonathan M. Lange <jml@mumak.net> and the testtools authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lib/testtools/MANIFEST.in b/lib/testtools/MANIFEST.in
new file mode 100644
index 0000000000..3296ee4c0e
--- /dev/null
+++ b/lib/testtools/MANIFEST.in
@@ -0,0 +1,9 @@
+include LICENSE
+include HACKING
+include Makefile
+include MANIFEST.in
+include MANUAL
+include NEWS
+include README
+include run-tests
+include .bzrignore
diff --git a/lib/testtools/MANUAL b/lib/testtools/MANUAL
new file mode 100644
index 0000000000..a040c2860d
--- /dev/null
+++ b/lib/testtools/MANUAL
@@ -0,0 +1,213 @@
+======
+Manual
+======
+
+Introduction
+------------
+
+This document provides overview of the features provided by testtools. Refer
+to the API docs (i.e. docstrings) for full details on a particular feature.
+
+Extensions to TestCase
+----------------------
+
+Controlling test execution
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Testtools supports two ways to control how tests are executed. The simplest
+is to add a new exception to self.exception_handlers::
+
+ >>> self.exception_handlers.insert(-1, (ExceptionClass, handler)).
+
+Having done this, if any of setUp, tearDown, or the test method raise
+ExceptionClass, handler will be called with the test case, test result and the
+raised exception.
+
+Secondly, by overriding __init__ to pass in runTest=RunTestFactory the whole
+execution of the test can be altered. The default is testtools.runtest.RunTest
+and calls case._run_setup, case._run_test_method and finally
+case._run_teardown. Other methods to control what RunTest is used may be
+added in future.
+
+
+TestCase.addCleanup
+~~~~~~~~~~~~~~~~~~~
+
+addCleanup is a robust way to arrange for a cleanup function to be called
+before tearDown. This is a powerful and simple alternative to putting cleanup
+logic in a try/finally block or tearDown method. e.g.::
+
+ def test_foo(self):
+ foo.lock()
+ self.addCleanup(foo.unlock)
+ ...
+
+
+TestCase.addOnException
+~~~~~~~~~~~~~~~~~~~~~~~
+
+addOnException adds an exception handler that will be called from the test
+framework when it detects an exception from your test code. The handler is
+given the exc_info for the exception, and can use this opportunity to attach
+more data (via the addDetails API) and potentially other uses.
+
+
+TestCase.skip
+~~~~~~~~~~~~~
+
+``skip`` is a simple way to have a test stop running and be reported as a
+skipped test, rather than a success/error/failure. This is an alternative to
+convoluted logic during test loading, permitting later and more localized
+decisions about the appropriateness of running a test. Many reasons exist to
+skip a test - for instance when a dependency is missing, or if the test is
+expensive and should not be run while on laptop battery power, or if the test
+is testing an incomplete feature (this is sometimes called a TODO). Using this
+feature when running your test suite with a TestResult object that is missing
+the ``addSkip`` method will result in the ``addError`` method being invoked
+instead.
+
+
+New assertion methods
+~~~~~~~~~~~~~~~~~~~~~
+
+testtools adds several assertion methods:
+
+ * assertIn
+ * assertNotIn
+ * assertIs
+ * assertIsNot
+ * assertIsInstance
+ * assertThat
+
+
+Improved assertRaises
+~~~~~~~~~~~~~~~~~~~~~
+
+TestCase.assertRaises returns the caught exception. This is useful for
+asserting more things about the exception than just the type::
+
+ error = self.assertRaises(UnauthorisedError, thing.frobnicate)
+ self.assertEqual('bob', error.username)
+ self.assertEqual('User bob cannot frobnicate', str(error))
+
+
+TestCase.assertThat
+~~~~~~~~~~~~~~~~~~~
+
+assertThat is a clean way to write complex assertions without tying them to
+the TestCase inheritance hierarchy (and thus making them easier to reuse).
+
+assertThat takes an object to be matched, and a matcher, and fails if the
+matcher does not match the matchee.
+
+See pydoc testtools.Matcher for the protocol that matchers need to implement.
+
+testtools includes some matchers in testtools.matchers.
+python -c 'import testtools.matchers; print testtools.matchers.__all__' will
+list those matchers.
+
+An example using the DocTestMatches matcher which uses doctests example
+matching logic::
+
+ def test_foo(self):
+ self.assertThat([1,2,3,4], DocTestMatches('[1, 2, 3, 4]'))
+
+
+Creation methods
+~~~~~~~~~~~~~~~~
+
+testtools.TestCase implements creation methods called ``getUniqueString`` and
+``getUniqueInteger``. See pages 419-423 of *xUnit Test Patterns* by Meszaros
+for a detailed discussion of creation methods.
+
+
+Test renaming
+~~~~~~~~~~~~~
+
+``testtools.clone_test_with_new_id`` is a function to copy a test case
+instance to one with a new name. This is helpful for implementing test
+parameterization.
+
+
+Extensions to TestResult
+------------------------
+
+TestResult.addSkip
+~~~~~~~~~~~~~~~~~~
+
+This method is called on result objects when a test skips. The
+``testtools.TestResult`` class records skips in its ``skip_reasons`` instance
+dict. The can be reported on in much the same way as succesful tests.
+
+
+TestResult.time
+~~~~~~~~~~~~~~~
+
+This method controls the time used by a TestResult, permitting accurate
+timing of test results gathered on different machines or in different threads.
+See pydoc testtools.TestResult.time for more details.
+
+
+ThreadsafeForwardingResult
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A TestResult which forwards activity to another test result, but synchronises
+on a semaphore to ensure that all the activity for a single test arrives in a
+batch. This allows simple TestResults which do not expect concurrent test
+reporting to be fed the activity from multiple test threads, or processes.
+
+Note that when you provide multiple errors for a single test, the target sees
+each error as a distinct complete test.
+
+
+TextTestResult
+~~~~~~~~~~~~~~
+
+A TestResult that provides a text UI very similar to the Python standard
+library UI. Key differences are that its supports the extended outcomes and
+details API, and is completely encapsulated into the result object, permitting
+it to be used without a 'TestRunner' object. Not all the Python 2.7 outcomes
+are displayed (yet). It is also a 'quiet' result with no dots or verbose mode.
+These limitations will be corrected soon.
+
+
+Test Doubles
+~~~~~~~~~~~~
+
+In testtools.testresult.doubles there are three test doubles that testtools
+uses for its own testing: Python26TestResult, Python27TestResult,
+ExtendedTestResult. These TestResult objects implement a single variation of
+the TestResult API each, and log activity to a list self._events. These are
+made available for the convenience of people writing their own extensions.
+
+
+startTestRun and stopTestRun
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Python 2.7 added hooks 'startTestRun' and 'stopTestRun' which are called
+before and after the entire test run. 'stopTestRun' is particularly useful for
+test results that wish to produce summary output.
+
+testtools.TestResult provides empty startTestRun and stopTestRun methods, and
+the default testtools runner will call these methods appropriately.
+
+
+Extensions to TestSuite
+-----------------------
+
+ConcurrentTestSuite
+~~~~~~~~~~~~~~~~~~~
+
+A TestSuite for parallel testing. This is used in conjuction with a helper that
+runs a single suite in some parallel fashion (for instance, forking, handing
+off to a subprocess, to a compute cloud, or simple threads).
+ConcurrentTestSuite uses the helper to get a number of separate runnable
+objects with a run(result), runs them all in threads using the
+ThreadsafeForwardingResult to coalesce their activity.
+
+
+Running tests
+-------------
+
+Testtools provides a convenient way to run a test suite using the testtools
+result object: python -m testtools.run testspec [testspec...].
diff --git a/lib/testtools/Makefile b/lib/testtools/Makefile
new file mode 100644
index 0000000000..5e232e3394
--- /dev/null
+++ b/lib/testtools/Makefile
@@ -0,0 +1,28 @@
+# See README for copyright and licensing details.
+
+PYTHON=python
+SOURCES=$(shell find testtools -name "*.py")
+
+check:
+ PYTHONPATH=$(PWD) $(PYTHON) -m testtools.run testtools.tests.test_suite
+
+TAGS: ${SOURCES}
+ ctags -e -R testtools/
+
+tags: ${SOURCES}
+ ctags -R testtools/
+
+clean:
+ rm -f TAGS tags
+ find testtools -name "*.pyc" -exec rm '{}' \;
+
+release:
+ ./setup.py sdist upload --sign
+
+apidocs:
+ pydoctor --make-html --add-package testtools \
+ --docformat=restructuredtext --project-name=testtools \
+ --project-url=https://launchpad.net/testtools
+
+
+.PHONY: check clean release apidocs
diff --git a/lib/testtools/NEWS b/lib/testtools/NEWS
new file mode 100644
index 0000000000..90d7fc492a
--- /dev/null
+++ b/lib/testtools/NEWS
@@ -0,0 +1,191 @@
+testtools NEWS
+++++++++++++++
+
+NEXT
+~~~~
+
+Improvements
+------------
+
+* New matcher "Annotate" that adds a simple string message to another matcher,
+ much like the option 'message' parameter to standard library assertFoo
+ methods.
+
+* New matchers "Not" and "MatchesAll". "Not" will invert another matcher, and
+ "MatchesAll" that needs a successful match for all of its arguments.
+
+* On Python 2.4, where types.FunctionType cannot be deepcopied, testtools will
+ now monkeypatch copy._deepcopy_dispatch using the same trivial patch that
+ added such support to Python 2.5. The monkey patch is triggered by the
+ absence of FunctionType from the dispatch dict rather than a version check.
+ Bug #498030.
+
+* On windows the test 'test_now_datetime_now' should now work reliably.
+
+* TestCase.getUniqueInteger and TestCase.getUniqueString now have docstrings.
+
+* TestCase.getUniqueString now takes an optional prefix parameter, so you can
+ now use it in circumstances that forbid strings with '.'s, and such like.
+
+* testtools.testcase.clone_test_with_new_id now uses copy.copy, rather than
+ copy.deepcopy. Tests that need a deeper copy should use the copy protocol to
+ control how they are copied. Bug #498869.
+
+* The backtrace test result output tests should now pass on windows and other
+ systems where os.sep is not '/'.
+
+
+0.9.2
+~~~~~
+
+Python 3 support, more matchers and better consistency with Python 2.7 --
+you'd think that would be enough for a point release. Well, we here on the
+testtools project think that you deserve more.
+
+We've added a hook so that user code can be called just-in-time whenever there
+is an exception, and we've also factored out the "run" logic of test cases so
+that new outcomes can be added without fiddling with the actual flow of logic.
+
+It might sound like small potatoes, but it's changes like these that will
+bring about the end of test frameworks.
+
+
+Improvements
+------------
+
+* A failure in setUp and tearDown now report as failures not as errors.
+
+* Cleanups now run after tearDown to be consistent with Python 2.7's cleanup
+ feature.
+
+* ExtendedToOriginalDecorator now passes unrecognised attributes through
+ to the decorated result object, permitting other extensions to the
+ TestCase -> TestResult protocol to work.
+
+* It is now possible to trigger code just-in-time after an exception causes
+ a test outcome such as failure or skip. See the testtools MANUAL or
+ ``pydoc testtools.TestCase.addOnException``. (bug #469092)
+
+* New matcher Equals which performs a simple equality test.
+
+* New matcher MatchesAny which looks for a match of any of its arguments.
+
+* TestCase no longer breaks if a TestSkipped exception is raised with no
+ parameters.
+
+* TestCase.run now clones test cases before they are run and runs the clone.
+ This reduces memory footprint in large test runs - state accumulated on
+ test objects during their setup and execution gets freed when test case
+ has finished running unless the TestResult object keeps a reference.
+ NOTE: As test cloning uses deepcopy, this can potentially interfere if
+ a test suite has shared state (such as the testscenarios or testresources
+ projects use). Use the __deepcopy__ hook to control the copying of such
+ objects so that the shared references stay shared.
+
+* Testtools now accepts contributions without copyright assignment under some
+ circumstances. See HACKING for details.
+
+* Testtools now provides a convenient way to run a test suite using the
+ testtools result object: python -m testtools.run testspec [testspec...].
+
+* Testtools now works on Python 3, thanks to Benjamin Peterson.
+
+* Test execution now uses a separate class, testtools.RunTest to run single
+ tests. This can be customised and extended in a more consistent fashion than
+ the previous run method idiom. See pydoc for more information.
+
+* The test doubles that testtools itself uses are now available as part of
+ the testtools API in testtols.testresult.doubles.
+
+* TracebackContent now sets utf8 as the charset encoding, rather than not
+ setting one and encoding with the default encoder.
+
+* With python2.7 testtools.TestSkipped will be the unittest.case.SkipTest
+ exception class making skips compatible with code that manually raises the
+ standard library exception. (bug #490109)
+
+Changes
+-------
+
+* TestCase.getUniqueInteger is now implemented using itertools.count. Thanks
+ to Benjamin Peterson for the patch. (bug #490111)
+
+
+0.9.1
+~~~~~
+
+The new matcher API introduced in 0.9.0 had a small flaw where the matchee
+would be evaluated twice to get a description of the mismatch. This could lead
+to bugs if the act of matching caused side effects to occur in the matchee.
+Since having such side effects isn't desirable, we have changed the API now
+before it has become widespread.
+
+Changes
+-------
+
+* Matcher API changed to avoid evaluating matchee twice. Please consult
+ the API documentation.
+
+* TestCase.getUniqueString now uses the test id, not the test method name,
+ which works nicer with parameterised tests.
+
+Improvements
+------------
+
+* Python2.4 is now supported again.
+
+
+0.9.0
+~~~~~
+
+This release of testtools is perhaps the most interesting and exciting one
+it's ever had. We've continued in bringing together the best practices of unit
+testing from across a raft of different Python projects, but we've also
+extended our mission to incorporating unit testing concepts from other
+languages and from our own research, led by Robert Collins.
+
+We now support skipping and expected failures. We'll make sure that you
+up-call setUp and tearDown, avoiding unexpected testing weirdnesses. We're
+now compatible with Python 2.5, 2.6 and 2.7 unittest library.
+
+All in all, if you are serious about unit testing and want to get the best
+thinking from the whole Python community, you should get this release.
+
+Improvements
+------------
+
+* A new TestResult API has been added for attaching details to test outcomes.
+ This API is currently experimental, but is being prepared with the intent
+ of becoming an upstream Python API. For more details see pydoc
+ testtools.TestResult and the TestCase addDetail / getDetails methods.
+
+* assertThat has been added to TestCase. This new assertion supports
+ a hamcrest-inspired matching protocol. See pydoc testtools.Matcher for
+ details about writing matchers, and testtools.matchers for the included
+ matchers. See http://code.google.com/p/hamcrest/.
+
+* Compatible with Python 2.6 and Python 2.7
+
+* Failing to upcall in setUp or tearDown will now cause a test failure.
+ While the base methods do nothing, failing to upcall is usually a problem
+ in deeper hierarchies, and checking that the root method is called is a
+ simple way to catch this common bug.
+
+* New TestResult decorator ExtendedToOriginalDecorator which handles
+ downgrading extended API calls like addSkip to older result objects that
+ do not support them. This is used internally to make testtools simpler but
+ can also be used to simplify other code built on or for use with testtools.
+
+* New TextTestResult supporting the extended APIs that testtools provides.
+
+* Nose will no longer find 'runTest' tests in classes derived from
+ testtools.testcase.TestCase (bug #312257).
+
+* Supports the Python 2.7/3.1 addUnexpectedSuccess and addExpectedFailure
+ TestResult methods, with a support function 'knownFailure' to let tests
+ trigger these outcomes.
+
+* When using the skip feature with TestResult objects that do not support it
+ a test success will now be reported. Previously an error was reported but
+ production experience has shown that this is too disruptive for projects that
+ are using skips: they cannot get a clean run on down-level result objects.
diff --git a/lib/testtools/README b/lib/testtools/README
new file mode 100644
index 0000000000..5e3dd07cd6
--- /dev/null
+++ b/lib/testtools/README
@@ -0,0 +1,54 @@
+=========
+testtools
+=========
+
+testtools is a set of extensions to the Python standard library's unit testing
+framework.
+
+These extensions have been derived from years of experience with unit testing
+in Python and come from many different sources.
+
+Licensing
+---------
+
+This project is distributed under the MIT license and copyright is owned by
+Jonathan M. Lange. See LICENSE for details.
+
+
+Dependencies
+------------
+
+ * Python 2.4+ or 3.0+
+
+
+Bug reports and patches
+-----------------------
+
+Please report bugs using Launchpad at <https://bugs.launchpad.net/testtools>.
+Patches can also be submitted via Launchpad, or mailed to the author. You can
+mail the author directly at jml@mumak.net.
+
+There's no mailing list for this project yet, however the testing-in-python
+mailing list may be a useful resource:
+
+ * Address: testing-in-python@lists.idyll.org
+ * Subscription link: http://lists.idyll.org/listinfo/testing-in-python
+
+
+History
+-------
+
+testtools used to be called 'pyunit3k'. The name was changed to avoid
+conflating the library with the Python 3.0 release (commonly referred to as
+'py3k').
+
+
+Thanks
+------
+
+ * Canonical Ltd
+ * Bazaar
+ * Twisted Matrix Labs
+ * Robert Collins
+ * Andrew Bennetts
+ * Benjamin Peterson
diff --git a/lib/testtools/setup.py b/lib/testtools/setup.py
new file mode 100755
index 0000000000..d7ed46f79f
--- /dev/null
+++ b/lib/testtools/setup.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+"""Distutils installer for testtools."""
+
+from distutils.core import setup
+import testtools
+version = '.'.join(str(component) for component in testtools.__version__[0:3])
+phase = testtools.__version__[3]
+if phase != 'final':
+ import bzrlib.workingtree
+ t = bzrlib.workingtree.WorkingTree.open_containing(__file__)[0]
+ if phase == 'alpha':
+ # No idea what the next version will be
+ version = 'next-%s' % t.branch.revno()
+ else:
+ # Preserve the version number but give it a revno prefix
+ version = version + '~%s' % t.branch.revno()
+
+setup(name='testtools',
+ author='Jonathan M. Lange',
+ author_email='jml+testtools@mumak.net',
+ url='https://launchpad.net/testtools',
+ description=('Extensions to the Python standard library unit testing '
+ 'framework'),
+ version=version,
+ packages=['testtools', 'testtools.testresult', 'testtools.tests'])
diff --git a/lib/testtools/__init__.py b/lib/testtools/testtools/__init__.py
index 0504d661d4..0504d661d4 100644
--- a/lib/testtools/__init__.py
+++ b/lib/testtools/testtools/__init__.py
diff --git a/lib/testtools/content.py b/lib/testtools/testtools/content.py
index 353e3f0f46..353e3f0f46 100644
--- a/lib/testtools/content.py
+++ b/lib/testtools/testtools/content.py
diff --git a/lib/testtools/content_type.py b/lib/testtools/testtools/content_type.py
index aded81b732..aded81b732 100644
--- a/lib/testtools/content_type.py
+++ b/lib/testtools/testtools/content_type.py
diff --git a/lib/testtools/matchers.py b/lib/testtools/testtools/matchers.py
index 039c84b7c7..039c84b7c7 100644
--- a/lib/testtools/matchers.py
+++ b/lib/testtools/testtools/matchers.py
diff --git a/lib/testtools/run.py b/lib/testtools/testtools/run.py
index c4f461ecfb..c4f461ecfb 100755
--- a/lib/testtools/run.py
+++ b/lib/testtools/testtools/run.py
diff --git a/lib/testtools/runtest.py b/lib/testtools/testtools/runtest.py
index 053e2205a7..053e2205a7 100644
--- a/lib/testtools/runtest.py
+++ b/lib/testtools/testtools/runtest.py
diff --git a/lib/testtools/testcase.py b/lib/testtools/testtools/testcase.py
index fd70141e6d..fd70141e6d 100644
--- a/lib/testtools/testcase.py
+++ b/lib/testtools/testtools/testcase.py
diff --git a/lib/testtools/testresult/__init__.py b/lib/testtools/testtools/testresult/__init__.py
index 2ee3d25293..2ee3d25293 100644
--- a/lib/testtools/testresult/__init__.py
+++ b/lib/testtools/testtools/testresult/__init__.py
diff --git a/lib/testtools/testresult/doubles.py b/lib/testtools/testtools/testresult/doubles.py
index d231c919c2..d231c919c2 100644
--- a/lib/testtools/testresult/doubles.py
+++ b/lib/testtools/testtools/testresult/doubles.py
diff --git a/lib/testtools/testresult/real.py b/lib/testtools/testtools/testresult/real.py
index 8c8a3edd6e..8c8a3edd6e 100644
--- a/lib/testtools/testresult/real.py
+++ b/lib/testtools/testtools/testresult/real.py
diff --git a/lib/testtools/tests/__init__.py b/lib/testtools/testtools/tests/__init__.py
index 2cceba91e2..2cceba91e2 100644
--- a/lib/testtools/tests/__init__.py
+++ b/lib/testtools/testtools/tests/__init__.py
diff --git a/lib/testtools/tests/helpers.py b/lib/testtools/testtools/tests/helpers.py
index c4cf10c736..c4cf10c736 100644
--- a/lib/testtools/tests/helpers.py
+++ b/lib/testtools/testtools/tests/helpers.py
diff --git a/lib/testtools/tests/test_content.py b/lib/testtools/testtools/tests/test_content.py
index 1159362036..1159362036 100644
--- a/lib/testtools/tests/test_content.py
+++ b/lib/testtools/testtools/tests/test_content.py
diff --git a/lib/testtools/tests/test_content_type.py b/lib/testtools/testtools/tests/test_content_type.py
index dbefc21dec..dbefc21dec 100644
--- a/lib/testtools/tests/test_content_type.py
+++ b/lib/testtools/testtools/tests/test_content_type.py
diff --git a/lib/testtools/tests/test_matchers.py b/lib/testtools/testtools/tests/test_matchers.py
index 74b1ebc56a..74b1ebc56a 100644
--- a/lib/testtools/tests/test_matchers.py
+++ b/lib/testtools/testtools/tests/test_matchers.py
diff --git a/lib/testtools/tests/test_runtest.py b/lib/testtools/testtools/tests/test_runtest.py
index 5c46ad1784..5c46ad1784 100644
--- a/lib/testtools/tests/test_runtest.py
+++ b/lib/testtools/testtools/tests/test_runtest.py
diff --git a/lib/testtools/tests/test_testresult.py b/lib/testtools/testtools/tests/test_testresult.py
index df15b91244..df15b91244 100644
--- a/lib/testtools/tests/test_testresult.py
+++ b/lib/testtools/testtools/tests/test_testresult.py
diff --git a/lib/testtools/tests/test_testsuite.py b/lib/testtools/testtools/tests/test_testsuite.py
index 3f2f02758f..3f2f02758f 100644
--- a/lib/testtools/tests/test_testsuite.py
+++ b/lib/testtools/testtools/tests/test_testsuite.py
diff --git a/lib/testtools/tests/test_testtools.py b/lib/testtools/testtools/tests/test_testtools.py
index af1fd794c3..af1fd794c3 100644
--- a/lib/testtools/tests/test_testtools.py
+++ b/lib/testtools/testtools/tests/test_testtools.py
diff --git a/lib/testtools/testsuite.py b/lib/testtools/testtools/testsuite.py
index 26b193799b..26b193799b 100644
--- a/lib/testtools/testsuite.py
+++ b/lib/testtools/testtools/testsuite.py
diff --git a/lib/testtools/utils.py b/lib/testtools/testtools/utils.py
index c0845b610c..c0845b610c 100644
--- a/lib/testtools/utils.py
+++ b/lib/testtools/testtools/utils.py
diff --git a/lib/update-external.sh b/lib/update-external.sh
index 7cf95f3fb8..ebb7bd0013 100755
--- a/lib/update-external.sh
+++ b/lib/update-external.sh
@@ -14,6 +14,6 @@ done
echo "Updating testtools..."
bzr export "$WORKDIR/testtools" lp:testtools
-rsync -avz --delete "$WORKDIR/testtools/testtools/" "$TARGETDIR/testtools/"
+rsync -avz --delete "$WORKDIR/testtools/" "$TARGETDIR/testtools/"
rm -rf "$WORKDIR"