[Zope3-checkins] SVN: zope.testing/trunk/src/zope/testing/testrunner Merged /zope.testing/branches/stub-testSetUp_in_layer 68684:68744

Stuart Bishop cvs-admin at zope.org
Mon Jun 19 02:55:31 EDT 2006


Log message for revision 68745:
  Merged /zope.testing/branches/stub-testSetUp_in_layer 68684:68744

Changed:
  A   zope.testing/trunk/src/zope/testing/testrunner-layers-api.txt
  U   zope.testing/trunk/src/zope/testing/testrunner.py

-=-
Copied: zope.testing/trunk/src/zope/testing/testrunner-layers-api.txt (from rev 68744, zope.testing/branches/stub-testSetUp_in_layer/src/zope/testing/testrunner-layers-api.txt)

Modified: zope.testing/trunk/src/zope/testing/testrunner.py
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner.py	2006-06-19 06:10:05 UTC (rev 68744)
+++ zope.testing/trunk/src/zope/testing/testrunner.py	2006-06-19 06:55:27 UTC (rev 68745)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2004 Zope Corporation and Contributors.
+# Copyright (c) 2004-2006 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -253,7 +253,7 @@
 
     try:
         try:
-            failed = run_with_options(options)
+            failed = not run_with_options(options)
         except EndRun:
             failed = True
     finally:
@@ -288,8 +288,20 @@
 
     return failed
 
-def run_with_options(options):
+def run_with_options(options, found_suites=None):
+    """Find and run tests
+    
+    Passing a list of suites using the found_suites parameter will cause
+    that list of suites to be used instead of attempting to load them from
+    the filesystem. This is useful for unit testing the test runner.
 
+    Returns True if all tests passed, or False if there were any failures
+    of any kind.
+    """
+
+    global _layer_name_cache
+    _layer_name_cache = {} # Reset to enforce test isolation
+
     if options.resume_layer:
         original_stderr = sys.stderr
         sys.stderr = sys.stdout
@@ -350,7 +362,7 @@
 
     remove_stale_bytecode(options)
 
-    tests_by_layer_name = find_tests(options)
+    tests_by_layer_name = find_tests(options, found_suites)
 
     ran = 0
     failures = []
@@ -456,7 +468,7 @@
     if options.gc:
         gc.set_threshold(*old_threshold)
 
-    return bool(import_errors or failures or errors)
+    return not bool(import_errors or failures or errors)
 
 def run_tests(options, tests, name, failures, errors):
     repeat = options.repeat or 1
@@ -478,7 +490,7 @@
 
         if options.verbose > 0 or options.progress:
             print '  Running:'
-        result = TestResult(options, tests)
+        result = TestResult(options, tests, layer_name=name)
 
         t = time.time()
 
@@ -656,7 +668,7 @@
     if layer not in setup_layers:
         for base in layer.__bases__:
             setup_layer(base, setup_layers)
-        print "  Set up %s.%s" % (layer.__module__, layer.__name__),
+        print "  Set up %s" % name_from_layer(layer),
         t = time.time()
         layer.setUp()
         print "in %.3f seconds." % (time.time() - t)
@@ -671,9 +683,14 @@
 
     max_width = 80
 
-    def __init__(self, options, tests):
+    def __init__(self, options, tests, layer_name=None):
         unittest.TestResult.__init__(self)
         self.options = options
+        # Calculate our list of relevant layers we need to call testSetUp
+        # and testTearDown on.
+        self.layers = []
+        if layer_name != 'unit':
+            gather_layers(layer_from_name(layer_name), self.layers)
         if options.progress:
             count = 0
             for test in tests:
@@ -716,7 +733,28 @@
 
         return ' ' + s[:room]
 
