Overhaul README.
authorRobert Collins <robertc@robertcollins.net>
Thu, 9 Apr 2009 11:39:07 +0000 (21:39 +1000)
committerRobert Collins <robertc@robertcollins.net>
Thu, 9 Apr 2009 11:39:07 +0000 (21:39 +1000)
README
c/README

diff --git a/README b/README
index 787d9e45a062a2e94415d4563728bff1871a1eab..1b44d3875baf831d5224ad597ae1927fce49f1b8 100644 (file)
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 
-  subunit: extensions to Python unittest to get test results from subprocesses.
+  subunit: A streaming protocol for test results
   Copyright (C) 2005  Robert Collins <robertc@robertcollins.net>
 
   This program is free software; you can redistribute it and/or modify
 Subunit
 -------
 
-Subunit is attempting to extend unittest with a clean and simple api to
-run arbitrary external test suites and return the results to standard
-Python unittest.
-
-Subunit comes in three parts:
- * Protocol writers (clients)
- * Protocol readers (servers)
- * Filters
-A reader component acts as a test suite in the language that it is written
-for. Currently subunit only provides a Python protocol reader.
-
-Writers output test results in subunit form. Writers are typically
-test suite runners or test suite result objects in specific languages.
-Currently subunit provides writers for Python, C, C++, and shell.
-
-Filters provide translation filtering capabilities and can be used to modify a
-stream on-the-fly. Currently subunit provides:
+Subunit is a streaming protocol for test results. The protocol is human
+readable and easily generated and parsed. By design all the components of 
+the protocol conceptually fit into the xUnit TestCase->TestResult interaction.
+
+Subunit comes with command line filters to process a subunit stream and
+language bindings for python, C, C++ and shell. Bindings are easy to write
+for other languages.
+
+A number of useful things can be done easily with subunit:
+ * Test aggregation: Tests run separately can be combined and then
+   reported/displayed together. For instance, tests from different languages
+   can be shown as a seamless whole.
+ * Test archiving: A test run may be recorded and replayed later.
+ * Test isolation: Tests that may crash or otherwise interact badly with each
+   other can be run seperately and then aggregated, rather than interfering
+   with each other.
+ * Grid testing: subunit can act as the necessary serialisation and
+   deserialiation to get test runs on distributed machines to be reported in
+   real time.
+
+Subunit supplies the following filters:
  * tap2subunit - convert perl's TestAnythingProtocol to subunit.
  * subunit2pyunit - convert a subunit stream to pyunit test results.
  * subunit-filter - filter out tests from a subunit stream.
@@ -45,96 +48,111 @@ stream on-the-fly. Currently subunit provides:
  * subunit-stats - generate a summary of a subunit stream.
  * subunit-tags - add or remove tags from a stream.
 
-The subunit code is organised at the top level by directories for language
-bindings, and additionally the filters directory for filters.
-
-Using subunit in Python
------------------------
-
-1) As a runner for external tests (potentially in other languages)
-2) As a process boundary for unittest TestCases to prevent them fiddling with
-   in-process state (i.e. singletons).
-3) As a wrapper around a TestCase (or Suite) to run a group of tests
-   externally.
-
-1) As a runner for external tests
-=================================
-This is supported on all platforms with Python 2.4.
-For each test script you want to run, declare a ExecTestCase with one
-or more tests whose docstring defines the script to run:
-
-import subunit
-import unittest
-class TestCProgram(subunit.ExecTestCase):
-
-    def test_script_one(self):
-        """./bin/script_one"""
-
-    def test_script_two(self):
-       """./bin/script_two"""
-
-# Yes, the test prefix on the methods matters.
-# Then run this in whatever normal way you would run Python unittests.
-# If you don't have a specific test runner, you can run it using the
-# default one in unittest.py:
-
-if __name__ == '__main__':
-     unittest.main()
-
-2) As a process boundary for unittest TestCases
-===============================================
-This is currently supported only on platforms
-that support os.fork(), which allows us to
-transparently introduce a process boundary
-without affecting manual test parameterisation.
-*** TODO explain in more detail and sketch out
-*** a solution for win32
-Just import subunit and derive your test cases
-from subunit.IsolatedTestCase:
-
-import subunit
-
-class TestFoo(subunit.IsolatedTestCase):
+Subunit is attempting to extend unittest with a clean and simple api to
+run arbitrary external test suites and return the results to standard
+Python unittest.
 
-    def test_something_globally(self):
-        SomethingGlobal.do()
-        self.assertEqual(SomethingGlobal.value(), 3)
-        # the run() method of IsolatedTestCase will intercept the
-        # test execution, fork() Python to create a new process,
-        # then run the test and report the results back to the parent
-        # process.
+Integration with other tools
+----------------------------
 
