~lifeless/bzr/index.range_map

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Robert Collins
  • Date: 2008-06-19 01:17:19 UTC
  • mfrom: (3218.1.277 +trunk)
  • Revision ID: robertc@robertcollins.net-20080619011719-1c4g4uxzzhdls2wf
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
42
42
from subprocess import Popen, PIPE
43
43
import sys
44
44
import tempfile
 
45
import threading
45
46
import time
46
47
import unittest
47
48
import warnings
73
74
from bzrlib.merge import merge_inner
74
75
import bzrlib.merge3
75
76
import bzrlib.plugin
76
 
from bzrlib.revision import common_ancestor
77
77
import bzrlib.store
78
78
from bzrlib import symbol_versioning
79
79
from bzrlib.symbol_versioning import (
81
81
    deprecated_function,
82
82
    deprecated_method,
83
83
    deprecated_passed,
84
 
    zero_ninetyone,
85
 
    zero_ninetytwo,
86
 
    one_zero,
87
84
    )
88
85
import bzrlib.trace
89
86
from bzrlib.transport import get_transport
109
106
 
110
107
default_transport = LocalURLServer
111
108
 
112
 
MODULES_TO_TEST = []
113
 
MODULES_TO_DOCTEST = [
114
 
        bzrlib.timestamp,
115
 
        bzrlib.errors,
116
 
        bzrlib.export,
117
 
        bzrlib.inventory,
118
 
        bzrlib.iterablefile,
119
 
        bzrlib.lockdir,
120
 
        bzrlib.merge3,
121
 
        bzrlib.option,
122
 
        bzrlib.store,
123
 
        bzrlib.version_info_formats.format_custom,
124
 
        # quoted to avoid module-loading circularity
125
 
        'bzrlib.tests',
126
 
        ]
127
 
 
128
 
 
129
 
def packages_to_test():
130
 
    """Return a list of packages to test.
131
 
 
132
 
    The packages are not globally imported so that import failures are
133
 
    triggered when running selftest, not when importing the command.
134
 
    """
135
 
    import bzrlib.doc
136
 
    import bzrlib.tests.blackbox
137
 
    import bzrlib.tests.branch_implementations
138
 
    import bzrlib.tests.bzrdir_implementations
139
 
    import bzrlib.tests.commands
140
 
    import bzrlib.tests.interrepository_implementations
141
 
    import bzrlib.tests.interversionedfile_implementations
142
 
    import bzrlib.tests.intertree_implementations
143
 
    import bzrlib.tests.inventory_implementations
144
 
    import bzrlib.tests.per_lock
145
 
    import bzrlib.tests.repository_implementations
146
 
    import bzrlib.tests.revisionstore_implementations
147
 
    import bzrlib.tests.tree_implementations
148
 
    import bzrlib.tests.workingtree_implementations
149
 
    return [
150
 
            bzrlib.doc,
151
 
            bzrlib.tests.blackbox,
152
 
            bzrlib.tests.branch_implementations,
153
 
            bzrlib.tests.bzrdir_implementations,
154
 
            bzrlib.tests.commands,
155
 
            bzrlib.tests.interrepository_implementations,
156
 
            bzrlib.tests.interversionedfile_implementations,
157
 
            bzrlib.tests.intertree_implementations,
158
 
            bzrlib.tests.inventory_implementations,
159
 
            bzrlib.tests.per_lock,
160
 
            bzrlib.tests.repository_implementations,
161
 
            bzrlib.tests.revisionstore_implementations,
162
 
            bzrlib.tests.tree_implementations,
163
 
            bzrlib.tests.workingtree_implementations,
164
 
            ]
165
 
 
166
109
 
167
110
class ExtendedTestResult(unittest._TextTestResult):
168
111
    """Accepts, reports and accumulates the results of running tests.
314
257
        self.report_success(test)
315
258
        self._cleanupLogFile(test)
316
259
        unittest.TestResult.addSuccess(self, test)
 
260
        test._log_contents = ''
317
261
 
318
262
    def _testConcluded(self, test):
319
263
        """Common code when a test has finished.
356
300
            # seems best to treat this as success from point-of-view of unittest
357
301
            # -- it actually does nothing so it barely matters :)
358
302
            unittest.TestResult.addSuccess(self, test)
 
303
            test._log_contents = ''
359
304
 
360
305
    def printErrorList(self, flavour, errors):
361
306
        for test, err in errors:
416
361
        self.pb.update('[test 0/%d] starting...' % (self.num_tests))
417
362
 
418
363
    def _progress_prefix_text(self):
419
 
        a = '[%d' % self.count
 
364
        # the longer this text, the less space we have to show the test
 
365
        # name...
 
366
        a = '[%d' % self.count              # total that have been run
 
367
        # tests skipped as known not to be relevant are not important enough
 
368
        # to show here
 
369
        ## if self.skip_count:
 
370
        ##     a += ', %d skip' % self.skip_count
 
371
        ## if self.known_failure_count:
 
372
        ##     a += '+%dX' % self.known_failure_count
420
373
        if self.num_tests is not None:
421
374
            a +='/%d' % self.num_tests
422
 
        a += ' in %ds' % (time.time() - self._overall_start_time)
 
375
        a += ' in '
 
376
        runtime = time.time() - self._overall_start_time
 
377
        if runtime >= 60:
 
378
            a += '%dm%ds' % (runtime / 60, runtime % 60)
 
379
        else:
 
380
            a += '%ds' % runtime
423
381
        if self.error_count:
424
 
            a += ', %d errors' % self.error_count
 
