~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-08-29 06:56:22 UTC
  • Revision ID: mbp@sourcefrog.net-20050829065622-5aa7add87c38f188
- additional trace messages for plugins

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
import logging
 
19
import unittest
 
20
import tempfile
 
21
import os
 
22
 
 
23
from testsweet import run_suite
 
24
 
 
25
import bzrlib.commands
 
26
import bzrlib.trace
 
27
 
 
28
MODULES_TO_TEST = []
 
29
MODULES_TO_DOCTEST = []
 
30
 
 
31
from logging import debug, warning, error
 
32
 
 
33
 
 
34
class TestCase(unittest.TestCase):
 
35
    """Base class for bzr unit tests.
 
36
    
 
37
    Tests that need access to disk resources should subclass 
 
38
    FunctionalTestCase not TestCase.
 
39
 
 
40
    Error and debug log messages are redirected from their usual
 
41
    location into a temporary file, the contents of which can be
 
42
    retrieved by _get_log().
 
43
       
 
44
    There are also convenience functions to invoke bzr's command-line
 
45
    routine, and to build and check bzr trees."""
 
46
 
 
47
    BZRPATH = 'bzr'
 
48
 
 
49
    def setUp(self):
 
50
        # this replaces the default testsweet.TestCase; we don't want logging changed
 
51
        unittest.TestCase.setUp(self)
 
52
        bzrlib.trace.disable_default_logging()
 
53
        self._enable_file_logging()
 
54
 
 
55
 
 
56
    def _enable_file_logging(self):
 
57
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
58
 
 
59
        self._log_file = os.fdopen(fileno, 'w+')
 
60
 
 
61
        hdlr = logging.StreamHandler(self._log_file)
 
62
        hdlr.setLevel(logging.DEBUG)
 
63
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
 
64
        logging.getLogger('').addHandler(hdlr)
 
65
        logging.getLogger('').setLevel(logging.DEBUG)
 
66
        self._log_hdlr = hdlr
 
67
        debug('opened log file %s', name)
 
68
        
 
69
        self._log_file_name = name
 
70
 
 
71
        
 
72
    def tearDown(self):
 
73
        logging.getLogger('').removeHandler(self._log_hdlr)
 
74
        bzrlib.trace.enable_default_logging()
 
75
        logging.debug('%s teardown', self.id())
 
76
        self._log_file.close()
 
77
        unittest.TestCase.tearDown(self)
 
78
 
 
79
 
 
80
    def log(self, *args):
 
81
        logging.debug(*args)
 
82
 
 
83
    def _get_log(self):
 
84
        """Return as a string the log for this test"""
 
85
        return open(self._log_file_name).read()
 
86
 
 
87
    def run_bzr(self, *args, **kwargs):
 
88
        """Invoke bzr, as if it were run from the command line.
 
89
 
 
90
        This should be the main method for tests that want to exercise the
 
91
        overall behavior of the bzr application (rather than a unit test
 
92
        or a functional test of the library.)
 
93
 
 
94
        Much of the old code runs bzr by forking a new copy of Python, but
 
95
        that is slower, harder to debug, and generally not necessary.
 
96
        """
 
97
        retcode = kwargs.get('retcode', 0)
 
98
        self.assertEquals(bzrlib.commands.run_bzr(args), retcode)
 
99
        
 
100
    def check_inventory_shape(self, inv, shape):
 
101
        """
 
102
        Compare an inventory to a list of expected names.
 
103
 
 
104
        Fail if they are not precisely equal.
 
105
        """
 
106
        extras = []
 
107
        shape = list(shape)             # copy
 
108
        for path, ie in inv.entries():
 
109
            name = path.replace('\\', '/')
 
110
            if ie.kind == 'dir':
 
111
                name = name + '/'
 
112
            if name in shape:
 
113
                shape.remove(name)
 
114
            else:
 
115
                extras.append(name)
 
116
        if shape:
 