-# you run this in the normal way you run test cases.
+Subunit's language bindings act as integration with various test runners like
+'check', 'cppunit', Python's 'unittest'. Beyond that a small amount of glue
+(typically a few lines) will allow Subunit to be used in more sophisticated
+ways.
+
+Python
+======
+
+As a TestResult, Subunit can translate method calls from a test run into a
+Subunit stream::
+
+ # Get a TestSuite or TestCase to run
+ suite = make_suite()
+ # Create a stream (any object with a 'write' method)
+ stream = file('tests.log', 'wb')
+ # Create a subunit result object which will output to the stream
+ result = subunit.TestProtocolClient(stream)
+ # Run the test suite reporting to the subunit result object
+ suite.run(result)
+ # Close the stream.
+ stream.close()
+
+As a TestCase, subunit can read from a stream and inform a TestResult
+of the activity from the stream::
+
+ # Get a stream (any object with a readline() method), in this case example the
+ # stream output by the example before.
+ stream = file('tests.log', 'rb')
+ # Create a subunit ProtocolTestCase which will read from the stream and emit 
+ # activity to a result when run() is called.
+ suite = subunit.ProtocolTestCase(stream)
+ # Create a result object to show the contents of the stream.
+ result = unittest._TextTestResult(sys.stdout)
+ # 'run' the tests - process the stream and feed its contents to result.
+ suite.run(result)
+ stream.close()
+
+Subunit has support for non-blocking usage too, for use with asyncore or
+Twisted. See the TestProtocolServer class for more details.
+
+Building on these foundations, Subunit also offers some convenience tools.
+
+The ``IsolatedTestSuite`` class is a decorator that will fork() before running
+the decorated item, and gather the results from the child process via subunit.
+This is useful for handlings tests that mutate global state, or are testing C
+extensions that could crash the VM.
+
+Similarly, ``IsolatedTestCase`` is a base class which can be subclassed to get
+tests that will fork() before the test is run.
+
+Finally, ``ExecTestCase`` is a convenience wrapper for running an external 
+program to get a subunit stream and then report that back to an arbitrary
+result object::
+
+ class AggregateTests(subunit.ExecTestCase):
+
+     def test_script_one(self):
+         """./bin/script_one"""
+
+     def test_script_two(self):
+         """./bin/script_two"""
+ # Normally your normal test loading would take of this automatically,
+ # It is only spelt out in detail here for clarity.
+ suite = unittest.TestSuite([AggregateTests("test_script_one"),
+     AggregateTests("test_script_two")])
+ # Create any TestResult class you like.
+ result = unittest._TextTestResult(sys.stdout)
+ # And run your suite as normal, subunit will exec each external script as
+ # needed and report to your result object.
+ suite.run(result)
 
-3) As a wrapper around a TestCase to run a group of tests externally.
-=====================================================================
+C
+=
 
-import subunit
-import unittest
+Subunit has C bindings to emit the protocol, and comes with a patch for 'check'
+which has been nominally accepted by the 'check' developers. See 'c/README' for
+more details.
 
-class TestFoo(unittest.TestCase):
+C++
+===
 
-    def test_foo(self):
-        ...
+C++ uses the C bindings and includes a patch for cppunit. See 'c++/README' for
+details.
 
+The subunit code is organised at the top level by directories for language
+bindings, and additionally the filters directory for filters.
 
-def test_suite():
-    result = subunit.IsolatedTestSuite()
-    loader = unittest.TestLoader()
-    result.addTest(loader.loadTestsFromName(__name__))
-    return result
+shell
+=====
 
-# you can test the result of test_suite() as follows (or in any normal Python
-# manner.
-runner = unittest.TextTestRunner(verbosity=2)
-runner.run(test_suite())
-# enjoy.
+Similar to C, the shell bindings consist of simple functions to output protocol
+elements, and a patch for adding subunit output to the 'ShUnit' shell test
+runner. See 'shell/README' for details.
 
 
-Some requirements:
-  The shape of the external unittest should not need to be known a-priori.
-  After the test has run, tests should still exist as discrete objects, so that
-  anything taking a reference to them doesn't get 50 copies of the same object.
+The protocol
+------------
 
 Sample subunit wire contents
 ----------------------------
@@ -163,7 +181,7 @@ foo.c:34 WARNING foo is not defined.
 
 
 Subunit protocol description
-----------------------------
+============================
 test|testing|test:|testing: test label
 success|success:|successful|successful: test label
 success|success:|successful|successful: test label [
@@ -211,10 +229,3 @@ in the expected manner. As this is a normal condition for such tests it is
 represented as a successful test in Python.
 In future, skip and xfail results will be represented semantically in Python,
 but some discussion is underway on the right way to do this.
-
-
-TODO:
-def run:
-    do a fork,
-      this process runs server
-      child runs client and calls self.run() with a SubprocessTestResult
index 89e6b58079b25222bd9e3c25e4cfea453f2309a3..863f53b6b712e45feee028643fcc3bc6c5b9a9f7 100644 (file)
--- a/c/README
+++ b/c/README
 This subtree contains an implementation of the subunit child protocol.
 Currently I have no plans to write a test runner in C, so I have not written
 an implementation of the parent protocol. [but will happily accept patches].
-This implementation is build using SCons and tested via 'check'.
+This implementation is built using SCons and tested via 'check'.
 See the tests/ directory for the test programs.
-You can use `make check` or `scons check` to run the tests. I plan to write a
-'check' runner which uses these bindings to provide subunit output, at which
-point creating a trivial python test_c.py script which uses the pyunit gui to
-will be added to me todo list.
+You can use `make check` or `scons check` to run the tests. 
 
 The C protocol consists of four functions which you can use to output test
 metadata trivially. See lib/subunit_child.[ch] for details.
@@ -34,11 +31,11 @@ However, this is not a test runner - subunit provides no support for [for
 instance] managing assertions, cleaning up on errors etc. You can look at
 'check' (http://check.sourceforge.net/) or
 'gunit' (https://garage.maemo.org/projects/gunit) for C unit test
-frameworks. I plan to write ui layers for both of these that use the subunit
-bindings for reporting. There is a patch for 'check'
-(check-subunit-0.9.3.patch, and check-subunit-0.9.5.patch) in this source tree.
+frameworks. 
+There is a patch for 'check' (check-subunit-*.patch) in this source tree.
 Its also available as request ID #1470750 in the sourceforge request tracker
-http://sourceforge.net/tracker/index.php.
+http://sourceforge.net/tracker/index.php. The 'check' developers have indicated
+they will merge this during the current release cycle.
 
 If you are a test environment maintainer - either homegrown, or 'check' or
 'gunit' or some other, you will to know how the subunit calls should be used.