~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-23 03:09:50 UTC
  • Revision ID: mbp@sourcefrog.net-20050323030950-c762f4c38f99dbd9
Prepare for smart recursive add.

- New Inventory.add_path, so that callers don't need to know so much
  about path lookup.

- Make gen_file_id public.

- New add module and smart_add function; does previous add operations
  more cleanly but not recursive mode yet.

- New warning() function.

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