382
            a += ', %d err' % self.error_count
425
383
        if self.failure_count:
426
 
            a += ', %d failed' % self.failure_count
427
 
        if self.known_failure_count:
428
 
            a += ', %d known failures' % self.known_failure_count
429
 
        if self.skip_count:
430
 
            a += ', %d skipped' % self.skip_count
 
384
            a += ', %d fail' % self.failure_count
431
385
        if self.unsupported:
432
 
            a += ', %d missing features' % len(self.unsupported)
 
386
            a += ', %d missing' % len(self.unsupported)
433
387
        a += ']'
434
388
        return a
435
389
 
763
717
        return password
764
718
 
765
719
 
 
720
def _report_leaked_threads():
 
721
    bzrlib.trace.warning('%s is leaking threads among %d leaking tests',
 
722
                         TestCase._first_thread_leaker_id,
 
723
                         TestCase._leaking_threads_tests)
 
724
 
 
725
 
766
726
class TestCase(unittest.TestCase):
767
727
    """Base class for bzr unit tests.
768
728
    
784
744
    accidentally overlooked.
785
745
    """
786
746
 
 
747
    _active_threads = None
 
748
    _leaking_threads_tests = 0
 
749
    _first_thread_leaker_id = None
787
750
    _log_file_name = None
788
751
    _log_contents = ''
789
752
    _keep_log_file = False
790
753
    # record lsprof data when performing benchmark calls.
791
754
    _gather_lsprof_in_benchmarks = False
 
755
    attrs_to_keep = ('id', '_testMethodName', '_testMethodDoc',
 
756
                     '_log_contents', '_log_file_name', '_benchtime',
 
757
                     '_TestCase__testMethodName')
792
758
 
793
759
    def __init__(self, methodName='testMethod'):
794
760
        super(TestCase, self).__init__(methodName)
797
763
    def setUp(self):
798
764
        unittest.TestCase.setUp(self)
799
765
        self._cleanEnvironment()
800
 
        bzrlib.trace.disable_default_logging()
801
766
        self._silenceUI()
802
767
        self._startLogFile()
803
768
        self._benchcalls = []
804
769
        self._benchtime = None
805
770
        self._clear_hooks()
806
771
        self._clear_debug_flags()
 
772
        TestCase._active_threads = threading.activeCount()
 
773
        self.addCleanup(self._check_leaked_threads)
 
774
 
 
775
    def _check_leaked_threads(self):
 
776
        active = threading.activeCount()
 
777
        leaked_threads = active - TestCase._active_threads
 
778
        TestCase._active_threads = active
 
779
        if leaked_threads:
 
780
            TestCase._leaking_threads_tests += 1
 
781
            if TestCase._first_thread_leaker_id is None:
 
782
                TestCase._first_thread_leaker_id = self.id()
 
783
                # we're not specifically told when all tests are finished.
 
784
                # This will do. We use a function to avoid keeping a reference
 
785
                # to a TestCase object.
 
786
                atexit.register(_report_leaked_threads)
807
787
 
808
788
    def _clear_debug_flags(self):
809
789
        """Prevent externally set debug flags affecting tests.
811
791
        Tests that want to use debug flags can just set them in the
812
792
        debug_flags set during setup/teardown.
813
793
        """
814
 
        self._preserved_debug_flags = set(debug.debug_flags)
815
 
        debug.debug_flags.clear()
816
 
        self.addCleanup(self._restore_debug_flags)
 
794
        if 'allow_debug' not in selftest_debug_flags:
 
795
            self._preserved_debug_flags = set(debug.debug_flags)
 
796
            debug.debug_flags.clear()
 
797
            self.addCleanup(self._restore_debug_flags)
817
798
 
818
799
    def _clear_hooks(self):
819
800
        # prevent hooks affecting tests
821
802
        import bzrlib.smart.server
822
803
        self._preserved_hooks = {
823
804
            bzrlib.branch.Branch: bzrlib.branch.Branch.hooks,
 
805
            bzrlib.mutabletree.MutableTree: bzrlib.mutabletree.MutableTree.hooks,
824
806
            bzrlib.smart.server.SmartTCPServer: bzrlib.smart.server.SmartTCPServer.hooks,
825
807
            }
826
808
        self.addCleanup(self._restoreHooks)
879
861
            return
880
862
        if message is None:
881
863
            message = "texts not equal:\n"
 
864
        if a == b + '\n':
 
865
            message = 'first string is missing a final newline.\n'
 
866
        if a + '\n' == b:
 
867
            message = 'second string is missing a final newline.\n'
882
868
        raise AssertionError(message +
883
869
                             self._ndiff_strings(a, b))
884
870
        
1041
1027
        else:
1042
1028
            self.fail('Unexpected success.  Should have failed: %s' % reason)
1043
1029
 
 
1030
    def assertFileEqual(self, content, path):
 
1031
        """Fail if path does not contain 'content'."""
 
1032
        self.failUnlessExists(path)
 
1033
        f = file(path, 'rb')
 
1034
        try:
 
1035
            s = f.read()
 
1036
        finally:
 
1037
            f.close()
 
1038
        self.assertEqualDiff(content, s)
 
1039
 
 
1040
    def failUnlessExists(self, path):
 
1041
        """Fail unless path or paths, which may be abs or relative, exist."""
 
1042
        if not isinstance(path, basestring):
 
1043
            for p in path:
 
1044
                self.failUnlessExists(p)
 
1045
        else:
 
1046
            self.failUnless(osutils.lexists(path),path+" does not exist")
 
