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)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
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)
92
"""Return as a string the log for this test"""
93
return open(self._log_file_name).read()
95
def run_bzr(self, *args, **kwargs):
96
"""Invoke bzr, as if it were run from the command line.
98
This should be the main method for tests that want to exercise the
99
overall behavior of the bzr application (rather than a unit test
100
or a functional test of the library.)
102
Much of the old code runs bzr by forking a new copy of Python, but
103
that is slower, harder to debug, and generally not necessary.
105
retcode = kwargs.get('retcode', 0)
106
result = self.apply_redirected(None, None, None,
107
bzrlib.commands.run_bzr, args)
108
self.assertEquals(result, retcode)
111
def check_inventory_shape(self, inv, shape):
113
Compare an inventory to a list of expected names.
115
Fail if they are not precisely equal.
118
shape = list(shape) # copy
119
for path, ie in inv.entries():
120
name = path.replace('\\', '/')
128
self.fail("expected paths not found in inventory: %r" % shape)
130
self.fail("unexpected paths found in inventory: %r" % extras)
132
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
133
a_callable=None, *args, **kwargs):
134
"""Call callable with redirected std io pipes.
136
Returns the return code."""
137
from StringIO import StringIO
138
if not callable(a_callable):
139
raise ValueError("a_callable must be callable.")
143
stdout = self._log_file
145
stderr = self._log_file
146
real_stdin = sys.stdin
147
real_stdout = sys.stdout
148
real_stderr = sys.stderr
153
return a_callable(*args, **kwargs)
155
sys.stdout = real_stdout
156
sys.stderr = real_stderr
157
sys.stdin = real_stdin
160
BzrTestBase = TestCase
163
class TestCaseInTempDir(TestCase):
164
"""Derived class that runs a test within a temporary directory.
166
This is useful for tests that need to create a branch, etc.
168
The directory is created in a slightly complex way: for each
169
Python invocation, a new temporary top-level directory is created.
170
All test cases create their own directory within that. If the
171
tests complete successfully, the directory is removed.
173
InTempDir is an old alias for FunctionalTestCase.
178
OVERRIDE_PYTHON = 'python'
180
def check_file_contents(self, filename, expect):
181
self.log("check contents of file %s" % filename)
182
contents = file(filename, 'r').read()
183
if contents != expect:
184
self.log("expected: %r" % expect)
185
self.log("actually: %r" % contents)
186
self.fail("contents of %s not as expected")
188
def _make_test_root(self):
189
if TestCaseInTempDir.TEST_ROOT is not None:
193
root = 'test%04d.tmp' % i
197
if e.errno == errno.EEXIST:
202
# successfully created
203
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
205
# make a fake bzr directory there to prevent any tests propagating
206
# up onto the source directory's real branch
207
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
210
super(TestCaseInTempDir, self).setUp()
212
self._make_test_root()
213
self._currentdir = os.getcwdu()
214
short_id = self.id().replace('bzrlib.selftest.', '') \
215
.replace('__main__.', '')
216
self.test_dir = os.path.join(self.TEST_ROOT, short_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
"""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
315
global MODULES_TO_TEST, MODULES_TO_DOCTEST
318
['bzrlib.selftest.MetaTestLog',
319
'bzrlib.selftest.testinv',
320
'bzrlib.selftest.versioning',
321
'bzrlib.selftest.testmerge3',
322
'bzrlib.selftest.testhashcache',
323
'bzrlib.selftest.teststatus',
324
'bzrlib.selftest.testlog',
325
'bzrlib.selftest.testrevisionnamespaces',
326
'bzrlib.selftest.testbranch',
327
# 'bzrlib.selftest.testrevision',
328
# 'bzrlib.selftest.test_merge_core',
329
'bzrlib.selftest.test_smart_add',
330
'bzrlib.selftest.testdiff',
331
# 'bzrlib.selftest.test_parent',
332
'bzrlib.selftest.test_xml',
333
# 'bzrlib.selftest.testfetch',
334
# 'bzrlib.selftest.whitebox',
335
'bzrlib.selftest.teststore',
336
# 'bzrlib.selftest.blackbox',
339
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
340
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
341
if m not in MODULES_TO_DOCTEST:
342
MODULES_TO_DOCTEST.append(m)
344
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
345
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
348
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
349
for m in MODULES_TO_TEST:
350
suite.addTest(TestLoader().loadTestsFromModule(m))
351
for m in (MODULES_TO_DOCTEST):
352
suite.addTest(DocTestSuite(m))
353
for p in bzrlib.plugin.all_plugins:
354
if hasattr(p, 'test_suite'):
355
suite.addTest(p.test_suite())