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):
591
elif isinstance(item, unittest.TestSuite):
588
if isinstance(suite, unittest.TestCase):
590
elif isinstance(suite, unittest.TestSuite):
591
for item in suite._tests:
592
592
for r in iter_suite_tests(item):
595
raise Exception('unknown object %r inside test suite %r'
595
raise Exception('unknown type %r for object %r'
596
% (type(suite), suite))
599
599
class TestSkipped(Exception):
3167
def multiply_tests_from_modules(module_name_list, scenario_iter, loader=None):
3168
"""Adapt all tests in some given modules to given scenarios.
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
3175
:param module_name_list: List of fully-qualified names of test
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.
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.
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))
3196
'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
3202
# XXX: Isn't load_tests() a better way to provide the same functionality
3203
# without forcing a predefined TestScenarioApplier ? --vila 080215
3205
loader = TestUtil.TestLoader()
3207
suite = loader.suiteClass()
3209
adapter = TestScenarioApplier()
3210
adapter.scenarios = list(scenario_iter)
3211
adapt_modules(module_name_list, adapter, loader, suite)
3215
3167
def multiply_scenarios(scenarios_left, scenarios_right):
3216
3168
"""Multiply two sets of scenarios.
3226
3178
for right_name, right_dict in scenarios_right]
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)
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.
3184
This is the core workhorse for test parameterisation.
3186
Typically the load_tests() method for a per-implementation test suite will
3187
call multiply_tests and return the result.
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.
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.
3199
>>> r = multiply_tests(
3200
... bzrlib.tests.test_sampler.DemoTest('test_nothing'),
3201
... [('one', dict(param=1)),
3202
... ('two', dict(param=2))],
3204
>>> tests = list(iter_suite_tests(r))
3208
'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
3214
for test in iter_suite_tests(tests):
3215
apply_scenarios(test, scenarios, result)
3219
def apply_scenarios(test, scenarios, result):
3220
"""Apply the scenarios in scenarios to test and add to result.
3222
:param test: The test to apply scenarios to.
3223
:param scenarios: An iterable of scenarios to apply to test.
3225
:seealso: apply_scenario
3227
for scenario in scenarios:
3228
result.addTest(apply_scenario(test, scenario))
3232
def apply_scenario(test, scenario):
3233
"""Copy test and apply scenario to it.
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
3240
:return: The adapted test.
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)
3249
def clone_test(test, new_id):
3250
"""Clone a test giving it a new id.
3252
:param test: The test to clone.
3253
:param new_id: The id to assign to it.
3254
:return: The new test.
3256
from copy import deepcopy
3257
new_test = deepcopy(test)
3258
new_test.id = lambda: new_id
3242
3262
def _rmtree_temp_dir(dirname):
3347
3367
UnicodeFilenameFeature = _UnicodeFilenameFeature()
3350
class TestScenarioApplier(object):
3351
"""A tool to apply scenarios to tests."""
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))
3360
def adapt_test_to_scenario(self, test, scenario):
3361
"""Copy test and apply scenario to it.
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
3368
:return: The adapted test.
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
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.