~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Aaron Bentley
  • Date: 2006-02-22 14:39:42 UTC
  • mto: (2027.1.2 revert-subpath-56549)
  • mto: This revision was merged to the branch mainline in revision 1570.
  • Revision ID: abentley@panoramicfeedback.com-20060222143942-ae72299f2de66767
Fixed build_tree with symlinks

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
# little as possible, so this should be used rarely if it's added at all.
22
22
# (Suggestion from j-a-meinel, 2005-11-24)
23
23
 
 
24
import codecs
24
25
from cStringIO import StringIO
25
26
import difflib
26
27
import errno
28
29
import os
29
30
import re
30
31
import shutil
 
32
import stat
31
33
import sys
32
34
import tempfile
33
35
import unittest
34
36
import time
35
 
import codecs
 
37
 
36
38
 
37
39
import bzrlib.branch
 
40
import bzrlib.bzrdir as bzrdir
38
41
import bzrlib.commands
39
 
from bzrlib.errors import BzrError
 
42
import bzrlib.errors as errors
40
43
import bzrlib.inventory
 
44
import bzrlib.iterablefile
41
45
import bzrlib.merge3
42
46
import bzrlib.osutils
43
47
import bzrlib.osutils as osutils
44
48
import bzrlib.plugin
45
49
import bzrlib.store
46
50
import bzrlib.trace
 
51
from bzrlib.transport import urlescape
 
52
import bzrlib.transport
 
53
from bzrlib.transport.local import LocalRelpathServer
 
54
from bzrlib.transport.readonly import ReadonlyServer
47
55
from bzrlib.trace import mutter
48
56
from bzrlib.tests.TestUtil import TestLoader, TestSuite
49
57
from bzrlib.tests.treeshape import build_tree_contents
 
58
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
 
59
 
 
60
default_transport = LocalRelpathServer
50
61
 
51
62
MODULES_TO_TEST = []
52
63
MODULES_TO_DOCTEST = [
54
65
                      bzrlib.commands,
55
66
                      bzrlib.errors,
56
67
                      bzrlib.inventory,
 
68
                      bzrlib.iterablefile,
57
69
                      bzrlib.merge3,
 
70
                      bzrlib.option,
58
71
                      bzrlib.osutils,
59
 
                      bzrlib.store,
 
72
                      bzrlib.store
60
73
                      ]
61
74
def packages_to_test():
 
75
    """Return a list of packages to test.
 
76
 
 
77
    The packages are not globally imported so that import failures are
 
78
    triggered when running selftest, not when importing the command.
 
79
    """
 
80
    import bzrlib.doc
62
81
    import bzrlib.tests.blackbox
 
82
    import bzrlib.tests.branch_implementations
 
83
    import bzrlib.tests.bzrdir_implementations
 
84
    import bzrlib.tests.repository_implementations
 
85
    import bzrlib.tests.workingtree_implementations
63
86
    return [
64
 
            bzrlib.tests.blackbox
 
87
            bzrlib.doc,
 
88
            bzrlib.tests.blackbox,
 
89
            bzrlib.tests.branch_implementations,
 
90
            bzrlib.tests.bzrdir_implementations,
 
91
            bzrlib.tests.repository_implementations,
 
92
            bzrlib.tests.workingtree_implementations,
65
93
            ]
66
94
 
67
95
 
68
 
class EarlyStoppingTestResultAdapter(object):
69
 
    """An adapter for TestResult to stop at the first first failure or error"""
70
 
 
71
 
    def __init__(self, result):
72
 
        self._result = result
73
 
 
74
 
    def addError(self, test, err):
75
 
        self._result.addError(test, err)
76
 
        self._result.stop()
77
 
 
78
 
    def addFailure(self, test, err):
79
 
        self._result.addFailure(test, err)
80
 
        self._result.stop()
81
 
 
82
 
    def __getattr__(self, name):
83
 
        return getattr(self._result, name)
84
 
 
85
 
    def __setattr__(self, name, value):
86
 
        if name == '_result':
87
 
            object.__setattr__(self, name, value)
