Rev 4922: (jam) Add bzrlib.tests.permute_for_extension to simplify extension in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Dec 23 05:04:15 GMT 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4922 [merge]
revision-id: pqm at pqm.ubuntu.com-20091223050412-z05afly8exkncg8b
parent: pqm at pqm.ubuntu.com-20091223014722-dzs9ez4fhetyixha
parent: john at arbash-meinel.com-20091223041934-zbixrn1cg015bqq4
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2009-12-23 05:04:12 +0000
message:
  (jam) Add bzrlib.tests.permute_for_extension to simplify extension
  	testing.
renamed:
  bzrlib/tests/test_bencode.py => bzrlib/tests/test__bencode.py test_bencode.py-20070806225234-s51cnnkh6raytxti-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test__annotator.py test__annotator.py-20090617192546-21fnjrg2s2c16uem-2
  bzrlib/tests/test__chk_map.py  test__chk_map.py-20090309114220-1kurz7oez2gwqtcf-2
  bzrlib/tests/test__chunks_to_lines.py test__chunks_to_line-20081211024848-6uc3mtuje8j14l60-2
  bzrlib/tests/test__rio.py      test__rio.py-20090514191748-cy74k8yj46gzoeq6-1
  bzrlib/tests/test__static_tuple.py test__keys_type.py-20090908204220-aa346ccw4l37jzt7-2
  doc/developers/testing.txt     testing.txt-20080812140359-i70zzh6v2z7grqex-1
  bzrlib/tests/test__bencode.py  test_bencode.py-20070806225234-s51cnnkh6raytxti-1
=== modified file 'NEWS'
--- a/NEWS	2009-12-22 23:47:22 +0000
+++ b/NEWS	2009-12-23 05:04:12 +0000
@@ -23,6 +23,12 @@
   ``locations.conf`` or ``branch.conf``.
   (Ted Gould, Matthew Fuller, Vincent Ladeuil)
 
+* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies
+  running all tests in the current module, once against a pure python
+  implementation, and once against an extension (pyrex/C) implementation.
+  It can be used to dramatically simplify the implementation of
+  ``load_tests``.  (John Arbash Meinel)
+
 Bug Fixes
 *********
 

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-12-23 00:15:34 +0000
+++ b/bzrlib/tests/__init__.py	2009-12-23 05:04:12 +0000
@@ -3575,6 +3575,7 @@
         'bzrlib.tests.per_versionedfile',
         'bzrlib.tests.per_workingtree',
         'bzrlib.tests.test__annotator',
+        'bzrlib.tests.test__bencode',
         'bzrlib.tests.test__chk_map',
         'bzrlib.tests.test__dirstate_helpers',
         'bzrlib.tests.test__groupcompress',
@@ -3588,7 +3589,6 @@
         'bzrlib.tests.test_api',
         'bzrlib.tests.test_atomicfile',
         'bzrlib.tests.test_bad_files',
-        'bzrlib.tests.test_bencode',
         'bzrlib.tests.test_bisect_multi',
         'bzrlib.tests.test_branch',
         'bzrlib.tests.test_branchbuilder',
@@ -3956,6 +3956,47 @@
     return new_test
 
 
+def permute_tests_for_extension(standard_tests, loader, py_module_name,
+                                ext_module_name):
+    """Helper for permutating tests against an extension module.
+
+    This is meant to be used inside a modules 'load_tests()' function. It will
+    create 2 scenarios, and cause all tests in the 'standard_tests' to be run
+    against both implementations. Setting 'test.module' to the appropriate
+    module. See bzrlib.tests.test__chk_map.load_tests as an example.
+
+    :param standard_tests: A test suite to permute
+    :param loader: A TestLoader
+    :param py_module_name: The python path to a python module that can always
+        be loaded, and will be considered the 'python' implementation. (eg
+        'bzrlib._chk_map_py')
+    :param ext_module_name: The python path to an extension module. If the
+        module cannot be loaded, a single test will be added, which notes that
+        the module is not available. If it can be loaded, all standard_tests
+        will be run against that module.
+    :return: (suite, feature) suite is a test-suite that has all the permuted
+        tests. feature is the Feature object that can be used to determine if
+        the module is available.
+    """
+
+    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
+    scenarios = [
+        ('python', {'module': py_module}),
+    ]
+    suite = loader.suiteClass()
+    feature = ModuleAvailableFeature(ext_module_name)
+    if feature.available():
+        scenarios.append(('C', {'module': feature.module}))
+    else:
+        # the compiled module isn't available, so we add a failing test
+        class FailWithoutFeature(TestCase):
+            def test_fail(self):
+                self.requireFeature(feature)
+        suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
+    result = multiply_tests(standard_tests, scenarios, suite)
+    return result, feature
+
+
 def _rmtree_temp_dir(dirname, test_id=None):
     # If LANG=C we probably have created some bogus paths
     # which rmtree(unicode) will fail to delete

