~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Robert Collins
  • Date: 2009-03-07 06:58:17 UTC
  • mto: This revision was merged to the branch mainline in revision 4098.
  • Revision ID: robertc@robertcollins.net-20090307065817-btjngdy1cvv4nwfo
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.

Show diffs side-by-side

added added

removed removed

Lines of Context:
585
585
 
586
586
def iter_suite_tests(suite):
587
587
    """Return all tests in a suite, recursing through nested suites"""
588
 
    for item in suite._tests:
589
 
        if isinstance(item, unittest.TestCase):
590
 
            yield item
591
 
        elif isinstance(item, unittest.TestSuite):
 
588
    if isinstance(suite, unittest.TestCase):
 
589
        yield suite
 
590
    elif isinstance(suite, unittest.TestSuite):
 
591
        for item in suite._tests:
592
592
            for r in iter_suite_tests(item):
593
593
                yield r
594
 
        else:
595
 
            raise Exception('unknown object %r inside test suite %r'
596
 
                            % (item, suite))
 
594
    else:
 
595
        raise Exception('unknown type %r for object %r'
 
596
                        % (type(suite), suite))
597
597
 
598
598
 
599
599
class TestSkipped(Exception):
3158
3158
    return suite
3159
3159
 
3160
3160
 
3161
 
def multiply_tests_from_modules(module_name_list, scenario_iter, loader=None):
3162
 
    """Adapt all tests in some given modules to given scenarios.
3163
 
 
3164
 
    This is the recommended public interface for test parameterization.
3165
 
    Typically the test_suite() method for a per-implementation test
3166
 
    suite will call multiply_tests_from_modules and return the
3167
 
    result.
3168
 
 
3169
 
    :param module_name_list: List of fully-qualified names of test
3170
 
        modules.
3171
 
    :param scenario_iter: Iterable of pairs of (scenario_name,
3172
 
        scenario_param_dict).
3173
 
    :param loader: If provided, will be used instead of a new
3174
 
        bzrlib.tests.TestLoader() instance.
3175
 
 
3176
 
    This returns a new TestSuite containing the cross product of
3177
 
    all the tests in all the modules, each repeated for each scenario.
3178
 
    Each test is adapted by adding the scenario name at the end
3179
 
    of its name, and updating the test object's __dict__ with the
3180
 
    scenario_param_dict.
3181
 
 
3182
 
    >>> r = multiply_tests_from_modules(
3183
 
    ...     ['bzrlib.tests.test_sampler'],
3184
 
    ...     [('one', dict(param=1)),
3185
 
    ...      ('two', dict(param=2))])
3186
 
    >>> tests = list(iter_suite_tests(r))
3187
 
    >>> len(tests)
3188
 
    2
3189
 
    >>> tests[0].id()
3190
 
    'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
3191
 
    >>> tests[0].param
3192
 
    1
3193
 
    >>> tests[1].param
3194
 
    2
3195
 
    """
3196
 
    # XXX: Isn't load_tests() a better way to provide the same functionality
3197
 
    # without forcing a predefined TestScenarioApplier ? --vila 080215
3198
 
    if loader is None:
3199
 
        loader = TestUtil.TestLoader()
3200
 
 
3201
 
    suite = loader.suiteClass()
3202
 
 
3203
 
    adapter = TestScenarioApplier()
3204
 
    adapter.scenarios = list(scenario_iter)
3205
 
    adapt_modules(module_name_list, adapter, loader, suite)
3206
 
    return suite
3207
 
 
3208
 
 
3209
3161
def multiply_scenarios(scenarios_left, scenarios_right):
3210
3162
    """Multiply two sets of scenarios.
3211
3163
 
3220
3172
        for right_name, right_dict in scenarios_right]
3221
3173
 
3222
3174
 
3223
 
 
3224
 
def adapt_modules(mods_list, adapter, loader, suite):
3225
 
    """Adapt the modules in mods_list using adapter and add to suite."""
3226
 
    tests = loader.loadTestsFromModuleNames(mods_list)
3227
 
    adapt_tests(tests, adapter, suite)
3228
 
 
3229
 
 
3230
 
def adapt_tests(tests_list, adapter, suite):
3231
 
    """Adapt the tests in tests_list using adapter and add to suite."""
3232
 
    for test in iter_suite_tests(tests_list):
3233
 
        suite.addTests(adapter.adapt(test))
 
3175
def multiply_tests(tests, scenarios, result):
 
3176
    """Multiply tests_list by scenarios into result.
 
3177
 
 
3178
    This is the core workhorse for test parameterisation.
 
3179
 
 
3180
    Typically the load_tests() method for a per-implementation test suite will
 
3181
    call multiply_tests and return the result.
 