117
            self.fail("expected paths not found in inventory: %r" % shape)
 
118
        if extras:
 
119
            self.fail("unexpected paths found in inventory: %r" % extras)
 
120
 
 
121
BzrTestBase = TestCase
 
122
 
 
123
     
 
124
class FunctionalTestCase(TestCase):
 
125
    """Base class for tests that perform function testing - running bzr,
 
126
    using files on disk, and similar activities.
 
127
 
 
128
    InTempDir is an old alias for FunctionalTestCase.
 
129
    """
 
130
 
 
131
    TEST_ROOT = None
 
132
    _TEST_NAME = 'test'
 
133
    OVERRIDE_PYTHON = 'python'
 
134
 
 
135
    def check_file_contents(self, filename, expect):
 
136
        self.log("check contents of file %s" % filename)
 
137
        contents = file(filename, 'r').read()
 
138
        if contents != expect:
 
139
            self.log("expected: %r" % expect)
 
140
            self.log("actually: %r" % contents)
 
141
            self.fail("contents of %s not as expected")
 
142
 
 
143
    def _make_test_root(self):
 
144
        import os
 
145
        import shutil
 
146
        import tempfile
 
147
        
 
148
        if FunctionalTestCase.TEST_ROOT is not None:
 
149
            return
 
150
        FunctionalTestCase.TEST_ROOT = os.path.abspath(
 
151
                                 tempfile.mkdtemp(suffix='.tmp',
 
152
                                                  prefix=self._TEST_NAME + '-',
 
153
                                                  dir=os.curdir))
 
154
    
 
155
        # make a fake bzr directory there to prevent any tests propagating
 
156
        # up onto the source directory's real branch
 
157
        os.mkdir(os.path.join(FunctionalTestCase.TEST_ROOT, '.bzr'))
 
158
 
 
159
    def setUp(self):
 
160
        super(FunctionalTestCase, self).setUp()
 
161
        import os
 
162
        self._make_test_root()
 
163
        self._currentdir = os.getcwdu()
 
164
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
165
        os.mkdir(self.test_dir)
 
166
        os.chdir(self.test_dir)
 
167
        
 
168
    def tearDown(self):
 
169
        import os
 
170
        os.chdir(self._currentdir)
 
171
        super(FunctionalTestCase, self).tearDown()
 
172
 
 
173
    def _formcmd(self, cmd):
 
174
        if isinstance(cmd, basestring):
 
175
            cmd = cmd.split()
 
176
        if cmd[0] == 'bzr':
 
177
            cmd[0] = self.BZRPATH
 
178
            if self.OVERRIDE_PYTHON:
 
179
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
180
        self.log('$ %r' % cmd)
 
181
        return cmd
 
182
 
 
183
    def runcmd(self, cmd, retcode=0):
 
184
        """Run one command and check the return code.
 
185
 
 
186
        Returns a tuple of (stdout,stderr) strings.
 
187
 
 
188
        If a single string is based, it is split into words.
 
189
        For commands that are not simple space-separated words, please
 
190
        pass a list instead."""
 
191
        try:
 
192
            import shutil
 
193
            from subprocess import call
 
194
        except ImportError, e:
 
195
            _need_subprocess()
 
196
            raise
 
197
        cmd = self._formcmd(cmd)
 
198
        self.log('$ ' + ' '.join(cmd))
 
199
        actual_retcode = call(cmd, stdout=self._log_file, stderr=self._log_file)
 
200
        if retcode != actual_retcode:
 
201
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
202
                                % (cmd, actual_retcode, retcode))
 
203
 
 
204
    def backtick(self, cmd, retcode=0):
 
205
        """Run a command and return its output"""
 
206
        try:
 
207
            import shutil
 
208
            from subprocess import Popen, PIPE
 
209
        except ImportError, e:
 
210
            _need_subprocess()
 
211
            raise
 
212
 
 
213
        cmd = self._formcmd(cmd)
 