=== modified file 'bzrlib/tests/test__annotator.py'
--- a/bzrlib/tests/test__annotator.py	2009-12-22 15:50:40 +0000
+++ b/bzrlib/tests/test__annotator.py	2009-12-22 16:47:36 +0000
@@ -28,24 +28,9 @@
 
 def load_tests(standard_tests, module, loader):
     """Parameterize tests for all versions of groupcompress."""
-    scenarios = [
-        ('python', {'module': _annotator_py}),
-    ]
-    suite = loader.suiteClass()
-    if compiled_annotator_feature.available():
-        scenarios.append(('C', {'module': compiled_annotator_feature.module}))
-    else:
-        # the compiled module isn't available, so we add a failing test
-        class FailWithoutFeature(tests.TestCase):
-            def test_fail(self):
-                self.requireFeature(compiled_annotator_feature)
-        suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
-    result = tests.multiply_tests(standard_tests, scenarios, suite)
-    return result
-
-
-compiled_annotator_feature = tests.ModuleAvailableFeature(
-                                'bzrlib._annotator_pyx')
+    suite, _ = tests.permute_tests_for_extension(standard_tests, loader,
+        'bzrlib._annotator_py', 'bzrlib._annotator_pyx')
+    return suite
 
 
 class TestAnnotator(tests.TestCaseWithMemoryTransport):

=== renamed file 'bzrlib/tests/test_bencode.py' => 'bzrlib/tests/test__bencode.py'
--- a/bzrlib/tests/test_bencode.py	2009-12-22 15:50:40 +0000
+++ b/bzrlib/tests/test__bencode.py	2009-12-23 04:19:34 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2007 Canonical Ltd
+# Copyright (C) 2007, 2009 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -19,35 +19,21 @@
 from bzrlib import tests
 
 def load_tests(standard_tests, module, loader):
-    # parameterize all tests in this module
-    suite = loader.suiteClass()
-    import bzrlib.util._bencode_py as py_module
-    scenarios = [('python', {'bencode': py_module})]
-    if compiled_bencode_feature.available():
-        scenarios.append(('C', {'bencode': compiled_bencode_feature.module}))
-    else:
-        # the compiled module isn't available, so we add a failing test
-        class FailWithoutFeature(tests.TestCase):
-            def test_fail(self):
-                self.requireFeature(compiled_bencode_feature)
-        suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
-    tests.multiply_tests(standard_tests, scenarios, suite)
+    suite, _ = tests.permute_tests_for_extension(standard_tests, loader,
+        'bzrlib.util._bencode_py', 'bzrlib._bencode_pyx')
     return suite
 
 
-compiled_bencode_feature = tests.ModuleAvailableFeature('bzrlib._bencode_pyx')
-
-
 class TestBencodeDecode(tests.TestCase):
 
-    bencode = None
+    module = None
 
     def _check(self, expected, source):
-        self.assertEquals(expected, self.bencode.bdecode(source))
+        self.assertEquals(expected, self.module.bdecode(source))
 
     def _run_check_error(self, exc, bad):
         """Check that bdecoding a string raises a particular exception."""
-        self.assertRaises(exc, self.bencode.bdecode, bad)
+        self.assertRaises(exc, self.module.bdecode, bad)
 
     def test_int(self):
         self._check(0, 'i0e')
@@ -78,7 +64,7 @@
         self._check('1234567890', '10:1234567890')
 
     def test_large_string(self):
-        self.assertRaises(ValueError, self.bencode.bdecode, "2147483639:foo")
+        self.assertRaises(ValueError, self.module.bdecode, "2147483639:foo")
 
     def test_malformed_string(self):
         self._run_check_error(ValueError, '10:x')
@@ -133,7 +119,7 @@
         self._run_check_error(ValueError, 'd432432432432432432:e')
 
     def test_empty_string(self):
-        self.assertRaises(ValueError, self.bencode.bdecode, '')
+        self.assertRaises(ValueError, self.module.bdecode, '')
 
     def test_junk(self):
         self._run_check_error(ValueError, 'i6easd')
@@ -142,7 +128,7 @@
         self._run_check_error(ValueError, 'leanfdldjfh')
 
     def test_unknown_object(self):