88
 
        return setattr(self._result, name, value)
89
 
 
90
 
 
91
96
class _MyResult(unittest._TextTestResult):
92
97
    """Custom TestResult.
93
98
 
94
99
    Shows output in a different format, including displaying runtime for tests.
95
100
    """
 
101
    stop_early = False
96
102
 
97
103
    def _elapsedTime(self):
98
104
        return "%5dms" % (1000 * (time.time() - self._start_time))
132
138
        elif self.dots:
133
139
            self.stream.write('E')
134
140
        self.stream.flush()
 
141
        if self.stop_early:
 
142
            self.stop()
135
143
 
136
144
    def addFailure(self, test, err):
137
145
        unittest.TestResult.addFailure(self, test, err)
140
148
        elif self.dots:
141
149
            self.stream.write('F')
142
150
        self.stream.flush()
 
151
        if self.stop_early:
 
152
            self.stop()
143
153
 
144
154
    def addSuccess(self, test):
145
155
        if self.showAll:
163
173
    def printErrorList(self, flavour, errors):
164
174
        for test, err in errors:
165
175
            self.stream.writeln(self.separator1)
166
 
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
167
 
            if hasattr(test, '_get_log'):
 
176
            self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
 
177
            if getattr(test, '_get_log', None) is not None:
168
178
                print >>self.stream
169
179
                print >>self.stream, \
170
 
                        ('vvvv[log from %s]' % test).ljust(78,'-')
 
180
                        ('vvvv[log from %s]' % test.id()).ljust(78,'-')
171
181
                print >>self.stream, test._get_log()
172
182
                print >>self.stream, \
173
 
                        ('^^^^[log from %s]' % test).ljust(78,'-')
 
183
                        ('^^^^[log from %s]' % test.id()).ljust(78,'-')
174
184
            self.stream.writeln(self.separator2)
175
185
            self.stream.writeln("%s" % err)
176
186
 
180
190
 
181
191
    def _makeResult(self):
182
192
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
183
 
        if self.stop_on_failure:
184
 
            result = EarlyStoppingTestResultAdapter(result)
 
193
        result.stop_early = self.stop_on_failure
185
194
        return result
186
195
 
187
196
 
231
240
    _log_file_name = None
232
241
    _log_contents = ''
233
242
 
 
243
    def __init__(self, methodName='testMethod'):
 
244
        super(TestCase, self).__init__(methodName)
 
245
        self._cleanups = []
 
246
 
234
247
    def setUp(self):
235
248
        unittest.TestCase.setUp(self)
236
 
        self._cleanups = []
237
249
        self._cleanEnvironment()
238
250
        bzrlib.trace.disable_default_logging()
239
251
        self._startLogFile()
253
265
                                  charjunk=lambda x: False)
254
266
        return ''.join(difflines)
255
267
 
256
 
    def assertEqualDiff(self, a, b):
 
268
    def assertEqualDiff(self, a, b, message=None):
257
269
        """Assert two texts are equal, if not raise an exception.
258
270
        
259
271
        This is intended for use with multi-line strings where it can 
262
274
        # TODO: perhaps override assertEquals to call this for strings?
263
275
        if a == b:
264
276
            return
265
 
        raise AssertionError("texts not equal:\n" + 
 
277
        if message is None:
 
278
            message = "texts not equal:\n"
 
279
        raise AssertionError(message + 
266
280
                             self._ndiff_strings(a, b))      
267
281
        
 
282
    def assertEqualMode(self, mode, mode_test):
 
283
        self.assertEqual(mode, mode_test,
 
284
                         'mode mismatch %o != %o' % (mode, mode_test))
 
285
 
268
286
    def assertStartsWith(self, s, prefix):
269
287
        if not s.startswith(prefix):
270
288
            raise AssertionError('string %r does not start with %r' % (s, prefix))
289
307
            raise AssertionError("value(s) %r not present in container %r" % 
290
308
                                 (missing, superlist))
291
309
 
 
310
    def assertIs(self, left, right):
 
311
        if not (left is right):
 
312
            raise AssertionError("%r is not %r." % (left, right))
 
313
 
 
314
    def assertTransportMode(self, transport, path, mode):
 
315
        """Fail if a path does not have mode mode.
 