1047
 
 
1048
    def failIfExists(self, path):
 
1049
        """Fail if path or paths, which may be abs or relative, exist."""
 
1050
        if not isinstance(path, basestring):
 
1051
            for p in path:
 
1052
                self.failIfExists(p)
 
1053
        else:
 
1054
            self.failIf(osutils.lexists(path),path+" exists")
 
1055
 
1044
1056
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1045
1057
        """A helper for callDeprecated and applyDeprecated.
1046
1058
 
1073
1085
        To test that a deprecated method raises an error, do something like
1074
1086
        this::
1075
1087
 
1076
 
        self.assertRaises(errors.ReservedId,
1077
 
            self.applyDeprecated, zero_ninetyone,
1078
 
                br.append_revision, 'current:')
 
1088
            self.assertRaises(errors.ReservedId,
 
1089
                self.applyDeprecated,
 
1090
                deprecated_in((1, 5, 0)),
 
1091
                br.append_revision,
 
1092
                'current:')
1079
1093
 
1080
1094
        :param deprecation_format: The deprecation format that the callable
1081
1095
            should have been deprecated with. This is the same type as the
1159
1173
        """
1160
1174
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1161
1175
        self._log_file = os.fdopen(fileno, 'w+')
1162
 
        self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
 
1176
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1163
1177
        self._log_file_name = name
1164
1178
        self.addCleanup(self._finishLogFile)
1165
1179
 
1170
1184
        """
1171
1185
        if self._log_file is None:
1172
1186
            return
1173
 
        bzrlib.trace.disable_test_log(self._log_nonce)
 
1187
        bzrlib.trace.pop_log_file(self._log_memento)
1174
1188
        self._log_file.close()
1175
1189
        self._log_file = None
1176
1190
        if not self._keep_log_file:
1202
1216
            'BZREMAIL': None, # may still be present in the environment
1203
1217
            'EMAIL': None,
1204
1218
            'BZR_PROGRESS_BAR': None,
 
1219
            'BZR_LOG': None,
1205
1220
            # SSH Agent
1206
1221
            'SSH_AUTH_SOCK': None,
1207
1222
            # Proxies
1256
1271
                    result.addSuccess(self)
1257
1272
                result.stopTest(self)
1258
1273
                return
1259
 
        return unittest.TestCase.run(self, result)
 
1274
        try:
 
1275
            return unittest.TestCase.run(self, result)
 
1276
        finally:
 
1277
            saved_attrs = {}
 
1278
            absent_attr = object()
 
1279
            for attr_name in self.attrs_to_keep:
 
1280
                attr = getattr(self, attr_name, absent_attr)
 
1281
                if attr is not absent_attr:
 
1282
                    saved_attrs[attr_name] = attr
 
1283
            self.__dict__ = saved_attrs
1260
1284
 
1261
1285
    def tearDown(self):
1262
1286
        self._runCleanups()
1504
1528
            elif isinstance(args[0], basestring):
1505
1529
                args = list(shlex.split(args[0]))
1506
1530
        else:
1507
 
            symbol_versioning.warn(zero_ninetyone %
1508
 
                                   "passing varargs to run_bzr_subprocess",
1509
 
                                   DeprecationWarning, stacklevel=3)
 
1531
            raise ValueError("passing varargs to run_bzr_subprocess")
1510
1532
        process = self.start_bzr_subprocess(args, env_changes=env_changes,
1511
1533
                                            working_dir=working_dir,
1512
1534
                                            allow_plugins=allow_plugins)
2088
2110
    def build_tree_contents(self, shape):
2089
2111
        build_tree_contents(shape)
2090
2112
 
2091
 
    def assertFileEqual(self, content, path):
2092
 
        """Fail if path does not contain 'content'."""
2093
 
        self.failUnlessExists(path)
2094
 
        f = file(path, 'rb')
2095
 
        try:
2096
 
            s = f.read()
2097
 
        finally:
2098
 
            f.close()
2099
 
        self.assertEqualDiff(content, s)
2100
 
 
2101
 
    def failUnlessExists(self, path):
2102
 
        """Fail unless path or paths, which may be abs or relative, exist."""
2103
 
        if not isinstance(path, basestring):
2104
 
            for p in path:
2105
 
                self.failUnlessExists(p)
2106
 
        else:
2107
 
            self.failUnless(osutils.lexists(path),path+" does not exist")
2108
 
 
2109
 
    def failIfExists(self, path):
2110
 
        """Fail if path or paths, which may be abs or relative, exist."""
2111
 
        if not isinstance(path, basestring):
2112
 
            for p in path:
2113
 
                self.failIfExists(p)
2114
 
        else:
2115
 
            self.failIf(osutils.lexists(path),path+" exists")
2116
 
 
2117
2113
    def assertInWorkingTree(self, path, root_path='.', tree=None):
2118
2114
        """Assert whether path or paths are in the WorkingTree"""
2119
2115
        if tree is None:
2268
2264
def condition_id_in_list(id_list):
2269
2265
    """Create a condition filter which verify that test's id in a list.
2270
2266
    
2271
 
    :param name: A TestIdList object.
 
2267
    :param id_list: A TestIdList object.
2272
2268
    :return: A callable that returns True if the test's id appears in the list.
2273
2269
    """
2274
2270
    def condition(test):
2275
 
        return id_list.test_in(test.id())
 
2271
        return id_list.includes(test.id())
 
2272
    return condition
 
2273
 
 
2274
 
 
2275
def condition_id_startswith(start):
 
2276
    """Create a condition filter verifying that test's id starts with a string.
 
