1
# Copyright (C) 2005 by Canonical Ltd
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.
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.
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
24
from warnings import warn
25
from cStringIO import StringIO
28
import bzrlib.commands
35
MODULES_TO_DOCTEST = []
37
from logging import debug, warning, error
39
class CommandFailed(Exception):
42
class TestCase(unittest.TestCase):
43
"""Base class for bzr unit tests.
45
Tests that need access to disk resources should subclass
46
TestCaseInTempDir not TestCase.
48
Error and debug log messages are redirected from their usual
49
location into a temporary file, the contents of which can be
50
retrieved by _get_log().
52
There are also convenience functions to invoke bzr's command-line
53
routine, and to build and check bzr trees."""
58
# this replaces the default testsweet.TestCase; we don't want logging changed
59
unittest.TestCase.setUp(self)
60
bzrlib.trace.disable_default_logging()
61
self._enable_file_logging()
64
def _enable_file_logging(self):
65
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
67
self._log_file = os.fdopen(fileno, 'w+')
69
hdlr = logging.StreamHandler(self._log_file)
70
hdlr.setLevel(logging.DEBUG)
71
hdlr.setFormatter(logging.Formatter('%(levelname)4.4s %(message)s'))
72
logging.getLogger('').addHandler(hdlr)
73
logging.getLogger('').setLevel(logging.DEBUG)
75
debug('opened log file %s', name)
77
self._log_file_name = name
80
logging.getLogger('').removeHandler(self._log_hdlr)
81
bzrlib.trace.enable_default_logging()
82
logging.debug('%s teardown', self.id())
83
self._log_file.close()
84
unittest.TestCase.tearDown(self)
90
"""Return as a string the log for this test"""
91
return open(self._log_file_name).read()
94
def capture(self, cmd):
95
"""Shortcut that splits cmd into words, runs, and returns stdout"""
96
return self.run_bzr_captured(cmd.split())[0]
99
def run_bzr_captured(self, argv, retcode=0):
100
"""Invoke bzr and return (result, stdout, stderr).
102
Useful for code that wants to check the contents of the
103
output, the way error messages are presented, etc.
105
This should be the main method for tests that want to exercise the
106
overall behavior of the bzr application (rather than a unit test
107
or a functional test of the library.)
109
Much of the old code runs bzr by forking a new copy of Python, but
110
that is slower, harder to debug, and generally not necessary.
112
This runs bzr through the interface that catches and reports
113
errors, and with logging set to something approximating the
114
default, so that error reporting can be checked.
116
argv -- arguments to invoke bzr
117
retcode -- expected return code, or None for don't-care.
121
self.log('run bzr: %s', ' '.join(argv))
122
handler = logging.StreamHandler(stderr)
123
handler.setFormatter(bzrlib.trace.QuietFormatter())
124
handler.setLevel(logging.INFO)
125
logger = logging.getLogger('')
126
logger.addHandler(handler)
128
result = self.apply_redirected(None, stdout, stderr,
129
bzrlib.commands.run_bzr_catch_errors,
132
logger.removeHandler(handler)
133
out = stdout.getvalue()
134
err = stderr.getvalue()
136
self.log('output:\n%s', out)
138
self.log('errors:\n%s', err)
139
if retcode is not None:
140
self.assertEquals(result, retcode)
144
def run_bzr(self, *args, **kwargs):
145
"""Invoke bzr, as if it were run from the command line.
147
This should be the main method for tests that want to exercise the
148
overall behavior of the bzr application (rather than a unit test
149
or a functional test of the library.)
151
This sends the stdout/stderr results into the test's log,
152
where it may be useful for debugging. See also run_captured.
154
retcode = kwargs.pop('retcode', 0)
155
return self.run_bzr_captured(args, retcode)
158
def check_inventory_shape(self, inv, shape):
160
Compare an inventory to a list of expected names.
162
Fail if they are not precisely equal.
165
shape = list(shape) # copy
166
for path, ie in inv.entries():
167
name = path.replace('\\', '/')
175
self.fail("expected paths not found in inventory: %r" % shape)
177
self.fail("unexpected paths found in inventory: %r" % extras)
179
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
180
a_callable=None, *args, **kwargs):
181
"""Call callable with redirected std io pipes.
183
Returns the return code."""
184
if not callable(a_callable):
185
raise ValueError("a_callable must be callable.")
189
if hasattr(self, "_log_file"):
190
stdout = self._log_file
194
if hasattr(self, "_log_file"):
195
stderr = self._log_file
198
real_stdin = sys.stdin
199
real_stdout = sys.stdout
200
real_stderr = sys.stderr
205
return a_callable(*args, **kwargs)
207
sys.stdout = real_stdout
208
sys.stderr = real_stderr
209
sys.stdin = real_stdin
212
BzrTestBase = TestCase
215
class TestCaseInTempDir(TestCase):
216
"""Derived class that runs a test within a temporary directory.
218
This is useful for tests that need to create a branch, etc.
220
The directory is created in a slightly complex way: for each
221
Python invocation, a new temporary top-level directory is created.
222
All test cases create their own directory within that. If the
223
tests complete successfully, the directory is removed.
225
InTempDir is an old alias for FunctionalTestCase.
230
OVERRIDE_PYTHON = 'python'
232
def check_file_contents(self, filename, expect):
233
self.log("check contents of file %s" % filename)
234
contents = file(filename, 'r').read()
235
if contents != expect:
236
self.log("expected: %r" % expect)
237
self.log("actually: %r" % contents)
238
self.fail("contents of %s not as expected")
240
def _make_test_root(self):
241
if TestCaseInTempDir.TEST_ROOT is not None:
245
root = 'test%04d.tmp' % i
249
if e.errno == errno.EEXIST:
254
# successfully created
255
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
257
# make a fake bzr directory there to prevent any tests propagating
258
# up onto the source directory's real branch
259
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
262
super(TestCaseInTempDir, self).setUp()
263
self._make_test_root()
264
self._currentdir = os.getcwdu()
265
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
266
os.mkdir(self.test_dir)
267
os.chdir(self.test_dir)
270
os.chdir(self._currentdir)
271
super(TestCaseInTempDir, self).tearDown()
274
def build_tree(self, shape):
275
"""Build a test tree according to a pattern.
277
shape is a sequence of file specifications. If the final
278
character is '/', a directory is created.
280
This doesn't add anything to a branch.
282
# XXX: It's OK to just create them using forward slashes on windows?
284
assert isinstance(name, basestring)
289
print >>f, "contents of", name
294
class MetaTestLog(TestCase):
295
def test_logging(self):
296
"""Test logs are captured when a test fails."""
297
logging.info('an info message')
298
warning('something looks dodgy...')
299
logging.debug('hello, test is running')
303
def selftest(verbose=False, pattern=".*"):
304
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
308
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
309
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
310
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
311
from doctest import DocTestSuite
313
global MODULES_TO_TEST, MODULES_TO_DOCTEST
316
['bzrlib.selftest.MetaTestLog',
317
'bzrlib.selftest.test_parent',
318
'bzrlib.selftest.testinv',
319
'bzrlib.selftest.testfetch',
320
'bzrlib.selftest.versioning',
321
'bzrlib.selftest.whitebox',
322
'bzrlib.selftest.testmerge3',
323
'bzrlib.selftest.testmerge',
324
'bzrlib.selftest.testhashcache',
325
'bzrlib.selftest.teststatus',
326
'bzrlib.selftest.testlog',
327
'bzrlib.selftest.blackbox',
328
'bzrlib.selftest.testrevisionnamespaces',
329
'bzrlib.selftest.testbranch',
330
'bzrlib.selftest.testremotebranch',
331
'bzrlib.selftest.testrevision',
332
'bzrlib.selftest.test_merge_core',
333
'bzrlib.selftest.test_smart_add',
334
'bzrlib.selftest.testdiff',
335
'bzrlib.selftest.test_xml',
337
'bzrlib.selftest.teststore',
338
'bzrlib.selftest.testtransport',
339
'bzrlib.selftest.testgraph',
342
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
343
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
344
if m not in MODULES_TO_DOCTEST:
345
MODULES_TO_DOCTEST.append(m)
347
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
348
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
351
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
352
for m in MODULES_TO_TEST:
353
suite.addTest(TestLoader().loadTestsFromModule(m))
354
for m in (MODULES_TO_DOCTEST):
355
suite.addTest(DocTestSuite(m))
356
for p in bzrlib.plugin.all_plugins:
357
if hasattr(p, 'test_suite'):
358
suite.addTest(p.test_suite())