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):
3161
def multiply_tests_from_modules(module_name_list, scenario_iter, loader=None):
3162
"""Adapt all tests in some given modules to given scenarios.
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
3169
:param module_name_list: List of fully-qualified names of test
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.
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.
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))
3190
'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
3196
# XXX: Isn't load_tests() a better way to provide the same functionality
3197
# without forcing a predefined TestScenarioApplier ? --vila 080215
3199
loader = TestUtil.TestLoader()
3201
suite = loader.suiteClass()
3203
adapter = TestScenarioApplier()
3204
adapter.scenarios = list(scenario_iter)
3205
adapt_modules(module_name_list, adapter, loader, suite)
3209
3161
def multiply_scenarios(scenarios_left, scenarios_right):
3210
3162
"""Multiply two sets of scenarios.
3220
3172
for right_name, right_dict in scenarios_right]
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)
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.
3178
This is the core workhorse for test parameterisation.
3180
Typically the load_tests() method for a per-implementation test suite will
3181
call multiply_tests and return the result.
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.
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.
3193
>>> r = multiply_tests(
3194
... bzrlib.tests.test_sampler.DemoTest('test_nothing'),
3195
... [('one', dict(param=1)),
3196
... ('two', dict(param=2))],
3198
>>> tests = list(iter_suite_tests(r))
3202
'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
3208
for test in iter_suite_tests(tests):
3209
apply_scenarios(test, scenarios, result)
3213
def apply_scenarios(test, scenarios, result):
3214
"""Apply the scenarios in scenarios to test and add to result.
3216
:param test: The test to apply scenarios to.
3217
:param scenarios: An iterable of scenarios to apply to test.
3219
:seealso: apply_scenario
3221
for scenario in scenarios:
3222
result.addTest(apply_scenario(test, scenario))
3226
def apply_scenario(test, scenario):
3227
"""Copy test and apply scenario to it.
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
3234
:return: The adapted test.
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)
3243
def clone_test(test, new_id):
3244
"""Clone a test giving it a new id.
3246
:param test: The test to clone.
3247
:param new_id: The id to assign to it.
3248
:return: The new test.
3250
from copy import deepcopy
3251
new_test = deepcopy(test)
3252
new_test.id = lambda: new_id
3236
3256
def _rmtree_temp_dir(dirname):
3341
3361
UnicodeFilenameFeature = _UnicodeFilenameFeature()
3344
class TestScenarioApplier(object):
3345
"""A tool to apply scenarios to tests."""
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))
3354
def adapt_test_to_scenario(self, test, scenario):
3355
"""Copy test and apply scenario to it.
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
3362
:return: The adapted test.
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
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.