2277
    
 
2278
    :param start: A string.
 
2279
    :return: A callable that returns True if the test's id starts with the
 
2280
        given string.
 
2281
    """
 
2282
    def condition(test):
 
2283
        return test.id().startswith(start)
2276
2284
    return condition
2277
2285
 
2278
2286
 
2308
2316
    return TestUtil.TestSuite(result)
2309
2317
 
2310
2318
 
2311
 
def filter_suite_by_re(suite, pattern, exclude_pattern=DEPRECATED_PARAMETER,
2312
 
                       random_order=DEPRECATED_PARAMETER):
 
2319
def filter_suite_by_re(suite, pattern):
2313
2320
    """Create a test suite by filtering another one.
2314
2321
    
2315
2322
    :param suite:           the source suite
2316
2323
    :param pattern:         pattern that names must match
2317
 
    :param exclude_pattern: A pattern that names must not match. This parameter
2318
 
        is deprecated as of bzrlib 1.0. Please use the separate function
2319
 
        exclude_tests_by_re instead.
2320
 
    :param random_order:    If True, tests in the new suite will be put in
2321
 
        random order. This parameter is deprecated as of bzrlib 1.0. Please
2322
 
        use the separate function randomize_suite instead.
2323
2324
    :returns: the newly created suite
2324
2325
    """ 
2325
 
    if deprecated_passed(exclude_pattern):
2326
 
        symbol_versioning.warn(
2327
 
            one_zero % "passing exclude_pattern to filter_suite_by_re",
2328
 
                DeprecationWarning, stacklevel=2)
2329
 
        if exclude_pattern is not None:
2330
 
            suite = exclude_tests_by_re(suite, exclude_pattern)
2331
2326
    condition = condition_id_re(pattern)
2332
2327
    result_suite = filter_suite_by_condition(suite, condition)
2333
 
    if deprecated_passed(random_order):
2334
 
        symbol_versioning.warn(
2335
 
            one_zero % "passing random_order to filter_suite_by_re",
2336
 
                DeprecationWarning, stacklevel=2)
2337
 
        if random_order:
2338
 
            result_suite = randomize_suite(result_suite)
2339
2328
    return result_suite
2340
2329
 
2341
2330
 
2351
2340
    return result_suite
2352
2341
 
2353
2342
 
 
2343
def filter_suite_by_id_startswith(suite, start):
 
2344
    """Create a test suite by filtering another one.
 
2345
 
 
2346
    :param suite: The source suite.
 
2347
    :param start: A string the test id must start with.
 
2348
    :returns: the newly created suite
 
2349
    """
 
2350
    condition = condition_id_startswith(start)
 
2351
    result_suite = filter_suite_by_condition(suite, condition)
 
2352
    return result_suite
 
2353
 
 
2354
 
2354
2355
def exclude_tests_by_re(suite, pattern):
2355
2356
    """Create a test suite which excludes some tests from suite.
2356
2357
 
2385
2386
    return TestUtil.TestSuite(tests)
2386
2387
 
2387
2388
 
2388
 
@deprecated_function(one_zero)
2389
 
def sort_suite_by_re(suite, pattern, exclude_pattern=None,
2390
 
                     random_order=False, append_rest=True):
2391
 
    """DEPRECATED: Create a test suite by sorting another one.
2392
 
 
2393
 
    This method has been decomposed into separate helper methods that should be
2394
 
    called directly:
2395
 
     - filter_suite_by_re
2396
 
     - exclude_tests_by_re
2397
 
     - randomize_suite
2398
 
     - split_suite_by_re
 
2389
def split_suite_by_condition(suite, condition):
 
2390
    """Split a test suite into two by a condition.
2399
2391
    
2400
 
    :param suite:           the source suite
2401
 
    :param pattern:         pattern that names must match in order to go
2402
 
                            first in the new suite
2403
 
    :param exclude_pattern: pattern that names must not match, if any
2404
 
    :param random_order:    if True, tests in the new suite will be put in
2405
 
                            random order (with all tests matching pattern
2406
 
                            first).
2407
 
    :param append_rest:     if False, pattern is a strict filter and not
2408
 
                            just an ordering directive
2409
 
    :returns: the newly created suite
 
2392
    :param suite: The suite to split.
 
2393
    :param condition: The condition to match on. Tests that match this
 
2394
        condition are returned in the first test suite, ones that do not match
 
2395
        are in the second suite.
 
2396
    :return: A tuple of two test suites, where the first contains tests from
 
2397
        suite matching the condition, and the second contains the remainder
 
2398
        from suite. The order within each output suite is the same as it was in
 
2399
        suite.
2410
2400
    """ 
2411
 
    if exclude_pattern is not None:
2412
 
        suite = exclude_tests_by_re(suite, exclude_pattern)
2413
 
    if random_order:
2414
 
        order_changer = randomize_suite
2415
 
    else:
2416
 
        order_changer = preserve_input
2417
 
    if append_rest:
2418
 
        suites = map(order_changer, split_suite_by_re(suite, pattern))
2419
 
        return TestUtil.TestSuite(suites)
2420
 
    else:
2421
 
        return order_changer(filter_suite_by_re(suite, pattern))
 
2401
    matched = []
 
2402
    did_not_match = []
 
2403
    for test in iter_suite_tests(suite):
 
2404
        if condition(test):
 
2405
            matched.append(test)
 
2406
        else:
 
2407
            did_not_match.append(test)
 
2408
    return TestUtil.TestSuite(matched), TestUtil.TestSuite(did_not_match)
2422
2409
 
2423
2410
 
2424
2411
def split_suite_by_re(suite, pattern):
2433
2420
        suite. The order within each output suite is the same as it was in
2434
2421
        suite.
2435
2422
    """ 
