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
79
logging.getLogger('').removeHandler(self._log_hdlr)
80
bzrlib.trace.enable_default_logging()
81
logging.debug('%s teardown', self.id())
82
self._log_file.close()
83
unittest.TestCase.tearDown(self)
90
"""Return as a string the log for this test"""
91
return open(self._log_file_name).read()
93
def run_bzr(self, *args, **kwargs):
94
"""Invoke bzr, as if it were run from the command line.
96
This should be the main method for tests that want to exercise the
97
overall behavior of the bzr application (rather than a unit test
98
or a functional test of the library.)
100
Much of the old code runs bzr by forking a new copy of Python, but
101
that is slower, harder to debug, and generally not necessary.
103
retcode = kwargs.get('retcode', 0)
104
result = self.apply_redirected(None, None, None,
105
bzrlib.commands.run_bzr, args)
106
self.assertEquals(result, retcode)
108
def check_inventory_shape(self, inv, shape):
110
Compare an inventory to a list of expected names.
112
Fail if they are not precisely equal.
115
shape = list(shape) # copy
116
for path, ie in inv.entries():
117
name = path.replace('\\', '/')
125
self.fail("expected paths not found in inventory: %r" % shape)
127
self.fail("unexpected paths found in inventory: %r" % extras)
129
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
130
a_callable=None, *args, **kwargs):
131
"""Call callable with redirected std io pipes.
133
Returns the return code."""
134
from StringIO import StringIO
135
if not callable(a_callable):
136
raise ValueError("a_callable must be callable.")
140
stdout = self._log_file
142
stderr = self._log_file
143
real_stdin = sys.stdin
144
real_stdout = sys.stdout
145
real_stderr = sys.stderr
150
return a_callable(*args, **kwargs)
152
sys.stdout = real_stdout
153
sys.stderr = real_stderr
154
sys.stdin = real_stdin
157
BzrTestBase = TestCase
160
class TestCaseInTempDir(TestCase):
161
"""Derived class that runs a test within a temporary directory.
163
This is useful for tests that need to create a branch, etc.
165
The directory is created in a slightly complex way: for each
166
Python invocation, a new temporary top-level directory is created.
167
All test cases create their own directory within that. If the
168
tests complete successfully, the directory is removed.
170
InTempDir is an old alias for FunctionalTestCase.
175
OVERRIDE_PYTHON = 'python'
177
def check_file_contents(self, filename, expect):
178
self.log("check contents of file %s" % filename)
179
contents = file(filename, 'r').read()
180
if contents != expect:
181
self.log("expected: %r" % expect)
182
self.log("actually: %r" % contents)
183
self.fail("contents of %s not as expected")
185
def _make_test_root(self):
190
if TestCaseInTempDir.TEST_ROOT is not None:
192
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
193
tempfile.mkdtemp(suffix='.tmp',
194
prefix=self._TEST_NAME + '-',
197
# make a fake bzr directory there to prevent any tests propagating
198
# up onto the source directory's real branch
199
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
202
super(TestCaseInTempDir, self).setUp()
204
self._make_test_root()
205
self._currentdir = os.getcwdu()
206
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
207
os.mkdir(self.test_dir)
208
os.chdir(self.test_dir)
212
os.chdir(self._currentdir)
213
super(TestCaseInTempDir, self).tearDown()
215
def _formcmd(self, cmd):
216
if isinstance(cmd, basestring):
219
cmd[0] = self.BZRPATH
220
if self.OVERRIDE_PYTHON:
221
cmd.insert(0, self.OVERRIDE_PYTHON)
222
self.log('$ %r' % cmd)
225
def runcmd(self, cmd, retcode=0):
226
"""Run one command and check the return code.
228
Returns a tuple of (stdout,stderr) strings.
230
If a single string is based, it is split into words.
231
For commands that are not simple space-separated words, please
232
pass a list instead."""
233
cmd = self._formcmd(cmd)
234
self.log('$ ' + ' '.join(cmd))
235
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
236
stderr=self._log_file)
237
if retcode != actual_retcode:
238
raise CommandFailed("test failed: %r returned %d, expected %d"
239
% (cmd, actual_retcode, retcode))
241
def backtick(self, cmd, retcode=0):
242
"""Run a command and return its output"""
243
cmd = self._formcmd(cmd)
244
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
245
outd, errd = child.communicate()
247
actual_retcode = child.wait()
249
outd = outd.replace('\r', '')
251
if retcode != actual_retcode:
252
raise CommandFailed("test failed: %r returned %d, expected %d"
253
% (cmd, actual_retcode, retcode))
259
def build_tree(self, shape):
260
"""Build a test tree according to a pattern.
262
shape is a sequence of file specifications. If the final
263
character is '/', a directory is created.
265
This doesn't add anything to a branch.
267
# XXX: It's OK to just create them using forward slashes on windows?
270
assert isinstance(name, basestring)
275
print >>f, "contents of", name
280
class MetaTestLog(TestCase):
281
def test_logging(self):
282
"""Test logs are captured when a test fails."""
283
logging.info('an info message')
284
warning('something looks dodgy...')
285
logging.debug('hello, test is running')
289
def selftest(verbose=False, pattern=".*"):
290
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
294
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
295
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
296
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
297
from doctest import DocTestSuite
303
global MODULES_TO_TEST, MODULES_TO_DOCTEST
306
['bzrlib.selftest.MetaTestLog',
307
'bzrlib.selftest.test_parent',
308
'bzrlib.selftest.testinv',
309
'bzrlib.selftest.testfetch',
310
'bzrlib.selftest.versioning',
311
'bzrlib.selftest.whitebox',
312
'bzrlib.selftest.testmerge3',
313
'bzrlib.selftest.testhashcache',
314
'bzrlib.selftest.teststatus',
315
'bzrlib.selftest.testlog',
316
'bzrlib.selftest.blackbox',
317
'bzrlib.selftest.testrevisionnamespaces',
318
'bzrlib.selftest.testbranch',
319
'bzrlib.selftest.testrevision',
320
'bzrlib.selftest.test_merge_core',
321
'bzrlib.selftest.test_smart_add',
322
'bzrlib.selftest.testdiff',
326
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
327
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
328
if m not in MODULES_TO_DOCTEST:
329
MODULES_TO_DOCTEST.append(m)
331
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
332
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
335
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
336
for m in MODULES_TO_TEST:
337
suite.addTest(TestLoader().loadTestsFromModule(m))
338
for m in (MODULES_TO_DOCTEST):
339
suite.addTest(DocTestSuite(m))
340
for p in bzrlib.plugin.all_plugins:
341
if hasattr(p, 'test_suite'):
342
suite.addTest(p.test_suite())