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
25
from testsweet import run_suite
26
import bzrlib.commands
33
MODULES_TO_DOCTEST = []
35
from logging import debug, warning, error
37
class CommandFailed(Exception):
40
class TestCase(unittest.TestCase):
41
"""Base class for bzr unit tests.
43
Tests that need access to disk resources should subclass
44
TestCaseInTempDir not TestCase.
46
Error and debug log messages are redirected from their usual
47
location into a temporary file, the contents of which can be
48
retrieved by _get_log().
50
There are also convenience functions to invoke bzr's command-line
51
routine, and to build and check bzr trees."""
56
# this replaces the default testsweet.TestCase; we don't want logging changed
57
unittest.TestCase.setUp(self)
58
bzrlib.trace.disable_default_logging()
59
self._enable_file_logging()
62
def _enable_file_logging(self):
63
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
65
self._log_file = os.fdopen(fileno, 'w+')
67
hdlr = logging.StreamHandler(self._log_file)
68
hdlr.setLevel(logging.DEBUG)
69
hdlr.setFormatter(logging.Formatter('%(levelname)4.4s %(message)s'))
70
logging.getLogger('').addHandler(hdlr)
71
logging.getLogger('').setLevel(logging.DEBUG)
73
debug('opened log file %s', name)
75
self._log_file_name = name
78
logging.getLogger('').removeHandler(self._log_hdlr)
79
bzrlib.trace.enable_default_logging()
80
logging.debug('%s teardown', self.id())
81
self._log_file.close()
82
unittest.TestCase.tearDown(self)
88
"""Return as a string the log for this test"""
89
return open(self._log_file_name).read()
91
def run_bzr(self, *args, **kwargs):
92
"""Invoke bzr, as if it were run from the command line.
94
This should be the main method for tests that want to exercise the
95
overall behavior of the bzr application (rather than a unit test
96
or a functional test of the library.)
98
Much of the old code runs bzr by forking a new copy of Python, but
99
that is slower, harder to debug, and generally not necessary.
101
retcode = kwargs.get('retcode', 0)
102
result = self.apply_redirected(None, None, None,
103
bzrlib.commands.run_bzr, args)
104
self.assertEquals(result, retcode)
107
def check_inventory_shape(self, inv, shape):
109
Compare an inventory to a list of expected names.
111
Fail if they are not precisely equal.
114
shape = list(shape) # copy
115
for path, ie in inv.entries():
116
name = path.replace('\\', '/')
124
self.fail("expected paths not found in inventory: %r" % shape)
126
self.fail("unexpected paths found in inventory: %r" % extras)
128
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
129
a_callable=None, *args, **kwargs):
130
"""Call callable with redirected std io pipes.
132
Returns the return code."""
133
from StringIO import StringIO
134
if not callable(a_callable):
135
raise ValueError("a_callable must be callable.")
139
if hasattr(self, "_log_file"):
140
stdout = self._log_file
144
if hasattr(self, "_log_file"):
145
stderr = self._log_file
148
real_stdin = sys.stdin
149
real_stdout = sys.stdout
150
real_stderr = sys.stderr
155
return a_callable(*args, **kwargs)
157
sys.stdout = real_stdout
158
sys.stderr = real_stderr
159
sys.stdin = real_stdin
162
BzrTestBase = TestCase
165
class TestCaseInTempDir(TestCase):
166
"""Derived class that runs a test within a temporary directory.
168
This is useful for tests that need to create a branch, etc.
170
The directory is created in a slightly complex way: for each
171
Python invocation, a new temporary top-level directory is created.
172
All test cases create their own directory within that. If the
173
tests complete successfully, the directory is removed.
175
InTempDir is an old alias for FunctionalTestCase.
180
OVERRIDE_PYTHON = 'python'
182
def check_file_contents(self, filename, expect):
183
self.log("check contents of file %s" % filename)
184
contents = file(filename, 'r').read()
185
if contents != expect:
186
self.log("expected: %r" % expect)
187
self.log("actually: %r" % contents)
188
self.fail("contents of %s not as expected")
190
def _make_test_root(self):
195
if TestCaseInTempDir.TEST_ROOT is not None:
197
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
198
tempfile.mkdtemp(suffix='.tmp',
199
prefix=self._TEST_NAME + '-',
202
# make a fake bzr directory there to prevent any tests propagating
203
# up onto the source directory's real branch
204
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
207
super(TestCaseInTempDir, self).setUp()
209
self._make_test_root()
210
self._currentdir = os.getcwdu()
211
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
212
os.mkdir(self.test_dir)
213
os.chdir(self.test_dir)
217
os.chdir(self._currentdir)
218
super(TestCaseInTempDir, self).tearDown()
220
def _formcmd(self, cmd):
221
if isinstance(cmd, basestring):
224
cmd[0] = self.BZRPATH
225
if self.OVERRIDE_PYTHON:
226
cmd.insert(0, self.OVERRIDE_PYTHON)
227
self.log('$ %r' % cmd)
230
def runcmd(self, cmd, retcode=0):
231
"""Run one command and check the return code.
233
Returns a tuple of (stdout,stderr) strings.
235
If a single string is based, it is split into words.
236
For commands that are not simple space-separated words, please
237
pass a list instead."""
238
cmd = self._formcmd(cmd)
239
self.log('$ ' + ' '.join(cmd))
240
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
241
stderr=self._log_file)
242
if retcode != actual_retcode:
243
raise CommandFailed("test failed: %r returned %d, expected %d"
244
% (cmd, actual_retcode, retcode))
246
def backtick(self, cmd, retcode=0):
247
"""Run a command and return its output"""
248
cmd = self._formcmd(cmd)
249
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
250
outd, errd = child.communicate()
252
actual_retcode = child.wait()
254
outd = outd.replace('\r', '')
256
if retcode != actual_retcode:
257
raise CommandFailed("test failed: %r returned %d, expected %d"
258
% (cmd, actual_retcode, retcode))
264
def build_tree(self, shape):
265
"""Build a test tree according to a pattern.
267
shape is a sequence of file specifications. If the final
268
character is '/', a directory is created.
270
This doesn't add anything to a branch.
272
# XXX: It's OK to just create them using forward slashes on windows?
275
assert isinstance(name, basestring)
280
print >>f, "contents of", name
285
class MetaTestLog(TestCase):
286
def test_logging(self):
287
"""Test logs are captured when a test fails."""
288
logging.info('an info message')
289
warning('something looks dodgy...')
290
logging.debug('hello, test is running')
294
def selftest(verbose=False, pattern=".*"):
295
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
299
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
300
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
301
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
302
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_merge_core',
328
'bzrlib.selftest.test_smart_add',
329
'bzrlib.selftest.testdiff',
330
'bzrlib.selftest.test_xml',
332
'bzrlib.selftest.teststore',
333
'bzrlib.selftest.testgraph',
336
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
337
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
338
if m not in MODULES_TO_DOCTEST:
339
MODULES_TO_DOCTEST.append(m)
341
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
342
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
345
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
346
for m in MODULES_TO_TEST:
347
suite.addTest(TestLoader().loadTestsFromModule(m))
348
for m in (MODULES_TO_DOCTEST):
349
suite.addTest(DocTestSuite(m))
350
for p in bzrlib.plugin.all_plugins:
351
if hasattr(p, 'test_suite'):
352
suite.addTest(p.test_suite())