/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-09-18 00:57:26 UTC
  • mto: (1185.8.2) (974.1.91)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: mbp@sourcefrog.net-20050918005726-9cbc228aeeaf4849
- run_bzr_captured also includes logged errors in 
  captured stderr
- simple test that exercises this

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
import sys
 
23
import errno
 
24
import subprocess
 
25
from warnings import warn
 
26
from cStringIO import StringIO
 
27
 
 
28
import testsweet
 
29
import bzrlib.commands
 
30
 
 
31
import bzrlib.trace
 
32
import bzrlib.fetch
 
33
 
 
34
 
 
35
MODULES_TO_TEST = []
 
36
MODULES_TO_DOCTEST = []
 
37
 
 
38
from logging import debug, warning, error
 
39
 
 
40
class CommandFailed(Exception):
 
41
    pass
 
42
 
 
43
class TestCase(unittest.TestCase):
 
44
    """Base class for bzr unit tests.
 
45
    
 
46
    Tests that need access to disk resources should subclass 
 
47
    TestCaseInTempDir not TestCase.
 
48
 
 
49
    Error and debug log messages are redirected from their usual
 
50
    location into a temporary file, the contents of which can be
 
51
    retrieved by _get_log().
 
52
       
 
53
    There are also convenience functions to invoke bzr's command-line
 
54
    routine, and to build and check bzr trees."""
 
55
 
 
56
    BZRPATH = 'bzr'
 
57
 
 
58
    def setUp(self):
 
59
        # this replaces the default testsweet.TestCase; we don't want logging changed
 
60
        unittest.TestCase.setUp(self)
 
61
        bzrlib.trace.disable_default_logging()
 
62
        self._enable_file_logging()
 
63
 
 
64
 
 
65
    def _enable_file_logging(self):
 
66
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
67
 
 
68
        self._log_file = os.fdopen(fileno, 'w+')
 
69
 
 
70
        hdlr = logging.StreamHandler(self._log_file)
 
71
        hdlr.setLevel(logging.DEBUG)
 
72
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
 
73
        logging.getLogger('').addHandler(hdlr)
 
74
        logging.getLogger('').setLevel(logging.DEBUG)
 
75
        self._log_hdlr = hdlr
 
76
        debug('opened log file %s', name)
 
77
        
 
78
        self._log_file_name = name
 
79
 
 
80
    def tearDown(self):
 
81
        logging.getLogger('').removeHandler(self._log_hdlr)
 
82
        bzrlib.trace.enable_default_logging()
 
83
        logging.debug('%s teardown', self.id())
 
84
        self._log_file.close()
 
85
        unittest.TestCase.tearDown(self)
 
86
 
 
87
    def log(self, *args):
 
88
        logging.debug(*args)
 
89
 
 
90
    def _get_log(self):
 
91
        """Return as a string the log for this test"""
 
92
        return open(self._log_file_name).read()
 
93
 
 
94
 
 
95
    def run_bzr_captured(self, argv, retcode=0):
 
96
        """Invoke bzr and return (result, stdout, stderr).
 
97
 
 
98
        Useful for code that wants to check the contents of the
 
99
        output, the way error messages are presented, etc.
 
100
 
 
101
        This should be the main method for tests that want to exercise the
 
102
        overall behavior of the bzr application (rather than a unit test
 
103
        or a functional test of the library.)
 
104
 
 
105
        Much of the old code runs bzr by forking a new copy of Python, but
 
106
        that is slower, harder to debug, and generally not necessary.
 
107
 
 
108
        This runs bzr through the interface that catches and reports
 
109
        errors, and with logging set to something approximating the
 
110
        default, so that error reporting can be checked.
 
111
 
 
112
        argv -- arguments to invoke bzr
 
113
        retcode -- expected return code, or None for don't-care.
 
114
        """
 
115
        stdout = StringIO()
 
116
        stderr = StringIO()
 
117
        self.log('run bzr: %s', ' '.join(argv))
 
118
        handler = logging.StreamHandler(stderr)
 
119
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
120
        handler.setLevel(logging.INFO)
 
121
        logger = logging.getLogger('')
 
122
        logger.addHandler(handler)
 
123
        try:
 
124
            result = self.apply_redirected(None, stdout, stderr,
 
125
                                           bzrlib.commands.run_bzr_catch_errors,
 
126
                                           argv)
 
127
        finally:
 
128
            logger.removeHandler(handler)
 
129
        out = stdout.getvalue()
 
130
        err = stderr.getvalue()
 
131
        if out:
 
132
            self.log('output:\n%s', out)
 
133
        if err:
 
134
            self.log('errors:\n%s', err)
 
135
        if retcode is not None:
 
136
            self.assertEquals(result, retcode)
 
137
        return out, err
 
138
 
 
139
 
 
140
    def run_bzr(self, *args, **kwargs):
 