2436
 
    matched = []
2437
 
    did_not_match = []
2438
 
    filter_re = re.compile(pattern)
2439
 
    for test in iter_suite_tests(suite):
2440
 
        test_id = test.id()
2441
 
        if filter_re.search(test_id):
2442
 
            matched.append(test)
2443
 
        else:
2444
 
            did_not_match.append(test)
2445
 
    return TestUtil.TestSuite(matched), TestUtil.TestSuite(did_not_match)
 
2423
    return split_suite_by_condition(suite, condition_id_re(pattern))
2446
2424
 
2447
2425
 
2448
2426
def run_suite(suite, name='test', verbose=False, pattern=".*",
2503
2481
    return result.wasSuccessful()
2504
2482
 
2505
2483
 
 
2484
# Controlled by "bzr selftest -E=..." option
 
2485
selftest_debug_flags = set()
 
2486
 
 
2487
 
2506
2488
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
2507
2489
             transport=None,
2508
2490
             test_suite_factory=None,
2514
2496
             exclude_pattern=None,
2515
2497
             strict=False,
2516
2498
             load_list=None,
 
2499
             debug_flags=None,
 
2500
             starting_with=None,
2517
2501
             ):
2518
2502
    """Run the whole test suite under the enhanced runner"""
2519
2503
    # XXX: Very ugly way to do this...
2527
2511
        transport = default_transport
2528
2512
    old_transport = default_transport
2529
2513
    default_transport = transport
 
2514
    global selftest_debug_flags
 
2515
    old_debug_flags = selftest_debug_flags
 
2516
    if debug_flags is not None:
 
2517
        selftest_debug_flags = set(debug_flags)
2530
2518
    try:
2531
2519
        if load_list is None:
2532
2520
            keep_only = None
2533
2521
        else:
2534
2522
            keep_only = load_test_id_list(load_list)
2535
2523
        if test_suite_factory is None:
2536
 
            suite = test_suite(keep_only)
 
2524
            suite = test_suite(keep_only, starting_with)
2537
2525
        else:
2538
2526
            suite = test_suite_factory()
2539
2527
        return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
2548
2536
                     strict=strict)
2549
2537
    finally:
2550
2538
        default_transport = old_transport
 
2539
        selftest_debug_flags = old_debug_flags
2551
2540
 
2552
2541
 
2553
2542
def load_test_id_list(file_name):
2573
2562
    return test_list
2574
2563
 
2575
2564
 
 
2565
def suite_matches_id_list(test_suite, id_list):
 
2566
    """Warns about tests not appearing or appearing more than once.
 
2567
 
 
2568
    :param test_suite: A TestSuite object.
 
2569
    :param test_id_list: The list of test ids that should be found in 
 
2570
         test_suite.
 
2571
 
 
2572
    :return: (absents, duplicates) absents is a list containing the test found
 
2573
        in id_list but not in test_suite, duplicates is a list containing the
 
2574
        test found multiple times in test_suite.
 
2575
 
 
2576
    When using a prefined test id list, it may occurs that some tests do not
 
2577
    exist anymore or that some tests use the same id. This function warns the
 
2578
    tester about potential problems in his workflow (test lists are volatile)
 
2579
    or in the test suite itself (using the same id for several tests does not
 
2580
    help to localize defects).
 
2581
    """
 
2582
    # Build a dict counting id occurrences
 
2583
    tests = dict()
 
2584
    for test in iter_suite_tests(test_suite):
 
2585
        id = test.id()
 
2586
        tests[id] = tests.get(id, 0) + 1
 
2587
 
 
2588
    not_found = []
 
2589
    duplicates = []
 
2590
    for id in id_list:
 
2591
        occurs = tests.get(id, 0)
 
2592
        if not occurs:
 
2593
            not_found.append(id)
 
2594
        elif occurs > 1:
 
2595
            duplicates.append(id)
 
2596
 
 
2597
    return not_found, duplicates
 
2598
 
 
2599
 
2576
2600
class TestIdList(object):
2577
2601
    """Test id list to filter a test suite.
2578
2602
 
2609
2633
                modules[mod_name] = True
2610
2634
        self.modules = modules
2611
2635
 
2612
 
    def is_module_name_used(self, module_name):
 
2636
    def refers_to(self, module_name):
2613
2637
        """Is there tests for the module or one of its sub modules."""
2614
2638
        return self.modules.has_key(module_name)
2615
2639
 
2616
 
    def test_in(self, test_id):
 
2640
    def includes(self, test_id):
2617
2641
        return self.tests.has_key(test_id)
2618
2642
 
2619
2643
 
2620
 
def test_suite(keep_only=None):
 
2644
def test_suite(keep_only=None, starting_with=None):
2621
2645
    """Build and return TestSuite for the whole of bzrlib.
2622
2646
 
2623
2647
    :param keep_only: A list of test ids limiting the suite returned.
2624
2648
 
 
2649
    :param starting_with: An id limiting the suite returned to the tests
 
2650
         starting with it.
 
2651
 
2625
2652
    This function can be replaced if you need to change the default test
2626
2653
    suite on a global basis, but it is not encouraged.
