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
151
result = a_callable(*args, **kwargs)
153
sys.stdout = real_stdout
154
sys.stderr = real_stderr
155
sys.stdin = real_stdin
159
BzrTestBase = TestCase
162
class TestCaseInTempDir(TestCase):
163
"""Derived class that runs a test within a temporary directory.
165
This is useful for tests that need to create a branch, etc.
167
The directory is created in a slightly complex way: for each
168
Python invocation, a new temporary top-level directory is created.
169
All test cases create their own directory within that. If the
170
tests complete successfully, the directory is removed.
172
InTempDir is an old alias for FunctionalTestCase.
177
OVERRIDE_PYTHON = 'python'
179
def check_file_contents(self, filename, expect):
180
self.log("check contents of file %s" % filename)
181
contents = file(filename, 'r').read()
182
if contents != expect:
183
self.log("expected: %r" % expect)
184
self.log("actually: %r" % contents)
185
self.fail("contents of %s not as expected")
187
def _make_test_root(self):
192
if TestCaseInTempDir.TEST_ROOT is not None:
194
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
195
tempfile.mkdtemp(suffix='.tmp',
196
prefix=self._TEST_NAME + '-',
199
# make a fake bzr directory there to prevent any tests propagating
200
# up onto the source directory's real branch
201
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
204
super(TestCaseInTempDir, self).setUp()
206
self._make_test_root()
207
self._currentdir = os.getcwdu()
208
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
209
os.mkdir(self.test_dir)
210
os.chdir(self.test_dir)
214
os.chdir(self._currentdir)
215
super(TestCaseInTempDir, self).tearDown()
217
def _formcmd(self, cmd):
218
if isinstance(cmd, basestring):
221
cmd[0] = self.BZRPATH
222
if self.OVERRIDE_PYTHON:
223
cmd.insert(0, self.OVERRIDE_PYTHON)
224
self.log('$ %r' % cmd)
227
def runcmd(self, cmd, retcode=0):
228
"""Run one command and check the return code.
230
Returns a tuple of (stdout,stderr) strings.
232
If a single string is based, it is split into words.
233
For commands that are not simple space-separated words, please
234
pass a list instead."""
235
cmd = self._formcmd(cmd)
236
self.log('$ ' + ' '.join(cmd))
237
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
238
stderr=self._log_file)
239
if retcode != actual_retcode:
240
raise CommandFailed("test failed: %r returned %d, expected %d"
241
% (cmd, actual_retcode, retcode))
243
def backtick(self, cmd, retcode=0):
244
"""Run a command and return its output"""
245
cmd = self._formcmd(cmd)
246
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
247
outd, errd = child.communicate()
249
actual_retcode = child.wait()
251
outd = outd.replace('\r', '')
253
if retcode != actual_retcode:
254
raise CommandFailed("test failed: %r returned %d, expected %d"
255
% (cmd, actual_retcode, retcode))
261
def build_tree(self, shape):
262
"""Build a test tree according to a pattern.
264
shape is a sequence of file specifications. If the final
265
character is '/', a directory is created.
267
This doesn't add anything to a branch.
269
# XXX: It's OK to just create them using forward slashes on windows?
272
assert isinstance(name, basestring)
277
print >>f, "contents of", name
282
class MetaTestLog(TestCase):
283
def test_logging(self):
284
"""Test logs are captured when a test fails."""
285
logging.info('an info message')
286
warning('something looks dodgy...')
287
logging.debug('hello, test is running')
291
def selftest(verbose=False, pattern=".*"):
292
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
296
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
297
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
298
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
299
from doctest import DocTestSuite
305
global MODULES_TO_TEST, MODULES_TO_DOCTEST
308
['bzrlib.selftest.MetaTestLog',
309
'bzrlib.selftest.test_parent',
310
'bzrlib.selftest.testinv',
311
'bzrlib.selftest.testfetch',
312
'bzrlib.selftest.versioning',
313
'bzrlib.selftest.whitebox',
314
'bzrlib.selftest.testmerge3',
315
'bzrlib.selftest.testhashcache',
316
'bzrlib.selftest.teststatus',
317
'bzrlib.selftest.testlog',
318
'bzrlib.selftest.blackbox',
319
'bzrlib.selftest.testrevisionnamespaces',
320
'bzrlib.selftest.testbranch',
321
'bzrlib.selftest.testrevision',
322
'bzrlib.selftest.test_merge_core',
323
'bzrlib.selftest.test_smart_add',
324
'bzrlib.selftest.testdiff',
328
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
329
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
330
if m not in MODULES_TO_DOCTEST:
331
MODULES_TO_DOCTEST.append(m)
333
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
334
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
337
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
338
for m in MODULES_TO_TEST:
339
suite.addTest(TestLoader().loadTestsFromModule(m))
340
for m in (MODULES_TO_DOCTEST):
341
suite.addTest(DocTestSuite(m))
342
for p in bzrlib.plugin.all_plugins:
343
if hasattr(p, 'test_suite'):
344
suite.addTest(p.test_suite())