316
        
 
317
        If modes are not supported on this platform, the test is skipped.
 
318
        """
 
319
        if sys.platform == 'win32':
 
320
            return
 
321
        path_stat = transport.stat(path)
 
322
        actual_mode = stat.S_IMODE(path_stat.st_mode)
 
323
        self.assertEqual(mode, actual_mode,
 
324
            'mode of %r incorrect (%o != %o)' % (path, mode, actual_mode))
 
325
 
292
326
    def _startLogFile(self):
293
327
        """Send bzr and test log messages to a temporary file.
294
328
 
297
331
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
298
332
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
299
333
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
300
 
        bzrlib.trace.enable_test_log(self._log_file)
 
334
        self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
301
335
        self._log_file_name = name
302
336
        self.addCleanup(self._finishLogFile)
303
337
 
306
340
 
307
341
        Read contents into memory, close, and delete.
308
342
        """
309
 
        bzrlib.trace.disable_test_log()
 
343
        bzrlib.trace.disable_test_log(self._log_nonce)
310
344
        self._log_file.seek(0)
311
345
        self._log_contents = self._log_file.read()
312
346
        self._log_file.close()
367
401
 
368
402
        This should only be called from TestCase.tearDown.
369
403
        """
 
404
        # TODO: Perhaps this should keep running cleanups even if 
 
405
        # one of them fails?
370
406
        for cleanup_fn in reversed(self._cleanups):
371
407
            cleanup_fn()
372
408
 
473
509
        if stdin is None:
474
510
            stdin = StringIO("")
475
511
        if stdout is None:
476
 
            if hasattr(self, "_log_file"):
 
512
            if getattr(self, "_log_file", None) is not None:
477
513
                stdout = self._log_file
478
514
            else:
479
515
                stdout = StringIO()
480
516
        if stderr is None:
481
 
            if hasattr(self, "_log_file"):
 
517
            if getattr(self, "_log_file", None is not None):
482
518
                stderr = self._log_file
483
519
            else:
484
520
                stderr = StringIO()
560
596
            os.chdir(_currentdir)
561
597
        self.addCleanup(_leaveDirectory)
562
598
        
563
 
    def build_tree(self, shape, line_endings='native'):
 
599
    def build_tree(self, shape, line_endings='native', transport=None):
564
600
        """Build a test tree according to a pattern.
565
601
 
566
602
        shape is a sequence of file specifications.  If the final
571
607
                             in binary mode, exact contents are written
572
608
                             in native mode, the line endings match the
573
609
                             default platform endings.
 
610
 
 
611
        :param transport: A transport to write to, for building trees on 
 
612
                          VFS's. If the transport is readonly or None,
 
613
                          "." is opened automatically.
574
614
        """
575
615
        # XXX: It's OK to just create them using forward slashes on windows?
 
616
        if transport is None or transport.is_readonly():
 
617
            transport = bzrlib.transport.get_transport(".")
576
618
        for name in shape:
577
619
            self.assert_(isinstance(name, basestring))
578
620
            if name[-1] == '/':
579
 
                os.mkdir(name[:-1])
 
621
                transport.mkdir(urlescape(name[:-1]))
580
622
            else:
581
623
                if line_endings == 'binary':
582
 
                    f = file(name, 'wb')
 
624
                    end = '\n'
583
625
                elif line_endings == 'native':
584
 
                    f = file(name, 'wt')
 
626
                    end = os.linesep
585
627
                else:
586
 
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
587
 
                print >>f, "contents of", name
588
 
                f.close()
 
628
                    raise errors.BzrError('Invalid line ending request %r' % (line_endings,))
 
629
                content = "contents of %s%s" % (name, end)
 
630
                transport.put(urlescape(name), StringIO(content))
589
631
 
590
632
    def build_tree_contents(self, shape):
591
633
        build_tree_contents(shape)