141
        """Invoke bzr, as if it were run from the command line.
 
142
 
 
143
        This should be the main method for tests that want to exercise the
 
144
        overall behavior of the bzr application (rather than a unit test
 
145
        or a functional test of the library.)
 
146
 
 
147
        This sends the stdout/stderr results into the test's log,
 
148
        where it may be useful for debugging.  See also run_captured.
 
149
        """
 
150
        warn('TestBase.run_bzr is deprecated, use TestBase.run_bzr_captured')
 
151
        retcode = kwargs.pop('retcode', 0)
 
152
        self.run_bzr_captured(args, retcode)
 
153
 
 
154
 
 
155
    def check_inventory_shape(self, inv, shape):
 
156
        """
 
157
        Compare an inventory to a list of expected names.
 
158
 
 
159
        Fail if they are not precisely equal.
 
160
        """
 
161
        extras = []
 
162
        shape = list(shape)             # copy
 
163
        for path, ie in inv.entries():
 
164
            name = path.replace('\\', '/')
 
165
            if ie.kind == 'dir':
 
166
                name = name + '/'
 
167
            if name in shape:
 
168
                shape.remove(name)
 
169
            else:
 
170
                extras.append(name)
 
171
        if shape:
 
172
            self.fail("expected paths not found in inventory: %r" % shape)
 
173
        if extras:
 
174
            self.fail("unexpected paths found in inventory: %r" % extras)
 
175
 
 
176
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
177
                         a_callable=None, *args, **kwargs):
 
178
        """Call callable with redirected std io pipes.
 
179
 
 
180
        Returns the return code."""
 
181
        if not callable(a_callable):
 
182
            raise ValueError("a_callable must be callable.")
 
183
        if stdin is None:
 
184
            stdin = StringIO("")
 
185
        if stdout is None:
 
186
            if hasattr(self, "_log_file"):
 
187
                stdout = self._log_file
 
188
            else:
 
189
                stdout = StringIO()
 
190
        if stderr is None:
 
191
            if hasattr(self, "_log_file"):
 
192
                stderr = self._log_file
 
193
            else:
 
194
                stderr = StringIO()
 
195
        real_stdin = sys.stdin
 
196
        real_stdout = sys.stdout
 
197
        real_stderr = sys.stderr
 
198
        try:
 
199
            sys.stdout = stdout
 
200
            sys.stderr = stderr
 
201
            sys.stdin = stdin
 
202
            return a_callable(*args, **kwargs)
 
203
        finally:
 
204
            sys.stdout = real_stdout
 
205
            sys.stderr = real_stderr
 
206
            sys.stdin = real_stdin
 
207
 
 
208
 
 
209
BzrTestBase = TestCase
 
210
 
 
211
     
 
212
class TestCaseInTempDir(TestCase):
 
213
    """Derived class that runs a test within a temporary directory.
 
214
 
 
215
    This is useful for tests that need to create a branch, etc.
 
216
 
 
217
    The directory is created in a slightly complex way: for each
 
218
    Python invocation, a new temporary top-level directory is created.
 
219
    All test cases create their own directory within that.  If the
 
220
    tests complete successfully, the directory is removed.
 
221
 
 
222
    InTempDir is an old alias for FunctionalTestCase.
 
223
    """
 
224
 
 
225
    TEST_ROOT = None
 
226
    _TEST_NAME = 'test'
 
227
    OVERRIDE_PYTHON = 'python'
 
228
 
 
229
    def check_file_contents(self, filename, expect):
 
230
        self.log("check contents of file %s" % filename)
 
231
        contents = file(filename, 'r').read()
 
232
        if contents != expect:
 
233
            self.log("expected: %r" % expect)
 
234
            self.log("actually: %r" % contents)
 
235
            self.fail("contents of %s not as expected")
 
236
 
 
237
    def _make_test_root(self):
 
238
        if TestCaseInTempDir.TEST_ROOT is not None:
 
239
            return
 
240
        i = 0
 
241
        while True:
 
242
            root = 'test%04d.tmp' % i
 
243
            try:
 
244
                os.mkdir(root)
 
245
            except OSError, e:
 
246
                if e.errno == errno.EEXIST:
 
247
                    i += 1
 
248
                    continue
 
249
                else:
 
250
                    raise
 
251
            # successfully created
 
252
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
253
            break
 
254
        # make a fake bzr directory there to prevent any tests propagating
 
255
        # up onto the source directory's real branch
 
256
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
257
 
 
258
    def setUp(self):
 
259
        super(TestCaseInTempDir, self).setUp()
 
260
        self._make_test_root()
 
261
        self._currentdir = os.getcwdu()
 
262
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
263
        os.mkdir(self.test_dir)
 
264
        os.chdir(self.test_dir)
 
265
        
 
266
    def tearDown(self):
 
267
        os.chdir(self._currentdir)
 
268
        super(TestCaseInTempDir, self).tearDown()
 
269
 
 
270
    def _formcmd(self, cmd):
 
271
        if isinstance(cmd, basestring):
 