3182
 
 
3183
    :param tests: The tests to parameterise.
 
3184
    :param scenarios: The scenarios to apply: pairs of (scenario_name,
 
3185
        scenario_param_dict).
 
3186
    :param result: A TestSuite to add created tests to.
 
3187
 
 
3188
    This returns the passed in result TestSuite with the cross product of all
 
3189
    the tests repeated once for each scenario.  Each test is adapted by adding
 
3190
    the scenario name at the end of its id(), and updating the test object's
 
3191
    __dict__ with the scenario_param_dict.
 
3192
 
 
3193
    >>> r = multiply_tests(
 
3194
    ...     bzrlib.tests.test_sampler.DemoTest('test_nothing'),
 
3195
    ...     [('one', dict(param=1)),
 
3196
    ...      ('two', dict(param=2))],
 
3197
    ...     TestSuite())
 
3198
    >>> tests = list(iter_suite_tests(r))
 
3199
    >>> len(tests)
 
3200
    2
 
3201
    >>> tests[0].id()
 
3202
    'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
 
3203
    >>> tests[0].param
 
3204
    1
 
3205
    >>> tests[1].param
 
3206
    2
 
3207
    """
 
3208
    for test in iter_suite_tests(tests):
 
3209
        apply_scenarios(test, scenarios, result)
 
3210
    return result
 
3211
 
 
3212
 
 
3213
def apply_scenarios(test, scenarios, result):
 
3214
    """Apply the scenarios in scenarios to test and add to result.
 
3215
 
 
3216
    :param test: The test to apply scenarios to.
 
3217
    :param scenarios: An iterable of scenarios to apply to test.
 
3218
    :return: result
 
3219
    :seealso: apply_scenario
 
3220
    """
 
3221
    for scenario in scenarios:
 
3222
        result.addTest(apply_scenario(test, scenario))
 
3223
    return result
 
3224
 
 
3225
 
 
3226
def apply_scenario(test, scenario):
 
3227
    """Copy test and apply scenario to it.
 
3228
 
 
3229
    :param test: A test to adapt.
 
3230
    :param scenario: A tuple describing the scenarion.
 
3231
        The first element of the tuple is the new test id.
 
3232
        The second element is a dict containing attributes to set on the
 
3233
        test.
 
3234
    :return: The adapted test.
 
3235
    """
 
3236
    new_id = "%s(%s)" % (test.id(), scenario[0])
 
3237
    new_test = clone_test(test, new_id)
 
3238
    for name, value in scenario[1].items():
 
3239
        setattr(new_test, name, value)
 
3240
    return new_test
 
3241
 
 
3242
 
 
3243
def clone_test(test, new_id):
 
3244
    """Clone a test giving it a new id.
 
3245
 
 
3246
    :param test: The test to clone.
 
3247
    :param new_id: The id to assign to it.
 
3248
    :return: The new test.
 
3249
    """
 
3250
    from copy import deepcopy
 
3251
    new_test = deepcopy(test)
 
3252
    new_test.id = lambda: new_id
 
3253
    return new_test
3234
3254
 
3235
3255
 
3236
3256
def _rmtree_temp_dir(dirname):
3341
3361
UnicodeFilenameFeature = _UnicodeFilenameFeature()
3342
3362
 
3343
3363
 
3344
 
class TestScenarioApplier(object):
3345
 
    """A tool to apply scenarios to tests."""
3346
 
 
3347
 
    def adapt(self, test):
3348
 
        """Return a TestSuite containing a copy of test for each scenario."""
3349
 
        result = unittest.TestSuite()
3350
 
        for scenario in self.scenarios:
3351
 
            result.addTest(self.adapt_test_to_scenario(test, scenario))
3352
 
        return result
3353
 
 
3354
 
    def adapt_test_to_scenario(self, test, scenario):
3355
 
        """Copy test and apply scenario to it.
3356
 
 
3357
 
        :param test: A test to adapt.
3358
 
        :param scenario: A tuple describing the scenarion.
3359
 
            The first element of the tuple is the new test id.
3360
 
            The second element is a dict containing attributes to set on the
3361
 
            test.
3362
 
        :return: The adapted test.
3363
 
        """
3364
 
        from copy import deepcopy
3365
 
        new_test = deepcopy(test)
3366
 
        for name, value in scenario[1].items():
3367
 
            setattr(new_test, name, value)
3368
 
        new_id = "%s(%s)" % (new_test.id(), scenario[0])
3369
 
        new_test.id = lambda: new_id
3370
 
        return new_test
3371
 
 
3372
 
 
3373
3364
def probe_unicode_in_user_encoding():
3374
3365
    """Try to encode several unicode strings to use in unicode-aware tests.
3375
3366
    Return first successfull match.