604
646
        self.assertEqualDiff(content, open(path, 'r').read())
605
647
 
606
648
 
 
649
class TestCaseWithTransport(TestCaseInTempDir):
 
650
    """A test case that provides get_url and get_readonly_url facilities.
 
651
 
 
652
    These back onto two transport servers, one for readonly access and one for
 
653
    read write access.
 
654
 
 
655
    If no explicit class is provided for readonly access, a
 
656
    ReadonlyTransportDecorator is used instead which allows the use of non disk
 
657
    based read write transports.
 
658
 
 
659
    If an explicit class is provided for readonly access, that server and the 
 
660
    readwrite one must both define get_url() as resolving to os.getcwd().
 
661
    """
 
662
 
 
663
    def __init__(self, methodName='testMethod'):
 
664
        super(TestCaseWithTransport, self).__init__(methodName)
 
665
        self.__readonly_server = None
 
666
        self.__server = None
 
667
        self.transport_server = default_transport
 
668
        self.transport_readonly_server = None
 
669
 
 
670
    def get_readonly_url(self, relpath=None):
 
671
        """Get a URL for the readonly transport.
 
672
 
 
673
        This will either be backed by '.' or a decorator to the transport 
 
674
        used by self.get_url()
 
675
        relpath provides for clients to get a path relative to the base url.
 
676
        These should only be downwards relative, not upwards.
 
677
        """
 
678
        base = self.get_readonly_server().get_url()
 
679
        if relpath is not None:
 
680
            if not base.endswith('/'):
 
681
                base = base + '/'
 
682
            base = base + relpath
 
683
        return base
 
684
 
 
685
    def get_readonly_server(self):
 
686
        """Get the server instance for the readonly transport
 
687
 
 
688
        This is useful for some tests with specific servers to do diagnostics.
 
689
        """
 
690
        if self.__readonly_server is None:
 
691
            if self.transport_readonly_server is None:
 
692
                # readonly decorator requested
 
693
                # bring up the server
 
694
                self.get_url()
 
695
                self.__readonly_server = ReadonlyServer()
 
696
                self.__readonly_server.setUp(self.__server)
 
697
            else:
 
698
                self.__readonly_server = self.transport_readonly_server()
 
699
                self.__readonly_server.setUp()
 
700
            self.addCleanup(self.__readonly_server.tearDown)
 
701
        return self.__readonly_server
 
702
 
 
703
    def get_server(self):
 
704
        """Get the read/write server instance.
 
705
 
 
706
        This is useful for some tests with specific servers that need
 
707
        diagnostics.
 
708
        """
 
709
        if self.__server is None:
 
710
            self.__server = self.transport_server()
 
711
            self.__server.setUp()
 
712
            self.addCleanup(self.__server.tearDown)
 
713
        return self.__server
 
714
 
 
715
    def get_url(self, relpath=None):
 
716
        """Get a URL for the readwrite transport.
 
717
 
 
718
        This will either be backed by '.' or to an equivalent non-file based
 
719
        facility.
 
720
        relpath provides for clients to get a path relative to the base url.
 
721
        These should only be downwards relative, not upwards.
 
722
        """
 
723
        base = self.get_server().get_url()
 
724
        if relpath is not None and relpath != '.':
 
725
            if not base.endswith('/'):
 
726
                base = base + '/'
 
727
            base = base + relpath
 
728
        return base
 
729
 
 
730
    def make_branch(self, relpath):
 
731
        """Create a branch on the transport at relpath."""
 
732
        repo = self.make_repository(relpath)
 
733
        return repo.bzrdir.create_branch()
 
734
 
 
735
    def make_bzrdir(self, relpath):
 
736
        try:
 
737
            url = self.get_url(relpath)
 
738
            segments = url.split('/')
 
739
            if segments and segments[-1] not in ('', '.'):
 
740
                parent = '/'.join(segments[:-1])
 
741
                t = bzrlib.transport.get_transport(parent)
 
742
                try:
 
743
                    t.mkdir(segments[-1])
 
744
                except errors.FileExists:
 
745
                    pass
 
