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
24
from testsweet import run_suite
25
import bzrlib.commands
31
MODULES_TO_DOCTEST = []
33
from logging import debug, warning, error
36
class TestCase(unittest.TestCase):
37
"""Base class for bzr unit tests.
39
Tests that need access to disk resources should subclass
40
TestCaseInTempDir not TestCase.
42
Error and debug log messages are redirected from their usual
43
location into a temporary file, the contents of which can be
44
retrieved by _get_log().
46
There are also convenience functions to invoke bzr's command-line
47
routine, and to build and check bzr trees."""
52
# this replaces the default testsweet.TestCase; we don't want logging changed
53
unittest.TestCase.setUp(self)
54
bzrlib.trace.disable_default_logging()
55
self._enable_file_logging()
58
def _enable_file_logging(self):
59
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
61
self._log_file = os.fdopen(fileno, 'w+')
63
hdlr = logging.StreamHandler(self._log_file)
64
hdlr.setLevel(logging.DEBUG)
65
hdlr.setFormatter(logging.Formatter('%(levelname)4.4s %(message)s'))
66
logging.getLogger('').addHandler(hdlr)
67
logging.getLogger('').setLevel(logging.DEBUG)
69
debug('opened log file %s', name)
71
self._log_file_name = name
75
logging.getLogger('').removeHandler(self._log_hdlr)
76
bzrlib.trace.enable_default_logging()
77
logging.debug('%s teardown', self.id())
78
self._log_file.close()
79
unittest.TestCase.tearDown(self)
86
"""Return as a string the log for this test"""
87
return open(self._log_file_name).read()
89
def run_bzr(self, *args, **kwargs):
90
"""Invoke bzr, as if it were run from the command line.
92
This should be the main method for tests that want to exercise the
93
overall behavior of the bzr application (rather than a unit test
94
or a functional test of the library.)
96
Much of the old code runs bzr by forking a new copy of Python, but
97
that is slower, harder to debug, and generally not necessary.
99
retcode = kwargs.get('retcode', 0)
100
result = self.apply_redirected(None, None, None,
101
bzrlib.commands.run_bzr, args)
102
self.assertEquals(result, retcode)
104
def check_inventory_shape(self, inv, shape):
106
Compare an inventory to a list of expected names.
108
Fail if they are not precisely equal.
111
shape = list(shape) # copy
112
for path, ie in inv.entries():
113
name = path.replace('\\', '/')
121
self.fail("expected paths not found in inventory: %r" % shape)
123
self.fail("unexpected paths found in inventory: %r" % extras)
125
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
126
a_callable=None, *args, **kwargs):
127
"""Call callable with redirected std io pipes.
129
Returns the return code."""
130
from StringIO import StringIO
131
if not callable(a_callable):
132
raise ValueError("a_callable must be callable.")
136
stdout = self._log_file
138
stderr = self._log_file
139
real_stdin = sys.stdin
140
real_stdout = sys.stdout
141
real_stderr = sys.stderr
147
result = a_callable(*args, **kwargs)
149
sys.stdout = real_stdout
150
sys.stderr = real_stderr
151
sys.stdin = real_stdin
155
BzrTestBase = TestCase
158
class TestCaseInTempDir(TestCase):
159
"""Derived class that runs a test within a temporary directory.
161
This is useful for tests that need to create a branch, etc.
163
The directory is created in a slightly complex way: for each
164
Python invocation, a new temporary top-level directory is created.
165
All test cases create their own directory within that. If the
166
tests complete successfully, the directory is removed.
168
InTempDir is an old alias for FunctionalTestCase.
173
OVERRIDE_PYTHON = 'python'
175
def check_file_contents(self, filename, expect):
176
self.log("check contents of file %s" % filename)
177
contents = file(filename, 'r').read()
178
if contents != expect:
179
self.log("expected: %r" % expect)
180
self.log("actually: %r" % contents)
181
self.fail("contents of %s not as expected")
183
def _make_test_root(self):
188
if TestCaseInTempDir.TEST_ROOT is not None:
190
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
191
tempfile.mkdtemp(suffix='.tmp',
192
prefix=self._TEST_NAME + '-',
195
# make a fake bzr directory there to prevent any tests propagating
196
# up onto the source directory's real branch
197
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
200
super(TestCaseInTempDir, self).setUp()
202
self._make_test_root()
203
self._currentdir = os.getcwdu()
204
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
205
os.mkdir(self.test_dir)
206
os.chdir(self.test_dir)
210
os.chdir(self._currentdir)
211
super(TestCaseInTempDir, self).tearDown()
213
def _formcmd(self, cmd):
214
if isinstance(cmd, basestring):
217
cmd[0] = self.BZRPATH
218
if self.OVERRIDE_PYTHON:
219
cmd.insert(0, self.OVERRIDE_PYTHON)
220
self.log('$ %r' % cmd)
223
def runcmd(self, cmd, retcode=0):
224
"""Run one command and check the return code.
226
Returns a tuple of (stdout,stderr) strings.
228
If a single string is based, it is split into words.
229
For commands that are not simple space-separated words, please
230
pass a list instead."""
233
from subprocess import call
234
except ImportError, e:
237
cmd = self._formcmd(cmd)
238
self.log('$ ' + ' '.join(cmd))
239
actual_retcode = call(cmd, stdout=self._log_file, stderr=self._log_file)
240
if retcode != actual_retcode:
241
raise CommandFailed("test failed: %r returned %d, expected %d"
242
% (cmd, actual_retcode, retcode))
244
def backtick(self, cmd, retcode=0):
245
"""Run a command and return its output"""
248
from subprocess import Popen, PIPE
249
except ImportError, e:
253
cmd = self._formcmd(cmd)
254
child = Popen(cmd, stdout=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.testinv',
318
'bzrlib.selftest.testfetch',
319
'bzrlib.selftest.versioning',
320
'bzrlib.selftest.whitebox',
321
'bzrlib.selftest.testmerge3',
322
'bzrlib.selftest.testhashcache',
323
'bzrlib.selftest.teststatus',
324
'bzrlib.selftest.testlog',
325
'bzrlib.selftest.blackbox',
326
'bzrlib.selftest.testrevisionnamespaces',
327
'bzrlib.selftest.testbranch',
328
'bzrlib.selftest.testrevision',
329
'bzrlib.selftest.test_merge_core',
330
'bzrlib.selftest.test_smart_add',
331
'bzrlib.selftest.testdiff',
335
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
336
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
337
if m not in MODULES_TO_DOCTEST:
338
MODULES_TO_DOCTEST.append(m)
340
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
341
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
344
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
345
for m in MODULES_TO_TEST:
346
suite.addTest(TestLoader().loadTestsFromModule(m))
347
for m in (MODULES_TO_DOCTEST):
348
suite.addTest(DocTestSuite(m))
349
for p in bzrlib.plugin.all_plugins:
350
if hasattr(p, 'test_suite'):
351
suite.addTest(p.test_suite())