2627
2654
    """
2628
2655
    testmod_names = [
 
2656
                   'bzrlib.doc',
2629
2657
                   'bzrlib.util.tests.test_bencode',
 
2658
                   'bzrlib.tests.blackbox',
 
2659
                   'bzrlib.tests.branch_implementations',
 
2660
                   'bzrlib.tests.bzrdir_implementations',
 
2661
                   'bzrlib.tests.commands',
 
2662
                   'bzrlib.tests.inventory_implementations',
 
2663
                   'bzrlib.tests.interrepository_implementations',
 
2664
                   'bzrlib.tests.intertree_implementations',
 
2665
                   'bzrlib.tests.interversionedfile_implementations',
 
2666
                   'bzrlib.tests.per_lock',
 
2667
                   'bzrlib.tests.repository_implementations',
 
2668
                   'bzrlib.tests.revisionstore_implementations',
2630
2669
                   'bzrlib.tests.test__dirstate_helpers',
2631
2670
                   'bzrlib.tests.test_ancestry',
2632
2671
                   'bzrlib.tests.test_annotate',
2651
2690
                   'bzrlib.tests.test_deprecated_graph',
2652
2691
                   'bzrlib.tests.test_diff',
2653
2692
                   'bzrlib.tests.test_dirstate',
 
2693
                   'bzrlib.tests.test_directory_service',
2654
2694
                   'bzrlib.tests.test_email_message',
2655
2695
                   'bzrlib.tests.test_errors',
2656
2696
                   'bzrlib.tests.test_escaped_store',
2691
2731
                   'bzrlib.tests.test_missing',
2692
2732
                   'bzrlib.tests.test_msgeditor',
2693
2733
                   'bzrlib.tests.test_multiparent',
 
2734
                   'bzrlib.tests.test_mutabletree',
2694
2735
                   'bzrlib.tests.test_nonascii',
2695
2736
                   'bzrlib.tests.test_options',
2696
2737
                   'bzrlib.tests.test_osutils',
2702
2743
                   'bzrlib.tests.test_plugins',
2703
2744
                   'bzrlib.tests.test_progress',
2704
2745
                   'bzrlib.tests.test_rangemap',
 
2746
                   'bzrlib.tests.test_read_bundle',
2705
2747
                   'bzrlib.tests.test_reconfigure',
2706
2748
                   'bzrlib.tests.test_reconcile',
2707
2749
                   'bzrlib.tests.test_registry',
2708
2750
                   'bzrlib.tests.test_remote',
2709
2751
                   'bzrlib.tests.test_repository',
 
2752
                   'bzrlib.tests.per_repository_reference',
2710
2753
                   'bzrlib.tests.test_revert',
2711
2754
                   'bzrlib.tests.test_revision',
2712
 
                   'bzrlib.tests.test_revisionnamespaces',
 
2755
                   'bzrlib.tests.test_revisionspec',
2713
2756
                   'bzrlib.tests.test_revisiontree',
2714
2757
                   'bzrlib.tests.test_rio',
2715
2758
                   'bzrlib.tests.test_sampler',
2737
2780
                   'bzrlib.tests.test_transactions',
2738
2781
                   'bzrlib.tests.test_transform',
2739
2782
                   'bzrlib.tests.test_transport',
 
2783
                   'bzrlib.tests.test_transport_implementations',
2740
2784
                   'bzrlib.tests.test_tree',
2741
2785
                   'bzrlib.tests.test_treebuilder',
2742
2786
                   'bzrlib.tests.test_tsort',
2743
2787
                   'bzrlib.tests.test_tuned_gzip',
2744
2788
                   'bzrlib.tests.test_ui',
 
2789
                   'bzrlib.tests.test_uncommit',
2745
2790
                   'bzrlib.tests.test_upgrade',
2746
2791
                   'bzrlib.tests.test_urlutils',
2747
2792
                   'bzrlib.tests.test_versionedfile',
2754
2799
                   'bzrlib.tests.test_workingtree_4',
2755
2800
                   'bzrlib.tests.test_wsgi',
2756
2801
                   'bzrlib.tests.test_xml',
 
2802
                   'bzrlib.tests.tree_implementations',
 
2803
                   'bzrlib.tests.workingtree_implementations',
2757
2804
                   ]
2758
 
    test_transport_implementations = [
2759
 
        'bzrlib.tests.test_transport_implementations',
2760
 
        'bzrlib.tests.test_read_bundle',
2761
 
        ]
2762
 
    suite = TestUtil.TestSuite()
 
2805
 
2763
2806
    loader = TestUtil.TestLoader()
2764
2807
 
2765
 
    if keep_only is not None:
 
2808
    if starting_with is not None:
 
2809
        # We take precedence over keep_only because *at loading time* using
 
2810
        # both options means we will load less tests for the same final result.
 
2811
        def interesting_module(name):
 
2812
            return (
 
2813
                # Either the module name starts with the specified string
 
2814
                name.startswith(starting_with)
 
2815
                # or it may contain tests starting with the specified string
 
2816
                or starting_with.startswith(name)
 
2817
                )
 
2818
        loader = TestUtil.FilteredByModuleTestLoader(interesting_module)
 
2819
 
 
2820
    elif keep_only is not None:
2766
2821
        id_filter = TestIdList(keep_only)
 
2822
        loader = TestUtil.FilteredByModuleTestLoader(id_filter.refers_to)
 
2823
        def interesting_module(name):
 
2824
            return id_filter.refers_to(name)
 
2825
 
 
2826
    else:
 
2827
        loader = TestUtil.TestLoader()
 
2828
        def interesting_module(name):
 
2829
            # No filtering, all modules are interesting
 
2830
            return True
 
2831
 
 
2832
    suite = loader.suiteClass()
2767
2833
 
2768
2834
    # modules building their suite with loadTestsFromModuleNames
2769
 
    if keep_only is None:
2770
 
        suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
2771
 
    else:
2772
 
        for mod in [m for m in testmod_names
2773
 
                    if id_filter.is_module_name_used(m)]:
2774
 
            mod_suite = loader.loadTestsFromModuleNames([mod])
2775
 
            mod_suite = filter_suite_by_id_list(mod_suite, id_filter)
2776
 
            suite.addTest(mod_suite)
2777
 
 
2778
 
    # modules adapted for transport implementations
2779
 
    from bzrlib.tests.test_transport_implementations import TransportTestProviderAdapter
2780
 
    adapter = TransportTestProviderAdapter()
2781
 
    if keep_only is None:
2782
 
        adapt_modules(test_transport_implementations, adapter, loader, suite)
2783
 
    else:
2784
 
        for mod in [m for m in test_transport_implementations
2785
 
                    if id_filter.is_module_name_used(m)]:
2786
 
            mod_suite = TestUtil.TestSuite()
2787
 
            adapt_modules([mod], adapter, loader, mod_suite)
2788
 
            mod_suite = filter_suite_by_id_list(mod_suite, id_filter)
2789
 
            suite.addTest(mod_suite)
2790
 
 
2791
 
    # modules defining their own test_suite()
2792
 
    for package in [p for p in packages_to_test()
2793
 
                    if (keep_only is None
2794
 
                        or id_filter.is_module_name_used(p.__name__))]:
2795
 
        pack_suite = package.test_suite()
2796
 
        if keep_only is not None:
2797
 
            pack_suite = filter_suite_by_id_list(pack_suite, id_filter)
2798
 
        suite.addTest(pack_suite)
2799
 
 
2800
 
    # XXX: MODULES_TO_TEST should be obsoleted ?
2801
 
    for mod in [m for m in MODULES_TO_TEST
2802
 
                if keep_only is None or id_filter.is_module_name_used(m)]:
2803
 
        mod_suite = loader.loadTestsFromModule(mod)
2804
 
        if keep_only is not None:
2805
 
            mod_suite = filter_suite_by_id_list(mod_suite, id_filter)
2806
 
        suite.addTest(mod_suite)
2807
 
 
2808
 
    for mod in MODULES_TO_DOCTEST:
 
2835
    suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
 
2836
 
 
2837
    modules_to_doctest = [
 
2838
        'bzrlib',
 
2839
        'bzrlib.errors',
 
2840
        'bzrlib.export',
 
2841
        'bzrlib.inventory',
 
2842
        'bzrlib.iterablefile',
 
2843
        'bzrlib.lockdir',
 
2844
        'bzrlib.merge3',
 
2845
        'bzrlib.option',
 
2846
        'bzrlib.store',
 
2847
        'bzrlib.symbol_versioning',
 
2848
        'bzrlib.tests',
 
2849
        'bzrlib.timestamp',
 
2850
        'bzrlib.version_info_formats.format_custom',
 
2851
        ]
 
2852
 
 
2853
    for mod in modules_to_doctest:
 
2854
        if not interesting_module(mod):
 
2855
            # No tests to keep here, move along
 
2856
            continue
2809
2857
        try:
2810
2858
            doc_suite = doctest.DocTestSuite(mod)
2811
2859
        except ValueError, e:
2812
2860
            print '**failed to get doctest for: %s\n%s' % (mod, e)
2813
2861
            raise
2814
 
        if keep_only is not None:
2815
 
            # DocTests may use ids which doesn't contain the module name
2816
 
            doc_suite = filter_suite_by_id_list(doc_suite, id_filter)
2817
2862
        suite.addTest(doc_suite)
2818
2863
 
2819
2864
    default_encoding = sys.getdefaultencoding()
2820
 
    for name, plugin in  [(n, p) for (n, p) in bzrlib.plugin.plugins().items()
2821
 
                          if (keep_only is None
2822
 
                              or id_filter.is_module_name_used(
2823
 
                p.module.__name__))]:
2824
 
        try:
2825
 
            plugin_suite = plugin.test_suite()
2826
 
        except ImportError, e:
2827
 
            bzrlib.trace.warning(
2828
 
                'Unable to test plugin "%s": %s', name, e)
2829
 
        else:
2830
 
            if plugin_suite is not None:
2831
 
                if keep_only is not None:
2832
 
                    plugin_suite = filter_suite_by_id_list(plugin_suite,
2833
 
                                                           id_filter)
2834
 
                suite.addTest(plugin_suite)
 
2865
    for name, plugin in bzrlib.plugin.plugins().items():
 
2866
        if not interesting_module(plugin.module.__name__):
 
2867
            continue
 
2868
        plugin_suite = plugin.test_suite()
 
2869
        # We used to catch ImportError here and turn it into just a warning,
 
2870
        # but really if you don't have --no-plugins this should be a failure.
 
2871
        # mbp 20080213 - see http://bugs.launchpad.net/bugs/189771
 
2872
        if plugin_suite is None:
 
2873
            plugin_suite = plugin.load_plugin_tests(loader)
 
2874
        if plugin_suite is not None:
 
2875
            suite.addTest(plugin_suite)
2835
2876
        if default_encoding != sys.getdefaultencoding():
2836
2877
            bzrlib.trace.warning(
2837
2878
                'Plugin "%s" tried to reset default encoding to: %s', name,
2838
2879
                sys.getdefaultencoding())
2839
2880
            reload(sys)
2840
2881
            sys.setdefaultencoding(default_encoding)
 
2882
 
 
2883
    if starting_with is not None:
 
2884
        suite = filter_suite_by_id_startswith(suite, starting_with)
 
2885
 
 
2886
    if keep_only is not None:
 
2887
        # Now that the referred modules have loaded their tests, keep only the
 
2888
        # requested ones.
 
2889
        suite = filter_suite_by_id_list(suite, id_filter)
 
2890
        # Do some sanity checks on the id_list filtering
 
2891
        not_found, duplicates = suite_matches_id_list(suite, keep_only)
 
2892
        if starting_with is not None:
 
2893
            # The tester has used both keep_only and starting_with, so he is
 
2894
            # already aware that some tests are excluded from the list, there
 
2895
            # is no need to tell him which.
 
2896
            pass
 
2897
        else:
 
2898
            # Some tests mentioned in the list are not in the test suite. The
 
2899
            # list may be out of date, report to the tester.
 
2900
            for id in not_found:
 
2901
                bzrlib.trace.warning('"%s" not found in the test suite', id)
 
2902
        for id in duplicates:
 
2903
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
 
2904
 
2841
2905
    return suite
2842
2906
 
2843
2907
 
2844
 
def multiply_tests_from_modules(module_name_list, scenario_iter):
 
2908
def multiply_tests_from_modules(module_name_list, scenario_iter, loader=None):
2845
2909
    """Adapt all tests in some given modules to given scenarios.