+    def testSetUp(self):
+        """A layer may define a setup method to be called before each
+        individual test.
+        """
+        for layer in self.layers[-1::-1]:
+            if hasattr(layer, 'testSetUp'):
+                layer.testSetUp()
+
+    def testTearDown(self):
+        """A layer may define a teardown method to be called after each
+           individual test.
+           
+           This is useful for clearing the state of global
+           resources or resetting external systems such as relational
+           databases or daemons.
+        """
+        for layer in self.layers:
+            if hasattr(layer, 'testTearDown'):
+                layer.testTearDown()
+
     def startTest(self, test):
+        self.testSetUp()
         unittest.TestResult.startTest(self, test)
         testsRun = self.testsRun - 1
         count = test.countTestCases()
@@ -805,6 +843,7 @@
             sys.stdout.write('\r' + (' ' * self.last_width) + '\r')
 
     def stopTest(self, test):
+        self.testTearDown()
         if self.options.progress:
             self.last_width = self.test_width
         elif self.options.verbose > 1:
@@ -923,6 +962,16 @@
         gather_layers(b, result)
 
 def layer_from_name(layer_name):
+    """Return the layer for the corresponding layer_name by discovering
+       and importing the necessary module if necessary.
+
+       Note that a name -> layer cache is maintained by name_from_layer
+       to allow locating layers in cases where it would otherwise be
+       impossible.
+    """
+    global _layer_name_cache
+    if _layer_name_cache.has_key(layer_name):
+        return _layer_name_cache[layer_name]
     layer_names = layer_name.split('.')
     layer_module, module_layer_name = layer_names[:-1], layer_names[-1]
     return getattr(import_name('.'.join(layer_module)), module_layer_name)
@@ -946,12 +995,34 @@
                 result.append(layer)
     return result
 
+_layer_name_cache = {}
+
 def name_from_layer(layer):
-    return layer.__module__ + '.' + layer.__name__
+    """Determine a name for the Layer using the namespace to avoid conflicts.
 
-def find_tests(options):
+    We also cache a name -> layer mapping to enable layer_from_name to work
+    in cases where the layer cannot be imported (such as layers defined
+    in doctests)
+    """
+    if layer.__module__ == '__builtin__':
+        name = layer.__name__
+    else:
+        name = layer.__module__ + '.' + layer.__name__
+    _layer_name_cache[name] = layer
+    return name
+
+def find_tests(options, found_suites=None):
+    """Creates a dictionary mapping layer name to a suite of tests to be run
+    in that layer.
+
+    Passing a list of suites using the found_suites parameter will cause
+    that list of suites to be used instead of attempting to load them from
+    the filesystem. This is useful for unit testing the test runner.
+    """
     suites = {}
-    for suite in find_suites(options):
+    if found_suites is None:
+        found_suites = find_suites(options)
+    for suite in found_suites:
         for test, layer_name in tests_from_suite(suite, options):
             suite = suites.get(layer_name)
             if not suite:
@@ -960,10 +1031,20 @@
     return suites
 
 def tests_from_suite(suite, options, dlevel=1, dlayer='unit'):
+    """Returns a sequence of (test, layer_name)
+
+    The tree of suites is recursively visited, with the most specific
+    layer taking precidence. So if a TestCase with a layer of 'foo' is
+    contained in a TestSuite with a layer of 'bar', the test case would be
+    returned with 'foo' as the layer.
+
+    Tests are also filtered out based on the test level and test selection
+    filters stored in the options.
+    """
     level = getattr(suite, 'level', dlevel)
     layer = getattr(suite, 'layer', dlayer)
     if not isinstance(layer, basestring):
-        layer = layer.__module__ + '.' + layer.__name__
+        layer = name_from_layer(layer)
 
     if isinstance(suite, unittest.TestSuite):
         for possible_suite in suite:
@@ -1824,6 +1905,7 @@
         'testrunner-errors.txt',
         'testrunner-layers-ntd.txt',
         'testrunner-layers.txt',
+        'testrunner-layers-api.txt',
         'testrunner-progress.txt',
         'testrunner-simple.txt',
         'testrunner-test-selection.txt',



More information about the Zope3-Checkins mailing list