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 warnings import warn
26
from cStringIO import StringIO
29
import bzrlib.commands
36
MODULES_TO_DOCTEST = []
38
from logging import debug, warning, error
40
class CommandFailed(Exception):
43
class TestCase(unittest.TestCase):
44
"""Base class for bzr unit tests.
46
Tests that need access to disk resources should subclass
47
TestCaseInTempDir not TestCase.
49
Error and debug log messages are redirected from their usual
50
location into a temporary file, the contents of which can be
51
retrieved by _get_log().
53
There are also convenience functions to invoke bzr's command-line
54
routine, and to build and check bzr trees."""
59
# this replaces the default testsweet.TestCase; we don't want logging changed
60
unittest.TestCase.setUp(self)
61
bzrlib.trace.disable_default_logging()
62
self._enable_file_logging()
65
def _enable_file_logging(self):
66
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
68
self._log_file = os.fdopen(fileno, 'w+')
70
hdlr = logging.StreamHandler(self._log_file)
71
hdlr.setLevel(logging.DEBUG)
72
hdlr.setFormatter(logging.Formatter('%(levelname)4.4s %(message)s'))
73
logging.getLogger('').addHandler(hdlr)
74
logging.getLogger('').setLevel(logging.DEBUG)
76
debug('opened log file %s', name)
78
self._log_file_name = name
81
logging.getLogger('').removeHandler(self._log_hdlr)
82
bzrlib.trace.enable_default_logging()
83
logging.debug('%s teardown', self.id())
84
self._log_file.close()
85
unittest.TestCase.tearDown(self)
91
"""Return as a string the log for this test"""
92
return open(self._log_file_name).read()
95
def run_bzr_captured(self, argv, retcode=0):
96
"""Invoke bzr and return (result, stdout, stderr).
98
Useful for code that wants to check the contents of the
99
output, the way error messages are presented, etc.
101
This should be the main method for tests that want to exercise the
102
overall behavior of the bzr application (rather than a unit test
103
or a functional test of the library.)
105
Much of the old code runs bzr by forking a new copy of Python, but
106
that is slower, harder to debug, and generally not necessary.
108
This runs bzr through the interface that catches and reports
109
errors, and with logging set to something approximating the
110
default, so that error reporting can be checked.
112
argv -- arguments to invoke bzr
113
retcode -- expected return code, or None for don't-care.
117
self.log('run bzr: %s', ' '.join(argv))
118
handler = logging.StreamHandler(stderr)
119
handler.setFormatter(bzrlib.trace.QuietFormatter())
120
handler.setLevel(logging.INFO)
121
logger = logging.getLogger('')
122
logger.addHandler(handler)
124
result = self.apply_redirected(None, stdout, stderr,
125
bzrlib.commands.run_bzr_catch_errors,
128
logger.removeHandler(handler)
129
out = stdout.getvalue()
130
err = stderr.getvalue()
132
self.log('output:\n%s', out)
134
self.log('errors:\n%s', err)
135
if retcode is not None:
136
self.assertEquals(result, retcode)
140
def run_bzr(self, *args, **kwargs):
141
"""Invoke bzr, as if it were run from the command line.
143
This should be the main method for tests that want to exercise the
144
overall behavior of the bzr application (rather than a unit test
145
or a functional test of the library.)
147
This sends the stdout/stderr results into the test's log,
148
where it may be useful for debugging. See also run_captured.
150
warn('TestBase.run_bzr is deprecated, use TestBase.run_bzr_captured')
151
retcode = kwargs.pop('retcode', 0)
152
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 _formcmd(self, cmd):
271
if isinstance(cmd, basestring):
274
cmd[0] = self.BZRPATH
275
if self.OVERRIDE_PYTHON:
276
cmd.insert(0, self.OVERRIDE_PYTHON)
277
self.log('$ %r' % cmd)
280
def runcmd(self, cmd, retcode=0):
281
"""Run one command and check the return code.
283
Returns a tuple of (stdout,stderr) strings.
285
If a single string is based, it is split into words.
286
For commands that are not simple space-separated words, please
287
pass a list instead."""
288
cmd = self._formcmd(cmd)
289
self.log('$ ' + ' '.join(cmd))
290
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
291
stderr=self._log_file)
292
if retcode != actual_retcode:
293
raise CommandFailed("test failed: %r returned %d, expected %d"
294
% (cmd, actual_retcode, retcode))
296
def backtick(self, cmd, retcode=0):
297
"""Run a command and return its output"""
298
cmd = self._formcmd(cmd)
299
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
300
outd, errd = child.communicate()
302
actual_retcode = child.wait()
304
outd = outd.replace('\r', '')
306
if retcode != actual_retcode:
307
raise CommandFailed("test failed: %r returned %d, expected %d"
308
% (cmd, actual_retcode, retcode))
314
def build_tree(self, shape):
315
"""Build a test tree according to a pattern.
317
shape is a sequence of file specifications. If the final
318
character is '/', a directory is created.
320
This doesn't add anything to a branch.
322
# XXX: It's OK to just create them using forward slashes on windows?
324
assert isinstance(name, basestring)
329
print >>f, "contents of", name
334
class MetaTestLog(TestCase):
335
def test_logging(self):
336
"""Test logs are captured when a test fails."""
337
logging.info('an info message')
338
warning('something looks dodgy...')
339
logging.debug('hello, test is running')
343
def selftest(verbose=False, pattern=".*"):
344
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
348
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
349
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
350
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
351
from doctest import DocTestSuite
353
global MODULES_TO_TEST, MODULES_TO_DOCTEST
356
['bzrlib.selftest.MetaTestLog',
357
'bzrlib.selftest.test_parent',
358
'bzrlib.selftest.testinv',
359
'bzrlib.selftest.testfetch',
360
'bzrlib.selftest.versioning',
361
'bzrlib.selftest.whitebox',
362
'bzrlib.selftest.testmerge3',
363
'bzrlib.selftest.testmerge',
364
'bzrlib.selftest.testhashcache',
365
'bzrlib.selftest.teststatus',
366
'bzrlib.selftest.testlog',
367
'bzrlib.selftest.blackbox',
368
'bzrlib.selftest.testrevisionnamespaces',
369
'bzrlib.selftest.testbranch',
370
'bzrlib.selftest.testremotebranch',
371
'bzrlib.selftest.testrevision',
372
'bzrlib.selftest.test_merge_core',
373
'bzrlib.selftest.test_smart_add',
374
'bzrlib.selftest.testdiff',
375
'bzrlib.selftest.test_xml',
377
'bzrlib.selftest.teststore',
378
'bzrlib.selftest.testgraph',
381
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
382
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
383
if m not in MODULES_TO_DOCTEST:
384
MODULES_TO_DOCTEST.append(m)
386
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
387
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
390
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
391
for m in MODULES_TO_TEST:
392
suite.addTest(TestLoader().loadTestsFromModule(m))
393
for m in (MODULES_TO_DOCTEST):
394
suite.addTest(DocTestSuite(m))
395
for p in bzrlib.plugin.all_plugins:
396
if hasattr(p, 'test_suite'):
397
suite.addTest(p.test_suite())