272
            cmd = cmd.split()
 
273
        if cmd[0] == 'bzr':
 
274
            cmd[0] = self.BZRPATH
 
275
            if self.OVERRIDE_PYTHON:
 
276
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
277
        self.log('$ %r' % cmd)
 
278
        return cmd
 
279
 
 
280
    def runcmd(self, cmd, retcode=0):
 
281
        """Run one command and check the return code.
 
282
 
 
283
        Returns a tuple of (stdout,stderr) strings.
 
284
 
 
285
        If a single string is based, it is split into words.
 
286
        For commands that are not simple space-separated words, please
 
287
        pass a list instead."""
 
288
        cmd = self._formcmd(cmd)
 
289
        self.log('$ ' + ' '.join(cmd))
 
290
        actual_retcode = subprocess.call(cmd, stdout=self._log_file,
 
291
                                         stderr=self._log_file)
 
292
        if retcode != actual_retcode:
 
293
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
294
                                % (cmd, actual_retcode, retcode))
 
295
 
 
296
    def backtick(self, cmd, retcode=0):
 
297
        """Run a command and return its output"""
 
298
        cmd = self._formcmd(cmd)
 
299
        child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
 
300
        outd, errd = child.communicate()
 
301
        self.log(outd)
 
302
        actual_retcode = child.wait()
 
303
 
 
304
        outd = outd.replace('\r', '')
 
305
 
 
306
        if retcode != actual_retcode:
 
307
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
308
                                % (cmd, actual_retcode, retcode))
 
309
 
 
310
        return outd
 
311
 
 
312
 
 
313
 
 
314
    def build_tree(self, shape):
 
315
        """Build a test tree according to a pattern.
 
316
 
 
317
        shape is a sequence of file specifications.  If the final
 
318
        character is '/', a directory is created.
 
319
 
 
320
        This doesn't add anything to a branch.
 
321
        """
 
322
        # XXX: It's OK to just create them using forward slashes on windows?
 
323
        for name in shape:
 
324
            assert isinstance(name, basestring)
 
325
            if name[-1] == '/':
 
326
                os.mkdir(name[:-1])
 
327
            else:
 
328
                f = file(name, 'wt')
 
329
                print >>f, "contents of", name
 
330
                f.close()
 
331
                
 
332
 
 
333
 
 
334
class MetaTestLog(TestCase):
 
335
    def test_logging(self):
 
336
        """Test logs are captured when a test fails."""
 
337
        logging.info('an info message')
 
338
        warning('something looks dodgy...')
 
339
        logging.debug('hello, test is running')
 
340
        ##assert 0
 
341
 
 
342
 
 
343
def selftest(verbose=False, pattern=".*"):
 
344
    return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
345
 
 
346
 
 
347
def test_suite():
 
348
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
349
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
 
350
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
351
    from doctest import DocTestSuite
 
352
 
 
353
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
354
 
 
355
    testmod_names = \
 
356
                  ['bzrlib.selftest.MetaTestLog',
 
357
                   'bzrlib.selftest.test_parent',
 
358
                   'bzrlib.selftest.testinv',
 
359
                   'bzrlib.selftest.testfetch',
 
360
                   'bzrlib.selftest.versioning',
 
361
                   'bzrlib.selftest.whitebox',
 
362
                   'bzrlib.selftest.testmerge3',
 
363
                   'bzrlib.selftest.testmerge',
 
364
                   'bzrlib.selftest.testhashcache',
 
365
                   'bzrlib.selftest.teststatus',
 
366
                   'bzrlib.selftest.testlog',
 
367
                   'bzrlib.selftest.blackbox',
 
368
                   'bzrlib.selftest.testrevisionnamespaces',
 
369
                   'bzrlib.selftest.testbranch',
 
370
                   'bzrlib.selftest.testremotebranch',
 
371
                   'bzrlib.selftest.testrevision',
 
372
                   'bzrlib.selftest.test_merge_core',
 
373
                   'bzrlib.selftest.test_smart_add',
 
374
                   'bzrlib.selftest.testdiff',
 
375
                   'bzrlib.selftest.test_xml',
 
376
                   'bzrlib.fetch',
 
377
                   'bzrlib.selftest.teststore',
 
378
                   'bzrlib.selftest.testgraph',
 
379
                   ]
 
380
 
 
381
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
382
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
383
        if m not in MODULES_TO_DOCTEST:
 
384
            MODULES_TO_DOCTEST.append(m)
 
385
 
 
386
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
387
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
388
    print
 
389
    suite = TestSuite()
 
390
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
391
    for m in MODULES_TO_TEST:
 
392
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
393
    for m in (MODULES_TO_DOCTEST):
 
394
        suite.addTest(DocTestSuite(m))
 
395
    for p in bzrlib.plugin.all_plugins:
 
396
        if hasattr(p, 'test_suite'):
 
397
            suite.addTest(p.test_suite())
 
398
    return suite
 
399