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)8s %(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):
156
"""Compare an inventory to a list of expected names.
158
Fail if they are not precisely equal.
161
shape = list(shape) # copy
162
for path, ie in inv.entries():
163
name = path.replace('\\', '/')
171
self.fail("expected paths not found in inventory: %r" % shape)
173
self.fail("unexpected paths found in inventory: %r" % extras)
175
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
176
a_callable=None, *args, **kwargs):
177
"""Call callable with redirected std io pipes.
179
Returns the return code."""
180
if not callable(a_callable):
181
raise ValueError("a_callable must be callable.")
185
if hasattr(self, "_log_file"):
186
stdout = self._log_file
190
if hasattr(self, "_log_file"):
191
stderr = self._log_file
194
real_stdin = sys.stdin
195
real_stdout = sys.stdout
196
real_stderr = sys.stderr
201
return a_callable(*args, **kwargs)
203
sys.stdout = real_stdout
204
sys.stderr = real_stderr
205
sys.stdin = real_stdin
208
BzrTestBase = TestCase
211
class TestCaseInTempDir(TestCase):
212
"""Derived class that runs a test within a temporary directory.
214
This is useful for tests that need to create a branch, etc.
216
The directory is created in a slightly complex way: for each
217
Python invocation, a new temporary top-level directory is created.
218
All test cases create their own directory within that. If the
219
tests complete successfully, the directory is removed.
221
InTempDir is an old alias for FunctionalTestCase.
226
OVERRIDE_PYTHON = 'python'
228
def check_file_contents(self, filename, expect):
229
self.log("check contents of file %s" % filename)
230
contents = file(filename, 'r').read()
231
if contents != expect:
232
self.log("expected: %r" % expect)
233
self.log("actually: %r" % contents)
234
self.fail("contents of %s not as expected" % filename)
236
def _make_test_root(self):
237
if TestCaseInTempDir.TEST_ROOT is not None:
241
root = 'test%04d.tmp' % i
245
if e.errno == errno.EEXIST:
250
# successfully created
251
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
253
# make a fake bzr directory there to prevent any tests propagating
254
# up onto the source directory's real branch
255
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
258
super(TestCaseInTempDir, self).setUp()
259
self._make_test_root()
260
self._currentdir = os.getcwdu()
261
short_id = self.id().replace('bzrlib.selftest.', '') \
262
.replace('__main__.', '')
263
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
264
os.mkdir(self.test_dir)
265
os.chdir(self.test_dir)
268
os.chdir(self._currentdir)
269
super(TestCaseInTempDir, self).tearDown()
271
def build_tree(self, shape):
272
"""Build a test tree according to a pattern.
274
shape is a sequence of file specifications. If the final
275
character is '/', a directory is created.
277
This doesn't add anything to a branch.
279
# XXX: It's OK to just create them using forward slashes on windows?
281
assert isinstance(name, basestring)
286
print >>f, "contents of", name
290
class MetaTestLog(TestCase):
291
def test_logging(self):
292
"""Test logs are captured when a test fails."""
293
logging.info('an info message')
294
warning('something looks dodgy...')
295
logging.debug('hello, test is running')
299
def selftest(verbose=False, pattern=".*"):
300
"""Run the whole test suite under the enhanced runner"""
301
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
305
"""Build and return TestSuite for the whole program."""
306
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
307
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
308
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
309
from doctest import DocTestSuite
311
global MODULES_TO_TEST, MODULES_TO_DOCTEST
314
['bzrlib.selftest.MetaTestLog',
315
'bzrlib.selftest.testinv',
316
'bzrlib.selftest.test_ancestry',
317
'bzrlib.selftest.test_commit',
318
'bzrlib.selftest.test_commit_merge',
319
'bzrlib.selftest.versioning',
320
'bzrlib.selftest.testmerge3',
321
'bzrlib.selftest.testmerge',
322
'bzrlib.selftest.testhashcache',
323
'bzrlib.selftest.teststatus',
324
'bzrlib.selftest.testlog',
325
'bzrlib.selftest.testrevisionnamespaces',
326
'bzrlib.selftest.testbranch',
327
'bzrlib.selftest.testremotebranch',
328
'bzrlib.selftest.testrevision',
329
'bzrlib.selftest.test_revision_info',
330
'bzrlib.selftest.test_merge_core',
331
'bzrlib.selftest.test_smart_add',
332
'bzrlib.selftest.test_bad_files',
333
'bzrlib.selftest.testdiff',
334
'bzrlib.selftest.test_parent',
335
'bzrlib.selftest.test_xml',
336
'bzrlib.selftest.test_weave',
337
'bzrlib.selftest.testfetch',
338
'bzrlib.selftest.whitebox',
339
'bzrlib.selftest.teststore',
340
'bzrlib.selftest.blackbox',
341
'bzrlib.selftest.testgraph',
344
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
345
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
346
if m not in MODULES_TO_DOCTEST:
347
MODULES_TO_DOCTEST.append(m)
349
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
350
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
353
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
354
for m in MODULES_TO_TEST:
355
suite.addTest(TestLoader().loadTestsFromModule(m))
356
for m in (MODULES_TO_DOCTEST):
357
suite.addTest(DocTestSuite(m))
358
for p in bzrlib.plugin.all_plugins:
359
if hasattr(p, 'test_suite'):
360
suite.addTest(p.test_suite())