214
        child = Popen(cmd, stdout=PIPE, stderr=self._log_file)
 
215
        outd, errd = child.communicate()
 
216
        self.log(outd)
 
217
        actual_retcode = child.wait()
 
218
 
 
219
        outd = outd.replace('\r', '')
 
220
 
 
221
        if retcode != actual_retcode:
 
222
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
223
                                % (cmd, actual_retcode, retcode))
 
224
 
 
225
        return outd
 
226
 
 
227
 
 
228
 
 
229
    def build_tree(self, shape):
 
230
        """Build a test tree according to a pattern.
 
231
 
 
232
        shape is a sequence of file specifications.  If the final
 
233
        character is '/', a directory is created.
 
234
 
 
235
        This doesn't add anything to a branch.
 
236
        """
 
237
        # XXX: It's OK to just create them using forward slashes on windows?
 
238
        import os
 
239
        for name in shape:
 
240
            assert isinstance(name, basestring)
 
241
            if name[-1] == '/':
 
242
                os.mkdir(name[:-1])
 
243
            else:
 
244
                f = file(name, 'wt')
 
245
                print >>f, "contents of", name
 
246
                f.close()
 
247
                
 
248
 
 
249
 
 
250
class MetaTestLog(TestCase):
 
251
    def test_logging(self):
 
252
        """Test logs are captured when a test fails."""
 
253
        logging.info('an info message')
 
254
        warning('something looks dodgy...')
 
255
        logging.debug('hello, test is running')
 
256
        ##assert 0
 
257
 
 
258
 
 
259
InTempDir = FunctionalTestCase
 
260
 
 
261
 
 
262
def selftest(verbose=False):
 
263
    from unittest import TestLoader, TestSuite
 
264
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
 
265
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
266
    from doctest import DocTestSuite
 
267
    import os
 
268
    import shutil
 
269
    import time
 
270
    import sys
 
271
 
 
272
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
273
 
 
274
    testmod_names = \
 
275
                  ['bzrlib.selftest.MetaTestLog',
 
276
                   'bzrlib.selftest.testinv',
 
277
                   'bzrlib.selftest.testfetch',
 
278
                   'bzrlib.selftest.versioning',
 
279
                   'bzrlib.selftest.whitebox',
 
280
                   'bzrlib.selftest.testmerge3',
 
281
                   'bzrlib.selftest.testhashcache',
 
282
                   'bzrlib.selftest.teststatus',
 
283
                   'bzrlib.selftest.testlog',
 
284
                   'bzrlib.selftest.blackbox',
 
285
                   'bzrlib.selftest.testrevisionnamespaces',
 
286
                   'bzrlib.selftest.testbranch',
 
287
                   'bzrlib.selftest.testrevision',
 
288
                   'bzrlib.merge_core',
 
289
                   'bzrlib.selftest.testdiff',
 
290
                   ]
 
291
 
 
292
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
293
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
294
        if m not in MODULES_TO_DOCTEST:
 
295
            MODULES_TO_DOCTEST.append(m)
 
296
 
 
297
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
298
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
299
 
 
300
    print
 
301
 
 
302
    suite = TestSuite()
 
303
 
 
304
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
305
 
 
306
    for m in MODULES_TO_TEST:
 
307
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
308
 
 
309
    for m in (MODULES_TO_DOCTEST):
 
310
        suite.addTest(DocTestSuite(m))
 
311
 
 
312
    for p in bzrlib.plugin.all_plugins:
 
313
        if hasattr(p, 'test_suite'):
 
314
            suite.addTest(p.test_suite())
 
315
 
 
316
    import bzrlib.merge_core
 
317
    suite.addTest(unittest.makeSuite(bzrlib.merge_core.MergeTest, 'test_'))
 
318
 
 
319
    return run_suite(suite, 'testbzr', verbose=verbose)
 
320
 
 
321
 
 
322