-        self.assertRaises(ValueError, self.bencode.bdecode, 'relwjhrlewjh')
+        self.assertRaises(ValueError, self.module.bdecode, 'relwjhrlewjh')
 
     def test_unsupported_type(self):
         self._run_check_error(TypeError, float(1.5))
@@ -152,15 +138,15 @@
         self._run_check_error(TypeError, u"ie")
 
     def test_decoder_type_error(self):
-        self.assertRaises(TypeError, self.bencode.bdecode, 1)
+        self.assertRaises(TypeError, self.module.bdecode, 1)
 
 
 class TestBencodeEncode(tests.TestCase):
 
-    bencode = None
+    module = None
 
     def _check(self, expected, source):
-        self.assertEquals(expected, self.bencode.bencode(source))
+        self.assertEquals(expected, self.module.bencode(source))
 
     def test_int(self):
         self._check('i4e', 4)
@@ -192,7 +178,7 @@
         for i in range(10000):
             l.append([])
             l = l[0]
-        self.assertRaises(RuntimeError, self.bencode.bencode, 
+        self.assertRaises(RuntimeError, self.module.bencode, 
             top)
 
     def test_dict(self):
@@ -207,14 +193,14 @@
         for i in range(10000):
             d[''] = {}
             d = d['']
-        self.assertRaises(RuntimeError, self.bencode.bencode, 
+        self.assertRaises(RuntimeError, self.module.bencode, 
             top)
 
     def test_bencached(self):
-        self._check('i3e', self.bencode.Bencached(self.bencode.bencode(3)))
+        self._check('i3e', self.module.Bencached(self.module.bencode(3)))
 
     def test_invalid_dict(self):
-        self.assertRaises(TypeError, self.bencode.bencode, {1:"foo"})
+        self.assertRaises(TypeError, self.module.bencode, {1:"foo"})
 
     def test_bool(self):
         self._check('i1e', True)

=== modified file 'bzrlib/tests/test__chk_map.py'
--- a/bzrlib/tests/test__chk_map.py	2009-12-22 15:50:40 +0000
+++ b/bzrlib/tests/test__chk_map.py	2009-12-22 16:28:47 +0000
@@ -25,25 +25,11 @@
 
 
 def load_tests(standard_tests, module, loader):
-    # parameterize all tests in this module
-    suite = loader.suiteClass()
-    import bzrlib._chk_map_py as py_module
-    scenarios = [('python', {'module': py_module})]
-    if compiled_chkmap_feature.available():
-        scenarios.append(('C', {'module': compiled_chkmap_feature.module}))
-    else:
-        # the compiled module isn't available, so we add a failing test
-        class FailWithoutFeature(tests.TestCase):
-            def test_fail(self):
-                self.requireFeature(compiled_chkmap_feature)
-        suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
-    tests.multiply_tests(standard_tests, scenarios, suite)
+    suite, _ = tests.permute_tests_for_extension(standard_tests, loader,
+        'bzrlib._chk_map_py', 'bzrlib._chk_map_pyx')
     return suite
 
 
-compiled_chkmap_feature = tests.ModuleAvailableFeature('bzrlib._chk_map_pyx')
-
-
 class TestSearchKeys(tests.TestCase):
 
     module = None # Filled in by test parameterization

=== modified file 'bzrlib/tests/test__chunks_to_lines.py'
--- a/bzrlib/tests/test__chunks_to_lines.py	2009-12-22 15:50:40 +0000
+++ b/bzrlib/tests/test__chunks_to_lines.py	2009-12-22 17:14:45 +0000
@@ -21,21 +21,14 @@
 
 
 def load_tests(standard_tests, module, loader):
-    # parameterize all tests in this module
-    import bzrlib._chunks_to_lines_py as py_module
-    scenarios = [('python', {'module': py_module})]
-    if compiled_chunkstolines_feature.available():
-        scenarios.append(('C', {'module':
-                                compiled_chunkstolines_feature.module}))
-    else:
-        # the compiled module isn't available, so we add a failing test
-        class FailWithoutFeature(tests.TestCase):
-            def test_fail(self):
-                self.requireFeature(compiled_chunkstolines_feature)
-        standard_tests.addTest(FailWithoutFeature("test_fail"))
-    return tests.multiply_tests(standard_tests, scenarios, loader.suiteClass())
-
-
+    suite, _ = tests.permute_tests_for_extension(
+        standard_tests, loader, 'bzrlib._chunks_to_lines_py',
+        'bzrlib._chunks_to_lines_pyx')
+    return suite
+
+# test_osutils depends on this feature being around. We can't just use the one
+# generated by load_tests, because if we only load osutils but not this module,
+# then that code never gets run
 compiled_chunkstolines_feature = tests.ModuleAvailableFeature(
                                     'bzrlib._chunks_to_lines_pyx')
 

=== modified file 'bzrlib/tests/test__rio.py'
--- a/bzrlib/tests/test__rio.py	2009-12-22 15:50:40 +0000
+++ b/bzrlib/tests/test__rio.py	2009-12-22 16:28:47 +0000
@@ -23,25 +23,11 @@
 
 
 def load_tests(standard_tests, module, loader):
-    # parameterize all tests in this module
-    suite = loader.suiteClass()
-    import bzrlib._rio_py as py_module
-    scenarios = [('python', {'module': py_module})]
-    if compiled_rio_feature.available():
-        scenarios.append(('C', {'module': compiled_rio_feature.module}))
-    else:
-        # the compiled module isn't available, so we add a failing test
-        class FailWithoutFeature(tests.TestCase):
-            def test_fail(self):
-                self.requireFeature(compiled_rio_feature)
-        suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
-    tests.multiply_tests(standard_tests, scenarios, suite)
+    suite, _ = tests.permute_tests_for_extension(standard_tests, loader,
+        'bzrlib._rio_py', 'bzrlib._rio_pyx')
     return suite
 
 
-compiled_rio_feature = tests.ModuleAvailableFeature('bzrlib._rio_pyx')
-
-
 class TestValidTag(tests.TestCase):
 
     module = None # Filled in by test parameterization

=== modified file 'bzrlib/tests/test__static_tuple.py'
--- a/bzrlib/tests/test__static_tuple.py	2009-12-22 15:50:40 +0000
+++ b/bzrlib/tests/test__static_tuple.py	2009-12-22 16:28:47 +0000
@@ -32,25 +32,11 @@
 
 def load_tests(standard_tests, module, loader):
     """Parameterize tests for all versions of groupcompress."""
-    scenarios = [
-        ('python', {'module': _static_tuple_py}),
-    ]
-    suite = loader.suiteClass()
-    if compiled_static_tuple_feature.available():
-        scenarios.append(('C', {'module':
-                                compiled_static_tuple_feature.module}))
-    else:
-        # the compiled module isn't available, so we add a failing test
-        class FailWithoutFeature(tests.TestCase):
-            def test_fail(self):
-                self.requireFeature(compiled_static_tuple_feature)
-        suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
-    result = tests.multiply_tests(standard_tests, scenarios, suite)
-    return result
-
-
-compiled_static_tuple_feature = tests.ModuleAvailableFeature(
-                                    'bzrlib._static_tuple_c')
+    global compiled_static_tuple_feature
+    suite, compiled_static_tuple_feature = tests.permute_tests_for_extension(
+        standard_tests, loader, 'bzrlib._static_tuple_py',
+        'bzrlib._static_tuple_c')
+    return suite
 
 
 class _Meliae(tests.Feature):

=== modified file 'doc/developers/testing.txt'
--- a/doc/developers/testing.txt	2009-12-22 06:07:26 +0000
+++ b/doc/developers/testing.txt	2009-12-23 05:04:12 +0000
@@ -173,15 +173,21 @@
 
 Per-implementation tests are tests that are defined once and then run
 against multiple implementations of an interface.  For example,
-``test_transport_implementations.py`` defines tests that all Transport
-implementations (local filesystem, HTTP, and so on) must pass.
-
-They are found in ``bzrlib/tests/*_implementations/test_*.py``,
-``bzrlib/tests/per_*/*.py``, and
-``bzrlib/tests/test_*_implementations.py``.
+``per_transport.py`` defines tests that all Transport implementations
+(local filesystem, HTTP, and so on) must pass. They are found in
+``bzrlib/tests/per_*/*.py``, and ``bzrlib/tests/per_*.py``.
 
 These are really a sub-category of unit tests, but an important one.
 
+Along the same lines are tests for extension modules. We generally have
+both a pure-python and a compiled implementation for each module. As such,
+we want to run the same tests against both implementations. These can
+generally be found in ``bzrlib/tests/*__*.py`` since extension modules are
+usually prefixed with an underscore. Since there are only two
+implementations, we have a helper function
+``bzrlib.tests.permute_for_extension``, which can simplify the
+``load_tests`` implementation.
+
 
 Doctests
 ~~~~~~~~




More information about the bazaar-commits mailing list