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
77
def run(self, result):
78
self.apply_redirected(None, None, None,
79
unittest.TestCase.run, self, result)
82
logging.getLogger('').removeHandler(self._log_hdlr)
83
bzrlib.trace.enable_default_logging()
84
logging.debug('%s teardown', self.id())
85
self._log_file.close()
86
unittest.TestCase.tearDown(self)
93
"""Return as a string the log for this test"""
94
return open(self._log_file_name).read()
96
def run_bzr(self, *args, **kwargs):
97
"""Invoke bzr, as if it were run from the command line.
99
This should be the main method for tests that want to exercise the
100
overall behavior of the bzr application (rather than a unit test
101
or a functional test of the library.)
103
Much of the old code runs bzr by forking a new copy of Python, but
104
that is slower, harder to debug, and generally not necessary.
106
retcode = kwargs.get('retcode', 0)
107
result = self.apply_redirected(None, None, None,
108
bzrlib.commands.run_bzr, args)
109
self.assertEquals(result, retcode)
112
def check_inventory_shape(self, inv, shape):
114
Compare an inventory to a list of expected names.
116
Fail if they are not precisely equal.
119
shape = list(shape) # copy
120
for path, ie in inv.entries():
121
name = path.replace('\\', '/')
129
self.fail("expected paths not found in inventory: %r" % shape)
131
self.fail("unexpected paths found in inventory: %r" % extras)
133
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
134
a_callable=None, *args, **kwargs):
135
"""Call callable with redirected std io pipes.
137
Returns the return code."""
138
from StringIO import StringIO
139
if not callable(a_callable):
140
raise ValueError("a_callable must be callable.")
144
if hasattr(self, "_log_file"):
145
stdout = self._log_file
149
if hasattr(self, "_log_file"):
150
stderr = self._log_file
153
real_stdin = sys.stdin
154
real_stdout = sys.stdout
155
real_stderr = sys.stderr
160
return a_callable(*args, **kwargs)
162
sys.stdout = real_stdout
163
sys.stderr = real_stderr
164
sys.stdin = real_stdin
167
BzrTestBase = TestCase
170
class TestCaseInTempDir(TestCase):
171
"""Derived class that runs a test within a temporary directory.
173
This is useful for tests that need to create a branch, etc.
175
The directory is created in a slightly complex way: for each
176
Python invocation, a new temporary top-level directory is created.
177
All test cases create their own directory within that. If the
178
tests complete successfully, the directory is removed.
180
InTempDir is an old alias for FunctionalTestCase.
185
OVERRIDE_PYTHON = 'python'
187
def check_file_contents(self, filename, expect):
188
self.log("check contents of file %s" % filename)
189
contents = file(filename, 'r').read()
190
if contents != expect:
191
self.log("expected: %r" % expect)
192
self.log("actually: %r" % contents)
193
self.fail("contents of %s not as expected")
195
def _make_test_root(self):
200
if TestCaseInTempDir.TEST_ROOT is not None:
202
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
203
tempfile.mkdtemp(suffix='.tmp',
204
prefix=self._TEST_NAME + '-',
207
# make a fake bzr directory there to prevent any tests propagating
208
# up onto the source directory's real branch
209
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
212
super(TestCaseInTempDir, self).setUp()
214
self._make_test_root()
215
self._currentdir = os.getcwdu()
216
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
217
os.mkdir(self.test_dir)
218
os.chdir(self.test_dir)
222
os.chdir(self._currentdir)
223
super(TestCaseInTempDir, self).tearDown()
225
def _formcmd(self, cmd):
226
if isinstance(cmd, basestring):
229
cmd[0] = self.BZRPATH
230
if self.OVERRIDE_PYTHON:
231
cmd.insert(0, self.OVERRIDE_PYTHON)
232
self.log('$ %r' % cmd)
235
def runcmd(self, cmd, retcode=0):
236
"""Run one command and check the return code.
238
Returns a tuple of (stdout,stderr) strings.
240
If a single string is based, it is split into words.
241
For commands that are not simple space-separated words, please
242
pass a list instead."""
243
cmd = self._formcmd(cmd)
244
self.log('$ ' + ' '.join(cmd))
245
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
246
stderr=self._log_file)
247
if retcode != actual_retcode:
248
raise CommandFailed("test failed: %r returned %d, expected %d"
249
% (cmd, actual_retcode, retcode))
251
def backtick(self, cmd, retcode=0):
252
"""Run a command and return its output"""
253
cmd = self._formcmd(cmd)
254
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
255
outd, errd = child.communicate()
257
actual_retcode = child.wait()
259
outd = outd.replace('\r', '')
261
if retcode != actual_retcode:
262
raise CommandFailed("test failed: %r returned %d, expected %d"
263
% (cmd, actual_retcode, retcode))
269
def build_tree(self, shape):
270
"""Build a test tree according to a pattern.
272
shape is a sequence of file specifications. If the final
273
character is '/', a directory is created.
275
This doesn't add anything to a branch.
277
# XXX: It's OK to just create them using forward slashes on windows?
280
assert isinstance(name, basestring)
285
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
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
304
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
305
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
306
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
307
from doctest import DocTestSuite
313
global MODULES_TO_TEST, MODULES_TO_DOCTEST
316
['bzrlib.selftest.MetaTestLog',
317
'bzrlib.selftest.test_parent',
318
'bzrlib.selftest.testinv',
319
'bzrlib.selftest.testfetch',
320
'bzrlib.selftest.versioning',
321
'bzrlib.selftest.whitebox',
322
'bzrlib.selftest.testmerge3',
323
'bzrlib.selftest.testmerge',
324
'bzrlib.selftest.testhashcache',
325
'bzrlib.selftest.teststatus',
326
'bzrlib.selftest.testlog',
327
'bzrlib.selftest.blackbox',
328
'bzrlib.selftest.testrevisionnamespaces',
329
'bzrlib.selftest.testbranch',
330
'bzrlib.selftest.testrevision',
331
'bzrlib.selftest.test_merge_core',
332
'bzrlib.selftest.test_smart_add',
333
'bzrlib.selftest.testdiff',
334
'bzrlib.selftest.test_xml',
336
'bzrlib.selftest.teststore',
337
'bzrlib.selftest.testgraph',
340
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
341
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
342
if m not in MODULES_TO_DOCTEST:
343
MODULES_TO_DOCTEST.append(m)
345
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
346
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
349
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
350
for m in MODULES_TO_TEST:
351
suite.addTest(TestLoader().loadTestsFromModule(m))
352
for m in (MODULES_TO_DOCTEST):
353
suite.addTest(DocTestSuite(m))
354
for p in bzrlib.plugin.all_plugins:
355
if hasattr(p, 'test_suite'):
356
suite.addTest(p.test_suite())