2846
2910
 
2847
2911
    This is the recommended public interface for test parameterization.
2853
2917
        modules.
2854
2918
    :param scenario_iter: Iterable of pairs of (scenario_name, 
2855
2919
        scenario_param_dict).
 
2920
    :param loader: If provided, will be used instead of a new 
 
2921
        bzrlib.tests.TestLoader() instance.
2856
2922
 
2857
2923
    This returns a new TestSuite containing the cross product of
2858
2924
    all the tests in all the modules, each repeated for each scenario.
2874
2940
    >>> tests[1].param
2875
2941
    2
2876
2942
    """
2877
 
    loader = TestLoader()
2878
 
    suite = TestSuite()
 
2943
    # XXX: Isn't load_tests() a better way to provide the same functionality
 
2944
    # without forcing a predefined TestScenarioApplier ? --vila 080215
 
2945
    if loader is None:
 
2946
        loader = TestUtil.TestLoader()
 
2947
 
 
2948
    suite = loader.suiteClass()
 
2949
 
2879
2950
    adapter = TestScenarioApplier()
2880
2951
    adapter.scenarios = list(scenario_iter)
2881
2952
    adapt_modules(module_name_list, adapter, loader, suite)
2899
2970
 
2900
2971
def adapt_modules(mods_list, adapter, loader, suite):
2901
2972
    """Adapt the modules in mods_list using adapter and add to suite."""
2902
 
    for test in iter_suite_tests(loader.loadTestsFromModuleNames(mods_list)):
 
2973
    tests = loader.loadTestsFromModuleNames(mods_list)
 
2974
    adapt_tests(tests, adapter, suite)
 
2975
 
 
2976
 
 
2977
def adapt_tests(tests_list, adapter, suite):
 
2978
    """Adapt the tests in tests_list using adapter and add to suite."""
 
2979
    for test in iter_suite_tests(tests_list):
2903
2980
        suite.addTests(adapter.adapt(test))
2904
2981
 
2905
2982
 
2906
 
def adapt_tests(tests_list, adapter, loader, suite):
2907
 
    """Adapt the tests in tests_list using adapter and add to suite."""
2908
 
    for test in tests_list:
2909
 
        suite.addTests(adapter.adapt(loader.loadTestsFromName(test)))
2910
 
 
2911
 
 
2912
2983
def _rmtree_temp_dir(dirname):
2913
2984
    # If LANG=C we probably have created some bogus paths
2914
2985
    # which rmtree(unicode) will fail to delete
2971
3042
SymlinkFeature = _SymlinkFeature()
2972
3043
 
2973
3044
 
 
3045
class _HardlinkFeature(Feature):
 
3046
 
 
3047
    def _probe(self):
 
3048
        return osutils.has_hardlinks()
 
3049
 
 
3050
    def feature_name(self):
 
3051
        return 'hardlinks'
 
3052
 
 
3053
HardlinkFeature = _HardlinkFeature()
 
3054
 
 
3055
 
2974
3056
class _OsFifoFeature(Feature):
2975
3057
 
2976
3058
    def _probe(self):
2982
3064
OsFifoFeature = _OsFifoFeature()
2983
3065
 
2984
3066
 
 
3067
class _UnicodeFilenameFeature(Feature):
 
3068
    """Does the filesystem support Unicode filenames?"""
 
3069
 
 
3070
    def _probe(self):
 
3071
        try:
 
3072
            os.stat(u'\u03b1')
 
3073
        except UnicodeEncodeError:
 
3074
            return False
 
3075
        except (IOError, OSError):
 
3076
            # The filesystem allows the Unicode filename but the file doesn't
 
3077
            # exist.
 
3078
            return True
 
3079
        else:
 
3080
            # The filesystem allows the Unicode filename and the file exists,
 
3081
            # for some reason.
 
3082
            return True
 
3083
 
 
3084
UnicodeFilenameFeature = _UnicodeFilenameFeature()
 
3085
 
 
3086
 
2985
3087
class TestScenarioApplier(object):
2986
3088
    """A tool to apply scenarios to tests."""
2987
3089