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):
112
"""Compare an inventory to a list of expected names.
114
Fail if they are not precisely equal.
117
shape = list(shape) # copy
118
for path, ie in inv.entries():
119
name = path.replace('\\', '/')
127
self.fail("expected paths not found in inventory: %r" % shape)
129
self.fail("unexpected paths found in inventory: %r" % extras)
131
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
132
a_callable=None, *args, **kwargs):
133
"""Call callable with redirected std io pipes.
135
Returns the return code."""
136
from StringIO import StringIO
137
if not callable(a_callable):
138
raise ValueError("a_callable must be callable.")
142
stdout = self._log_file
144
stderr = self._log_file
145
real_stdin = sys.stdin
146
real_stdout = sys.stdout
147
real_stderr = sys.stderr
152
return a_callable(*args, **kwargs)
154
sys.stdout = real_stdout
155
sys.stderr = real_stderr
156
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):
188
if TestCaseInTempDir.TEST_ROOT is not None:
192
root = 'test%04d.tmp' % i
196
if e.errno == errno.EEXIST:
201
# successfully created
202
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
204
# make a fake bzr directory there to prevent any tests propagating
205
# up onto the source directory's real branch
206
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
209
super(TestCaseInTempDir, self).setUp()
211
self._make_test_root()
212
self._currentdir = os.getcwdu()
213
short_id = self.id().replace('bzrlib.selftest.', '') \
214
.replace('__main__.', '')
215
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
216
os.mkdir(self.test_dir)
217
os.chdir(self.test_dir)
221
os.chdir(self._currentdir)
222
super(TestCaseInTempDir, self).tearDown()
224
def _formcmd(self, cmd):
225
if isinstance(cmd, basestring):
228
cmd[0] = self.BZRPATH
229
if self.OVERRIDE_PYTHON:
230
cmd.insert(0, self.OVERRIDE_PYTHON)
231
self.log('$ %r' % cmd)
234
def runcmd(self, cmd, retcode=0):
235
"""Run one command and check the return code.
237
Returns a tuple of (stdout,stderr) strings.
239
If a single string is based, it is split into words.
240
For commands that are not simple space-separated words, please
241
pass a list instead."""
242
cmd = self._formcmd(cmd)
243
self.log('$ ' + ' '.join(cmd))
244
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
245
stderr=self._log_file)
246
if retcode != actual_retcode:
247
raise CommandFailed("test failed: %r returned %d, expected %d"
248
% (cmd, actual_retcode, retcode))
250
def backtick(self, cmd, retcode=0):
251
"""Run a command and return its output"""
252
cmd = self._formcmd(cmd)
253
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
254
outd, errd = child.communicate()
256
actual_retcode = child.wait()
258
outd = outd.replace('\r', '')
260
if retcode != actual_retcode:
261
raise CommandFailed("test failed: %r returned %d, expected %d"
262
% (cmd, actual_retcode, retcode))
268
def build_tree(self, shape):
269
"""Build a test tree according to a pattern.
271
shape is a sequence of file specifications. If the final
272
character is '/', a directory is created.
274
This doesn't add anything to a branch.
276
# XXX: It's OK to just create them using forward slashes on windows?
279
assert isinstance(name, basestring)
284
print >>f, "contents of", name
289
class MetaTestLog(TestCase):
290
def test_logging(self):
291
"""Test logs are captured when a test fails."""
292
logging.info('an info message')
293
warning('something looks dodgy...')
294
logging.debug('hello, test is running')
298
def selftest(verbose=False, pattern=".*"):
299
"""Run the whole test suite under the enhanced runner"""
300
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
304
"""Build and return TestSuite for the whole program."""
305
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
306
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
307
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
308
from doctest import DocTestSuite
314
global MODULES_TO_TEST, MODULES_TO_DOCTEST
317
['bzrlib.selftest.MetaTestLog',
318
'bzrlib.selftest.testinv',
319
'bzrlib.selftest.test_commit',
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.test_weave',
334
'bzrlib.selftest.testfetch',
335
'bzrlib.selftest.whitebox',
336
'bzrlib.selftest.teststore',
337
'bzrlib.selftest.blackbox',
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())