/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-03-09 08:45:56 UTC
  • mfrom: (4084.5.2 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090309084556-9i2m12qlud2qcrtw
(robertc) Bulk update all test adaptation into a single approach
        using multiply_tests rather than many varied test adapters.
        (Robert Collins

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