746
            return bzrlib.bzrdir.BzrDir.create(url)
 
747
        except errors.UninitializableFormat:
 
748
            raise TestSkipped("Format %s is not initializable.")
 
749
 
 
750
    def make_repository(self, relpath, shared=False):
 
751
        """Create a repository on our default transport at relpath."""
 
752
        made_control = self.make_bzrdir(relpath)
 
753
        return made_control.create_repository(shared=shared)
 
754
 
 
755
    def make_branch_and_tree(self, relpath):
 
756
        """Create a branch on the transport and a tree locally.
 
757
 
 
758
        Returns the tree.
 
759
        """
 
760
        # TODO: always use the local disk path for the working tree,
 
761
        # this obviously requires a format that supports branch references
 
762
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
 
763
        # RBC 20060208
 
764
        b = self.make_branch(relpath)
 
765
        try:
 
766
            return b.bzrdir.create_workingtree()
 
767
        except errors.NotLocalUrl:
 
768
            # new formats - catch No tree error and create
 
769
            # a branch reference and a checkout.
 
770
            # old formats at that point - raise TestSkipped.
 
771
            # TODO: rbc 20060208
 
772
            return WorkingTreeFormat2().initialize(bzrdir.BzrDir.open(relpath))
 
773
 
 
774
 
 
775
class ChrootedTestCase(TestCaseWithTransport):
 
776
    """A support class that provides readonly urls outside the local namespace.
 
777
 
 
778
    This is done by checking if self.transport_server is a MemoryServer. if it
 
779
    is then we are chrooted already, if it is not then an HttpServer is used
 
780
    for readonly urls.
 
781
 
 
782
    TODO RBC 20060127: make this an option to TestCaseWithTransport so it can
 
783
                       be used without needed to redo it when a different 
 
784
                       subclass is in use ?
 
785
    """
 
786
 
 
787
    def setUp(self):
 
788
        super(ChrootedTestCase, self).setUp()
 
789
        if not self.transport_server == bzrlib.transport.memory.MemoryServer:
 
790
            self.transport_readonly_server = bzrlib.transport.http.HttpServer
 
791
 
 
792
 
607
793
def filter_suite_by_re(suite, pattern):
608
794
    result = TestSuite()
609
795
    filter_re = re.compile(pattern)
614
800
 
615
801
 
616
802
def run_suite(suite, name='test', verbose=False, pattern=".*",
617
 
              stop_on_failure=False, keep_output=False):
 
803
              stop_on_failure=False, keep_output=False,
 
804
              transport=None):
618
805
    TestCaseInTempDir._TEST_NAME = name
619
806
    if verbose:
620
807
        verbosity = 2
639
826
 
640
827
 
641
828
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
642
 
             keep_output=False):
 
829
             keep_output=False,
 
830
             transport=None):
643
831
    """Run the whole test suite under the enhanced runner"""
644
 
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
645
 
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
 
832
    global default_transport
 
833
    if transport is None:
 
834
        transport = default_transport
 
835
    old_transport = default_transport
 
836
    default_transport = transport
 
837
    suite = test_suite()
 
838
    try:
 
839
        return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
 
840
                     stop_on_failure=stop_on_failure, keep_output=keep_output,
 
841
                     transport=transport)
 
842
    finally:
 
843
        default_transport = old_transport
 
844
 
646
845
 
647
846
 
648
847
def test_suite():
658
857
                   'bzrlib.tests.test_bad_files',
659
858
                   'bzrlib.tests.test_basis_inventory',
660
859
                   'bzrlib.tests.test_branch',
 
860
                   'bzrlib.tests.test_bzrdir',
661
861
                   'bzrlib.tests.test_command',
662
862
                   'bzrlib.tests.test_commit',
663
863
                   'bzrlib.tests.test_commit_merge',
664
864
                   'bzrlib.tests.test_config',
665
865
                   'bzrlib.tests.test_conflicts',
 
866
                   'bzrlib.tests.test_decorators',
666
867
                   'bzrlib.tests.test_diff',
 
