15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from testsweet import TestCase, run_suite, InTempDir, FunctionalTestCase
25
from testsweet import run_suite
19
26
import bzrlib.commands
20
29
import bzrlib.fetch
21
import bzrlib.selftest.teststore
23
32
MODULES_TO_TEST = []
24
33
MODULES_TO_DOCTEST = []
27
class BzrTestBase(InTempDir):
28
"""bzr-specific test base class"""
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()
29
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.
30
103
retcode = kwargs.get('retcode', 0)
31
104
result = self.apply_redirected(None, None, None,
32
105
bzrlib.commands.run_bzr, args)
33
106
self.assertEquals(result, retcode)
109
def check_inventory_shape(self, inv, shape):
111
Compare an inventory to a list of expected names.
113
Fail if they are not precisely equal.
116
shape = list(shape) # copy
117
for path, ie in inv.entries():
118
name = path.replace('\\', '/')
126
self.fail("expected paths not found in inventory: %r" % shape)
128
self.fail("unexpected paths found in inventory: %r" % extras)
130
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
131
a_callable=None, *args, **kwargs):
132
"""Call callable with redirected std io pipes.
134
Returns the return code."""
135
from StringIO import StringIO
136
if not callable(a_callable):
137
raise ValueError("a_callable must be callable.")
141
stdout = self._log_file
143
stderr = self._log_file
144
real_stdin = sys.stdin
145
real_stdout = sys.stdout
146
real_stderr = sys.stderr
151
return a_callable(*args, **kwargs)
153
sys.stdout = real_stdout
154
sys.stderr = real_stderr
155
sys.stdin = real_stdin
158
BzrTestBase = TestCase
161
class TestCaseInTempDir(TestCase):
162
"""Derived class that runs a test within a temporary directory.
164
This is useful for tests that need to create a branch, etc.
166
The directory is created in a slightly complex way: for each
167
Python invocation, a new temporary top-level directory is created.
168
All test cases create their own directory within that. If the
169
tests complete successfully, the directory is removed.
171
InTempDir is an old alias for FunctionalTestCase.
176
OVERRIDE_PYTHON = 'python'
178
def check_file_contents(self, filename, expect):
179
self.log("check contents of file %s" % filename)
180
contents = file(filename, 'r').read()
181
if contents != expect:
182
self.log("expected: %r" % expect)
183
self.log("actually: %r" % contents)
184
self.fail("contents of %s not as expected")
186
def _make_test_root(self):
191
if TestCaseInTempDir.TEST_ROOT is not None:
193
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
194
tempfile.mkdtemp(suffix='.tmp',
195
prefix=self._TEST_NAME + '-',
198
# make a fake bzr directory there to prevent any tests propagating
199
# up onto the source directory's real branch
200
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
203
super(TestCaseInTempDir, self).setUp()
205
self._make_test_root()
206
self._currentdir = os.getcwdu()
207
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
208
os.mkdir(self.test_dir)
209
os.chdir(self.test_dir)
213
os.chdir(self._currentdir)
214
super(TestCaseInTempDir, self).tearDown()
216
def _formcmd(self, cmd):
217
if isinstance(cmd, basestring):
220
cmd[0] = self.BZRPATH
221
if self.OVERRIDE_PYTHON:
222
cmd.insert(0, self.OVERRIDE_PYTHON)
223
self.log('$ %r' % cmd)
226
def runcmd(self, cmd, retcode=0):
227
"""Run one command and check the return code.
229
Returns a tuple of (stdout,stderr) strings.
231
If a single string is based, it is split into words.
232
For commands that are not simple space-separated words, please
233
pass a list instead."""
234
cmd = self._formcmd(cmd)
235
self.log('$ ' + ' '.join(cmd))
236
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
237
stderr=self._log_file)
238
if retcode != actual_retcode:
239
raise CommandFailed("test failed: %r returned %d, expected %d"
240
% (cmd, actual_retcode, retcode))
242
def backtick(self, cmd, retcode=0):
243
"""Run a command and return its output"""
244
cmd = self._formcmd(cmd)
245
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
246
outd, errd = child.communicate()
248
actual_retcode = child.wait()
250
outd = outd.replace('\r', '')
252
if retcode != actual_retcode:
253
raise CommandFailed("test failed: %r returned %d, expected %d"
254
% (cmd, actual_retcode, retcode))
260
def build_tree(self, shape):
261
"""Build a test tree according to a pattern.
263
shape is a sequence of file specifications. If the final
264
character is '/', a directory is created.
266
This doesn't add anything to a branch.
268
# XXX: It's OK to just create them using forward slashes on windows?
271
assert isinstance(name, basestring)
276
print >>f, "contents of", name
281
class MetaTestLog(TestCase):
282
def test_logging(self):
283
"""Test logs are captured when a test fails."""
284
logging.info('an info message')
285
warning('something looks dodgy...')
286
logging.debug('hello, test is running')
36
290
def selftest(verbose=False, pattern=".*"):
37
291
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)