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
18
from cStringIO import StringIO
29
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]
98
def run_bzr_captured(self, argv, retcode=0):
99
"""Invoke bzr and return (result, stdout, stderr).
101
Useful for code that wants to check the contents of the
102
output, the way error messages are presented, etc.
104
This should be the main method for tests that want to exercise the
105
overall behavior of the bzr application (rather than a unit test
106
or a functional test of the library.)
108
Much of the old code runs bzr by forking a new copy of Python, but
109
that is slower, harder to debug, and generally not necessary.
111
This runs bzr through the interface that catches and reports
112
errors, and with logging set to something approximating the
113
default, so that error reporting can be checked.
115
argv -- arguments to invoke bzr
116
retcode -- expected return code, or None for don't-care.
120
self.log('run bzr: %s', ' '.join(argv))
121
handler = logging.StreamHandler(stderr)
122
handler.setFormatter(bzrlib.trace.QuietFormatter())
123
handler.setLevel(logging.INFO)
124
logger = logging.getLogger('')
125
logger.addHandler(handler)
127
result = self.apply_redirected(None, stdout, stderr,
128
bzrlib.commands.run_bzr_catch_errors,
131
logger.removeHandler(handler)
132
out = stdout.getvalue()
133
err = stderr.getvalue()
135
self.log('output:\n%s', out)
137
self.log('errors:\n%s', err)
138
if retcode is not None:
139
self.assertEquals(result, retcode)
142
def run_bzr(self, *args, **kwargs):
143
"""Invoke bzr, as if it were run from the command line.
145
This should be the main method for tests that want to exercise the
146
overall behavior of the bzr application (rather than a unit test
147
or a functional test of the library.)
149
This sends the stdout/stderr results into the test's log,
150
where it may be useful for debugging. See also run_captured.
152
retcode = kwargs.pop('retcode', 0)
153
return self.run_bzr_captured(args, retcode)
155
def check_inventory_shape(self, inv, shape):
157
Compare an inventory to a list of expected names.
159
Fail if they are not precisely equal.
162
shape = list(shape) # copy
163
for path, ie in inv.entries():
164
name = path.replace('\\', '/')
172
self.fail("expected paths not found in inventory: %r" % shape)
174
self.fail("unexpected paths found in inventory: %r" % extras)
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.
180
Returns the return code."""
181
if not callable(a_callable):
182
raise ValueError("a_callable must be callable.")
186
if hasattr(self, "_log_file"):
187
stdout = self._log_file
191
if hasattr(self, "_log_file"):
192
stderr = self._log_file
195
real_stdin = sys.stdin
196
real_stdout = sys.stdout
197
real_stderr = sys.stderr
202
return a_callable(*args, **kwargs)
204
sys.stdout = real_stdout
205
sys.stderr = real_stderr
206
sys.stdin = real_stdin
209
BzrTestBase = TestCase
212
class TestCaseInTempDir(TestCase):
213
"""Derived class that runs a test within a temporary directory.
215
This is useful for tests that need to create a branch, etc.
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.
222
InTempDir is an old alias for FunctionalTestCase.
227
OVERRIDE_PYTHON = 'python'
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")
237
def _make_test_root(self):
238
if TestCaseInTempDir.TEST_ROOT is not None:
242
root = 'test%04d.tmp' % i
246
if e.errno == errno.EEXIST:
251
# successfully created
252
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
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'))
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)
267
os.chdir(self._currentdir)
268
super(TestCaseInTempDir, self).tearDown()
270
def build_tree(self, shape):
271
"""Build a test tree according to a pattern.
273
shape is a sequence of file specifications. If the final
274
character is '/', a directory is created.
276
This doesn't add anything to a branch.
278
# XXX: It's OK to just create them using forward slashes on windows?
280
assert isinstance(name, basestring)
285
print >>f, "contents of", name
289
class MetaTestLog(TestCase):
290
def test_logging(self):
291
"""Test logs are captured when a test fails."""
292
logging.info('an info message')
293
warning('something looks dodgy...')
294
logging.debug('hello, test is running')
298
def selftest(verbose=False, pattern=".*"):
299
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
303
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
304
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
305
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
306
from doctest import DocTestSuite
308
global MODULES_TO_TEST, MODULES_TO_DOCTEST
311
['bzrlib.selftest.MetaTestLog',
312
'bzrlib.selftest.test_parent',
313
'bzrlib.selftest.testinv',
314
'bzrlib.selftest.testfetch',
315
'bzrlib.selftest.versioning',
316
'bzrlib.selftest.whitebox',
317
'bzrlib.selftest.testmerge3',
318
'bzrlib.selftest.testmerge',
319
'bzrlib.selftest.testhashcache',
320
'bzrlib.selftest.teststatus',
321
'bzrlib.selftest.testlog',
322
'bzrlib.selftest.blackbox',
323
'bzrlib.selftest.testrevisionnamespaces',
324
'bzrlib.selftest.testbranch',
325
'bzrlib.selftest.testremotebranch',
326
'bzrlib.selftest.testrevision',
327
'bzrlib.selftest.test_revision_info',
328
'bzrlib.selftest.test_merge_core',
329
'bzrlib.selftest.test_smart_add',
330
'bzrlib.selftest.testdiff',
331
'bzrlib.selftest.test_xml',
333
'bzrlib.selftest.teststore',
334
'bzrlib.selftest.testgraph',
337
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
338
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
339
if m not in MODULES_TO_DOCTEST:
340
MODULES_TO_DOCTEST.append(m)
342
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
343
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
346
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
347
for m in MODULES_TO_TEST:
348
suite.addTest(TestLoader().loadTestsFromModule(m))
349
for m in (MODULES_TO_DOCTEST):
350
suite.addTest(DocTestSuite(m))
351
for p in bzrlib.plugin.all_plugins:
352
if hasattr(p, 'test_suite'):
353
suite.addTest(p.test_suite())