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
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()
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)
109
def check_inventory_shape(self, inv, shape):
111
Compare an inventory to a list of expected names.
113
Fail if they are not precisely equal.
116
shape = list(shape) # copy
117
for path, ie in inv.entries():
118
name = path.replace('\\', '/')
126
self.fail("expected paths not found in inventory: %r" % shape)
128
self.fail("unexpected paths found in inventory: %r" % extras)
130
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
131
a_callable=None, *args, **kwargs):
132
"""Call callable with redirected std io pipes.
134
Returns the return code."""
135
from StringIO import StringIO
136
if not callable(a_callable):
137
raise ValueError("a_callable must be callable.")
141
if hasattr(self, "_log_file"):
142
stdout = self._log_file
146
if hasattr(self, "_log_file"):
147
stderr = self._log_file
150
real_stdin = sys.stdin
151
real_stdout = sys.stdout
152
real_stderr = sys.stderr
157
return a_callable(*args, **kwargs)
159
sys.stdout = real_stdout
160
sys.stderr = real_stderr
161
sys.stdin = real_stdin
164
BzrTestBase = TestCase
167
class TestCaseInTempDir(TestCase):
168
"""Derived class that runs a test within a temporary directory.
170
This is useful for tests that need to create a branch, etc.
172
The directory is created in a slightly complex way: for each
173
Python invocation, a new temporary top-level directory is created.
174
All test cases create their own directory within that. If the
175
tests complete successfully, the directory is removed.
177
InTempDir is an old alias for FunctionalTestCase.
182
OVERRIDE_PYTHON = 'python'
184
def check_file_contents(self, filename, expect):
185
self.log("check contents of file %s" % filename)
186
contents = file(filename, 'r').read()
187
if contents != expect:
188
self.log("expected: %r" % expect)
189
self.log("actually: %r" % contents)
190
self.fail("contents of %s not as expected")
192
def _make_test_root(self):
193
if TestCaseInTempDir.TEST_ROOT is not None:
197
root = 'test%04d.tmp' % i
201
if e.errno == errno.EEXIST:
206
# successfully created
207
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
209
# make a fake bzr directory there to prevent any tests propagating
210
# up onto the source directory's real branch
211
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
214
super(TestCaseInTempDir, self).setUp()
216
self._make_test_root()
217
self._currentdir = os.getcwdu()
218
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
219
os.mkdir(self.test_dir)
220
os.chdir(self.test_dir)
224
os.chdir(self._currentdir)
225
super(TestCaseInTempDir, self).tearDown()
227
def _formcmd(self, cmd):
228
if isinstance(cmd, basestring):
231
cmd[0] = self.BZRPATH
232
if self.OVERRIDE_PYTHON:
233
cmd.insert(0, self.OVERRIDE_PYTHON)
234
self.log('$ %r' % cmd)
237
def runcmd(self, cmd, retcode=0):
238
"""Run one command and check the return code.
240
Returns a tuple of (stdout,stderr) strings.
242
If a single string is based, it is split into words.
243
For commands that are not simple space-separated words, please
244
pass a list instead."""
245
cmd = self._formcmd(cmd)
246
self.log('$ ' + ' '.join(cmd))
247
child = subprocess.Popen(cmd, stdout=subprocess.PIPE,
248
stderr=subprocess.PIPE)
249
outd, errd = child.communicate()
253
actual_retcode = child.wait()
254
if retcode != actual_retcode:
255
raise CommandFailed("test failed: %r returned %d, expected %d"
256
% (cmd, actual_retcode, retcode))
260
def backtick(self, cmd, retcode=0):
261
"""Run a command and return its output"""
262
cmd = self._formcmd(cmd)
263
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
264
outd, errd = child.communicate()
266
actual_retcode = child.wait()
268
outd = outd.replace('\r', '')
270
if retcode != actual_retcode:
271
raise CommandFailed("test failed: %r returned %d, expected %d"
272
% (cmd, actual_retcode, retcode))
278
def build_tree(self, shape):
279
"""Build a test tree according to a pattern.
281
shape is a sequence of file specifications. If the final
282
character is '/', a directory is created.
284
This doesn't add anything to a branch.
286
# XXX: It's OK to just create them using forward slashes on windows?
289
assert isinstance(name, basestring)
294
print >>f, "contents of", name
299
class MetaTestLog(TestCase):
300
def test_logging(self):
301
"""Test logs are captured when a test fails."""
302
logging.info('an info message')
303
warning('something looks dodgy...')
304
logging.debug('hello, test is running')
308
def selftest(verbose=False, pattern=".*"):
309
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
313
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
314
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
315
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
316
from doctest import DocTestSuite
322
global MODULES_TO_TEST, MODULES_TO_DOCTEST
325
['bzrlib.selftest.MetaTestLog',
326
'bzrlib.selftest.test_parent',
327
'bzrlib.selftest.testinv',
328
'bzrlib.selftest.testfetch',
329
'bzrlib.selftest.versioning',
330
'bzrlib.selftest.whitebox',
331
'bzrlib.selftest.testmerge3',
332
'bzrlib.selftest.testmerge',
333
'bzrlib.selftest.testhashcache',
334
'bzrlib.selftest.teststatus',
335
'bzrlib.selftest.testlog',
336
'bzrlib.selftest.blackbox',
337
'bzrlib.selftest.testrevisionnamespaces',
338
'bzrlib.selftest.testbranch',
339
'bzrlib.selftest.testremotebranch',
340
'bzrlib.selftest.testrevision',
341
'bzrlib.selftest.test_merge_core',
342
'bzrlib.selftest.test_smart_add',
343
'bzrlib.selftest.testdiff',
344
'bzrlib.selftest.test_xml',
346
'bzrlib.selftest.teststore',
347
'bzrlib.selftest.testgraph',
350
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
351
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
352
if m not in MODULES_TO_DOCTEST:
353
MODULES_TO_DOCTEST.append(m)
355
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
356
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
359
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
360
for m in MODULES_TO_TEST:
361
suite.addTest(TestLoader().loadTestsFromModule(m))
362
for m in (MODULES_TO_DOCTEST):
363
suite.addTest(DocTestSuite(m))
364
for p in bzrlib.plugin.all_plugins:
365
if hasattr(p, 'test_suite'):
366
suite.addTest(p.test_suite())