diff --git a/oslo_db/sqlalchemy/test_base.py b/oslo_db/sqlalchemy/test_base.py index bda0d91e..ab7dea20 100644 --- a/oslo_db/sqlalchemy/test_base.py +++ b/oslo_db/sqlalchemy/test_base.py @@ -14,9 +14,9 @@ # under the License. import debtcollector +import debtcollector.moves import fixtures import testresources -import testscenarios try: from oslotest import base as test_base @@ -25,8 +25,6 @@ except ImportError: ' test-requirements') -import os - from oslo_utils import reflection import six @@ -34,6 +32,7 @@ from oslo_db import exception from oslo_db.sqlalchemy import enginefacade from oslo_db.sqlalchemy import provision from oslo_db.sqlalchemy import session +from oslo_db.sqlalchemy.test_fixtures import optimize_package_test_loader @debtcollector.removals.removed_class("DbFixture") @@ -245,39 +244,5 @@ class PostgreSQLOpportunisticTestCase(OpportunisticTestCase): FIXTURE = PostgreSQLOpportunisticFixture -def optimize_db_test_loader(file_): - """Package level load_tests() function. - - Will apply an optimizing test suite to all sub-tests, which groups DB - tests and other resources appropriately. - - Place this in an __init__.py package file within the root of the test - suite, at the level where testresources loads it as a package:: - - from oslo_db.sqlalchemy import test_base - - load_tests = test_base.optimize_db_test_loader(__file__) - - Alternatively, the directive can be placed into a test module directly. - - """ - - this_dir = os.path.dirname(file_) - - def load_tests(loader, found_tests, pattern): - # pattern is None if the directive is placed within - # a test module directly, as well as within certain test - # discovery patterns - - if pattern is not None: - pkg_tests = loader.discover(start_dir=this_dir, pattern=pattern) - - result = testresources.OptimisingTestSuite() - found_tests = testscenarios.load_tests_apply_scenarios( - loader, found_tests, pattern) - result.addTest(found_tests) - - if pattern is not None: - result.addTest(pkg_tests) - return result - return load_tests +optimize_db_test_loader = debtcollector.moves.moved_function( + optimize_package_test_loader, "optimize_db_test_loader", __name__) diff --git a/oslo_db/sqlalchemy/test_fixtures.py b/oslo_db/sqlalchemy/test_fixtures.py index 8b35ac34..fd954b3c 100644 --- a/oslo_db/sqlalchemy/test_fixtures.py +++ b/oslo_db/sqlalchemy/test_fixtures.py @@ -15,7 +15,9 @@ import fixtures import logging +import os import testresources +import testscenarios from oslo_db import exception from oslo_db.sqlalchemy import enginefacade @@ -544,3 +546,79 @@ class MySQLOpportunisticFixture(OpportunisticDbFixture): class PostgresqlOpportunisticFixture(OpportunisticDbFixture): DRIVER = 'postgresql' + + +def optimize_package_test_loader(file_): + """Organize package-level tests into a testresources.OptimizingTestSuite. + + This function provides a unittest-compatible load_tests hook + for a given package; for per-module, use the + :func:`.optimize_module_test_loader` function. + + When a unitest or subunit style + test runner is used, the function will be called in order to + return a TestSuite containing the tests to run; this function + ensures that this suite is an OptimisingTestSuite, which will organize + the production of test resources across groups of tests at once. + + The function is invoked as:: + + from oslo_db.sqlalchemy import test_base + + load_tests = test_base.optimize_package_test_loader(__file__) + + The loader *must* be present in the package level __init__.py. + + The function also applies testscenarios expansion to all test collections. + This so that an existing test suite that already needs to build + TestScenarios from a load_tests call can still have this take place when + replaced with this function. + + """ + + this_dir = os.path.dirname(file_) + + def load_tests(loader, found_tests, pattern): + result = testresources.OptimisingTestSuite() + result.addTests(found_tests) + pkg_tests = loader.discover(start_dir=this_dir, pattern=pattern) + result.addTests(testscenarios.generate_scenarios(pkg_tests)) + + return result + return load_tests + + +def optimize_module_test_loader(): + """Organize module-level tests into a testresources.OptimizingTestSuite. + + This function provides a unittest-compatible load_tests hook + for a given module; for per-package, use the + :func:`.optimize_package_test_loader` function. + + When a unitest or subunit style + test runner is used, the function will be called in order to + return a TestSuite containing the tests to run; this function + ensures that this suite is an OptimisingTestSuite, which will organize + the production of test resources across groups of tests at once. + + The function is invoked as:: + + from oslo_db.sqlalchemy import test_base + + load_tests = test_base.optimize_module_test_loader() + + The loader *must* be present in an individual module, and *not* the + package level __init__.py. + + The function also applies testscenarios expansion to all test collections. + This so that an existing test suite that already needs to build + TestScenarios from a load_tests call can still have this take place when + replaced with this function. + + """ + + def load_tests(loader, found_tests, pattern): + result = testresources.OptimisingTestSuite() + result.addTests(testscenarios.generate_scenarios(found_tests)) + return result + return load_tests diff --git a/oslo_db/tests/sqlalchemy/test_fixtures.py b/oslo_db/tests/sqlalchemy/test_fixtures.py index 44fed1e8..f256d30f 100644 --- a/oslo_db/tests/sqlalchemy/test_fixtures.py +++ b/oslo_db/tests/sqlalchemy/test_fixtures.py @@ -11,7 +11,10 @@ # under the License. import mock +import os import testresources +import testscenarios +import unittest from oslo_db.sqlalchemy import enginefacade from oslo_db.sqlalchemy import provision @@ -19,6 +22,8 @@ from oslo_db.sqlalchemy import test_base as legacy_test_base from oslo_db.sqlalchemy import test_fixtures from oslotest import base as oslo_test_base +start_dir = os.path.dirname(__file__) + class BackendSkipTest(oslo_test_base.BaseTestCase): @@ -212,3 +217,82 @@ class LegacyBaseClassTest(oslo_test_base.BaseTestCase): db_resource = dict(st.resources)['db'] self.assertTrue(db_resource.provision_new_database) + + +class TestLoadHook(unittest.TestCase): + """Test the 'load_tests' hook supplied by test_base. + + The purpose of this loader is to organize tests into an + OptimisingTestSuite using the standard unittest load_tests hook. + The hook needs to detect if it is being invoked at the module + level or at the package level. It has to behave completely differently + in these two cases. + + """ + + def test_module_level(self): + load_tests = test_fixtures.optimize_module_test_loader() + + loader = unittest.TestLoader() + + found_tests = loader.discover(start_dir, pattern="test_fixtures.py") + new_loader = load_tests(loader, found_tests, "test_fixtures.py") + + self.assertTrue( + isinstance(new_loader, testresources.OptimisingTestSuite) + ) + + actual_tests = unittest.TestSuite( + testscenarios.generate_scenarios(found_tests) + ) + + self.assertEqual( + new_loader.countTestCases(), actual_tests.countTestCases() + ) + + def test_package_level(self): + self._test_package_level(test_fixtures.optimize_package_test_loader) + + def test_package_level_legacy(self): + self._test_package_level(legacy_test_base.optimize_db_test_loader) + + def _test_package_level(self, fn): + load_tests = fn( + os.path.join(start_dir, "__init__.py")) + + loader = unittest.TestLoader() + + new_loader = load_tests( + loader, unittest.suite.TestSuite(), "test_fixtures.py") + + self.assertTrue( + isinstance(new_loader, testresources.OptimisingTestSuite) + ) + + actual_tests = unittest.TestSuite( + testscenarios.generate_scenarios( + loader.discover(start_dir, pattern="test_fixtures.py")) + ) + + self.assertEqual( + new_loader.countTestCases(), actual_tests.countTestCases() + ) + + +class TestWScenarios(unittest.TestCase): + """a 'do nothing' test suite. + + Should generate exactly four tests when testscenarios is used. + + """ + + def test_one(self): + pass + + def test_two(self): + pass + + scenarios = [ + ('scenario1', dict(scenario='scenario 1')), + ('scenario2', dict(scenario='scenario 2')) + ]