868
                   'bzrlib.tests.test_doc_generate',
 
869
                   'bzrlib.tests.test_errors',
667
870
                   'bzrlib.tests.test_fetch',
668
871
                   'bzrlib.tests.test_gpg',
669
872
                   'bzrlib.tests.test_graph',
671
874
                   'bzrlib.tests.test_http',
672
875
                   'bzrlib.tests.test_identitymap',
673
876
                   'bzrlib.tests.test_inv',
 
877
                   'bzrlib.tests.test_lockable_files',
674
878
                   'bzrlib.tests.test_log',
675
879
                   'bzrlib.tests.test_merge',
676
880
                   'bzrlib.tests.test_merge3',
680
884
                   'bzrlib.tests.test_nonascii',
681
885
                   'bzrlib.tests.test_options',
682
886
                   'bzrlib.tests.test_osutils',
683
 
                   'bzrlib.tests.test_parent',
684
887
                   'bzrlib.tests.test_permissions',
685
888
                   'bzrlib.tests.test_plugins',
686
 
                   'bzrlib.tests.test_remove',
 
889
                   'bzrlib.tests.test_repository',
687
890
                   'bzrlib.tests.test_revision',
688
891
                   'bzrlib.tests.test_revisionnamespaces',
689
892
                   'bzrlib.tests.test_revprops',
695
898
                   'bzrlib.tests.test_sftp_transport',
696
899
                   'bzrlib.tests.test_smart_add',
697
900
                   'bzrlib.tests.test_source',
698
 
                   'bzrlib.tests.test_status',
699
901
                   'bzrlib.tests.test_store',
700
902
                   'bzrlib.tests.test_symbol_versioning',
701
903
                   'bzrlib.tests.test_testament',
702
904
                   'bzrlib.tests.test_trace',
703
905
                   'bzrlib.tests.test_transactions',
 
906
                   'bzrlib.tests.test_transform',
704
907
                   'bzrlib.tests.test_transport',
705
908
                   'bzrlib.tests.test_tsort',
706
909
                   'bzrlib.tests.test_ui',
711
914
                   'bzrlib.tests.test_workingtree',
712
915
                   'bzrlib.tests.test_xml',
713
916
                   ]
 
917
    test_transport_implementations = [
 
918
        'bzrlib.tests.test_transport_implementations']
714
919
 
715
920
    TestCase.BZRPATH = osutils.pathjoin(
716
921
            osutils.realpath(osutils.dirname(bzrlib.__path__[0])), 'bzr')
723
928
    # actually wrong, just "no such module".  We should probably override that
724
929
    # class, but for the moment just load them ourselves. (mbp 20051202)
725
930
    loader = TestLoader()
 
931
    from bzrlib.transport import TransportTestProviderAdapter
 
932
    adapter = TransportTestProviderAdapter()
 
933
    adapt_modules(test_transport_implementations, adapter, loader, suite)
726
934
    for mod_name in testmod_names:
727
935
        mod = _load_module_by_name(mod_name)
728
936
        suite.addTest(loader.loadTestsFromModule(mod))
733
941
    for m in (MODULES_TO_DOCTEST):
734
942
        suite.addTest(DocTestSuite(m))
735
943
    for name, plugin in bzrlib.plugin.all_plugins().items():
736
 
        if hasattr(plugin, 'test_suite'):
 
944
        if getattr(plugin, 'test_suite', None) is not None:
737
945
            suite.addTest(plugin.test_suite())
738
946
    return suite
739
947
 
740
948
 
 
949
def adapt_modules(mods_list, adapter, loader, suite):
 
950
    """Adapt the modules in mods_list using adapter and add to suite."""
 
951
    for mod_name in mods_list:
 
952
        mod = _load_module_by_name(mod_name)
 
953
        for test in iter_suite_tests(loader.loadTestsFromModule(mod)):
 
954
            suite.addTests(adapter.adapt(test))
 
955
 
 
956
 
741
957
def _load_module_by_name(mod_name):
742
958
    parts = mod_name.split('.')
743
959
    module = __import__(mod_name)