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
18
# TODO: Perhaps there should be an API to find out if bzr running under the
19
# test suite -- some plugins might want to avoid making intrusive changes if
20
# this is the case. However, we want behaviour under to test to diverge as
21
# little as possible, so this should be used rarely if it's added at all.
22
# (Suggestion from j-a-meinel, 2005-11-24)
24
from cStringIO import StringIO
39
import bzrlib.commands
40
from bzrlib.errors import (BzrError,
42
UninitializableFormat,
44
import bzrlib.inventory
45
import bzrlib.iterablefile
48
import bzrlib.osutils as osutils
52
from bzrlib.transport import urlescape
53
import bzrlib.transport
54
from bzrlib.transport.local import LocalRelpathServer
55
from bzrlib.transport.readonly import ReadonlyServer
56
from bzrlib.trace import mutter
57
from bzrlib.tests.TestUtil import TestLoader, TestSuite
58
from bzrlib.tests.treeshape import build_tree_contents
59
from bzrlib.workingtree import WorkingTree
61
default_transport = LocalRelpathServer
64
MODULES_TO_DOCTEST = [
75
def packages_to_test():
76
"""Return a list of packages to test.
78
The packages are not globally imported so that import failures are
79
triggered when running selftest, not when importing the command.
82
import bzrlib.tests.blackbox
83
import bzrlib.tests.branch_implementations
86
bzrlib.tests.blackbox,
87
bzrlib.tests.branch_implementations,
91
class _MyResult(unittest._TextTestResult):
94
Shows output in a different format, including displaying runtime for tests.
98
def _elapsedTime(self):
99
return "%5dms" % (1000 * (time.time() - self._start_time))
101
def startTest(self, test):
102
unittest.TestResult.startTest(self, test)
103
# In a short description, the important words are in
104
# the beginning, but in an id, the important words are
106
SHOW_DESCRIPTIONS = False
108
width = osutils.terminal_width()
109
name_width = width - 15
111
if SHOW_DESCRIPTIONS:
112
what = test.shortDescription()
114
if len(what) > name_width:
115
what = what[:name_width-3] + '...'
118
if what.startswith('bzrlib.tests.'):
120
if len(what) > name_width:
121
what = '...' + what[3-name_width:]
122
what = what.ljust(name_width)
123
self.stream.write(what)
125
self._start_time = time.time()
127
def addError(self, test, err):
128
if isinstance(err[1], TestSkipped):
129
return self.addSkipped(test, err)
130
unittest.TestResult.addError(self, test, err)
132
self.stream.writeln("ERROR %s" % self._elapsedTime())
134
self.stream.write('E')
139
def addFailure(self, test, err):
140
unittest.TestResult.addFailure(self, test, err)
142
self.stream.writeln(" FAIL %s" % self._elapsedTime())
144
self.stream.write('F')
149
def addSuccess(self, test):
151
self.stream.writeln(' OK %s' % self._elapsedTime())
153
self.stream.write('~')
155
unittest.TestResult.addSuccess(self, test)
157
def addSkipped(self, test, skip_excinfo):
159
print >>self.stream, ' SKIP %s' % self._elapsedTime()
160
print >>self.stream, ' %s' % skip_excinfo[1]
162
self.stream.write('S')
164
# seems best to treat this as success from point-of-view of unittest
165
# -- it actually does nothing so it barely matters :)
166
unittest.TestResult.addSuccess(self, test)
168
def printErrorList(self, flavour, errors):
169
for test, err in errors:
170
self.stream.writeln(self.separator1)
171
self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
172
if getattr(test, '_get_log', None) is not None:
174
print >>self.stream, \
175
('vvvv[log from %s]' % test.id()).ljust(78,'-')
176
print >>self.stream, test._get_log()
177
print >>self.stream, \
178
('^^^^[log from %s]' % test.id()).ljust(78,'-')
179
self.stream.writeln(self.separator2)
180
self.stream.writeln("%s" % err)
183
class TextTestRunner(unittest.TextTestRunner):
184
stop_on_failure = False
186
def _makeResult(self):
187
result = _MyResult(self.stream, self.descriptions, self.verbosity)
188
result.stop_early = self.stop_on_failure
192
def iter_suite_tests(suite):
193
"""Return all tests in a suite, recursing through nested suites"""
194
for item in suite._tests:
195
if isinstance(item, unittest.TestCase):
197
elif isinstance(item, unittest.TestSuite):
198
for r in iter_suite_tests(item):
201
raise Exception('unknown object %r inside test suite %r'
205
class TestSkipped(Exception):
206
"""Indicates that a test was intentionally skipped, rather than failing."""
210
class CommandFailed(Exception):
213
class TestCase(unittest.TestCase):
214
"""Base class for bzr unit tests.
216
Tests that need access to disk resources should subclass
217
TestCaseInTempDir not TestCase.
219
Error and debug log messages are redirected from their usual
220
location into a temporary file, the contents of which can be
221
retrieved by _get_log(). We use a real OS file, not an in-memory object,
222
so that it can also capture file IO. When the test completes this file
223
is read into memory and removed from disk.
225
There are also convenience functions to invoke bzr's command-line
226
routine, and to build and check bzr trees.
228
In addition to the usual method of overriding tearDown(), this class also
229
allows subclasses to register functions into the _cleanups list, which is
230
run in order as the object is torn down. It's less likely this will be
231
accidentally overlooked.
235
_log_file_name = None
238
def __init__(self, methodName='testMethod'):
239
super(TestCase, self).__init__(methodName)
243
unittest.TestCase.setUp(self)
244
self._cleanEnvironment()
245
bzrlib.trace.disable_default_logging()
248
def _ndiff_strings(self, a, b):
249
"""Return ndiff between two strings containing lines.
251
A trailing newline is added if missing to make the strings
253
if b and b[-1] != '\n':
255
if a and a[-1] != '\n':
257
difflines = difflib.ndiff(a.splitlines(True),
259
linejunk=lambda x: False,
260
charjunk=lambda x: False)
261
return ''.join(difflines)
263
def assertEqualDiff(self, a, b):
264
"""Assert two texts are equal, if not raise an exception.
266
This is intended for use with multi-line strings where it can
267
be hard to find the differences by eye.
269
# TODO: perhaps override assertEquals to call this for strings?
272
raise AssertionError("texts not equal:\n" +
273
self._ndiff_strings(a, b))
275
def assertStartsWith(self, s, prefix):
276
if not s.startswith(prefix):
277
raise AssertionError('string %r does not start with %r' % (s, prefix))
279
def assertEndsWith(self, s, suffix):
280
if not s.endswith(prefix):
281
raise AssertionError('string %r does not end with %r' % (s, suffix))
283
def assertContainsRe(self, haystack, needle_re):
284
"""Assert that a contains something matching a regular expression."""
285
if not re.search(needle_re, haystack):
286
raise AssertionError('pattern "%s" not found in "%s"'
287
% (needle_re, haystack))
289
def AssertSubset(self, sublist, superlist):
290
"""Assert that every entry in sublist is present in superlist."""
292
for entry in sublist:
293
if entry not in superlist:
294
missing.append(entry)
296
raise AssertionError("value(s) %r not present in container %r" %
297
(missing, superlist))
299
def assertIs(self, left, right):
300
if not (left is right):
301
raise AssertionError("%r is not %r." % (left, right))
303
def assertTransportMode(self, transport, path, mode):
304
"""Fail if a path does not have mode mode.
306
If modes are not supported on this platform, the test is skipped.
308
if sys.platform == 'win32':
310
path_stat = transport.stat(path)
311
actual_mode = stat.S_IMODE(path_stat.st_mode)
312
self.assertEqual(mode, actual_mode,
313
'mode of %r incorrect (%o != %o)' % (path, mode, actual_mode))
315
def _startLogFile(self):
316
"""Send bzr and test log messages to a temporary file.
318
The file is removed as the test is torn down.
320
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
321
encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
322
self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
323
self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
324
self._log_file_name = name
325
self.addCleanup(self._finishLogFile)
327
def _finishLogFile(self):
328
"""Finished with the log file.
330
Read contents into memory, close, and delete.
332
bzrlib.trace.disable_test_log(self._log_nonce)
333
self._log_file.seek(0)
334
self._log_contents = self._log_file.read()
335
self._log_file.close()
336
os.remove(self._log_file_name)
337
self._log_file = self._log_file_name = None
339
def addCleanup(self, callable):
340
"""Arrange to run a callable when this case is torn down.
342
Callables are run in the reverse of the order they are registered,
343
ie last-in first-out.
345
if callable in self._cleanups:
346
raise ValueError("cleanup function %r already registered on %s"
348
self._cleanups.append(callable)
350
def _cleanEnvironment(self):
353
'APPDATA': os.getcwd(),
358
self.addCleanup(self._restoreEnvironment)
359
for name, value in new_env.iteritems():
360
self._captureVar(name, value)
363
def _captureVar(self, name, newvalue):
364
"""Set an environment variable, preparing it to be reset when finished."""
365
self.__old_env[name] = os.environ.get(name, None)
367
if name in os.environ:
370
os.environ[name] = newvalue
373
def _restoreVar(name, value):
375
if name in os.environ:
378
os.environ[name] = value
380
def _restoreEnvironment(self):
381
for name, value in self.__old_env.iteritems():
382
self._restoreVar(name, value)
386
unittest.TestCase.tearDown(self)
388
def _runCleanups(self):
389
"""Run registered cleanup functions.
391
This should only be called from TestCase.tearDown.
393
# TODO: Perhaps this should keep running cleanups even if
395
for cleanup_fn in reversed(self._cleanups):
398
def log(self, *args):
402
"""Return as a string the log for this test"""
403
if self._log_file_name:
404
return open(self._log_file_name).read()
406
return self._log_contents
407
# TODO: Delete the log after it's been read in
409
def capture(self, cmd, retcode=0):
410
"""Shortcut that splits cmd into words, runs, and returns stdout"""
411
return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
413
def run_bzr_captured(self, argv, retcode=0):
414
"""Invoke bzr and return (stdout, stderr).
416
Useful for code that wants to check the contents of the
417
output, the way error messages are presented, etc.
419
This should be the main method for tests that want to exercise the
420
overall behavior of the bzr application (rather than a unit test
421
or a functional test of the library.)
423
Much of the old code runs bzr by forking a new copy of Python, but
424
that is slower, harder to debug, and generally not necessary.
426
This runs bzr through the interface that catches and reports
427
errors, and with logging set to something approximating the
428
default, so that error reporting can be checked.
430
argv -- arguments to invoke bzr
431
retcode -- expected return code, or None for don't-care.
435
self.log('run bzr: %s', ' '.join(argv))
436
# FIXME: don't call into logging here
437
handler = logging.StreamHandler(stderr)
438
handler.setFormatter(bzrlib.trace.QuietFormatter())
439
handler.setLevel(logging.INFO)
440
logger = logging.getLogger('')
441
logger.addHandler(handler)
443
result = self.apply_redirected(None, stdout, stderr,
444
bzrlib.commands.run_bzr_catch_errors,
447
logger.removeHandler(handler)
448
out = stdout.getvalue()
449
err = stderr.getvalue()
451
self.log('output:\n%s', out)
453
self.log('errors:\n%s', err)
454
if retcode is not None:
455
self.assertEquals(result, retcode)
458
def run_bzr(self, *args, **kwargs):
459
"""Invoke bzr, as if it were run from the command line.
461
This should be the main method for tests that want to exercise the
462
overall behavior of the bzr application (rather than a unit test
463
or a functional test of the library.)
465
This sends the stdout/stderr results into the test's log,
466
where it may be useful for debugging. See also run_captured.
468
retcode = kwargs.pop('retcode', 0)
469
return self.run_bzr_captured(args, retcode)
471
def check_inventory_shape(self, inv, shape):
472
"""Compare an inventory to a list of expected names.
474
Fail if they are not precisely equal.
477
shape = list(shape) # copy
478
for path, ie in inv.entries():
479
name = path.replace('\\', '/')
487
self.fail("expected paths not found in inventory: %r" % shape)
489
self.fail("unexpected paths found in inventory: %r" % extras)
491
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
492
a_callable=None, *args, **kwargs):
493
"""Call callable with redirected std io pipes.
495
Returns the return code."""
496
if not callable(a_callable):
497
raise ValueError("a_callable must be callable.")
501
if getattr(self, "_log_file", None) is not None:
502
stdout = self._log_file
506
if getattr(self, "_log_file", None is not None):
507
stderr = self._log_file
510
real_stdin = sys.stdin
511
real_stdout = sys.stdout
512
real_stderr = sys.stderr
517
return a_callable(*args, **kwargs)
519
sys.stdout = real_stdout
520
sys.stderr = real_stderr
521
sys.stdin = real_stdin
524
BzrTestBase = TestCase
527
class TestCaseInTempDir(TestCase):
528
"""Derived class that runs a test within a temporary directory.
530
This is useful for tests that need to create a branch, etc.
532
The directory is created in a slightly complex way: for each
533
Python invocation, a new temporary top-level directory is created.
534
All test cases create their own directory within that. If the
535
tests complete successfully, the directory is removed.
537
InTempDir is an old alias for FunctionalTestCase.
542
OVERRIDE_PYTHON = 'python'
544
def check_file_contents(self, filename, expect):
545
self.log("check contents of file %s" % filename)
546
contents = file(filename, 'r').read()
547
if contents != expect:
548
self.log("expected: %r" % expect)
549
self.log("actually: %r" % contents)
550
self.fail("contents of %s not as expected" % filename)
552
def _make_test_root(self):
553
if TestCaseInTempDir.TEST_ROOT is not None:
557
root = u'test%04d.tmp' % i
561
if e.errno == errno.EEXIST:
566
# successfully created
567
TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
569
# make a fake bzr directory there to prevent any tests propagating
570
# up onto the source directory's real branch
571
os.mkdir(osutils.pathjoin(TestCaseInTempDir.TEST_ROOT, '.bzr'))
574
super(TestCaseInTempDir, self).setUp()
575
self._make_test_root()
576
_currentdir = os.getcwdu()
577
short_id = self.id().replace('bzrlib.tests.', '') \
578
.replace('__main__.', '')
579
self.test_dir = osutils.pathjoin(self.TEST_ROOT, short_id)
580
os.mkdir(self.test_dir)
581
os.chdir(self.test_dir)
582
os.environ['HOME'] = self.test_dir
583
os.environ['APPDATA'] = self.test_dir
584
def _leaveDirectory():
585
os.chdir(_currentdir)
586
self.addCleanup(_leaveDirectory)
588
def build_tree(self, shape, line_endings='native', transport=None):
589
"""Build a test tree according to a pattern.
591
shape is a sequence of file specifications. If the final
592
character is '/', a directory is created.
594
This doesn't add anything to a branch.
595
:param line_endings: Either 'binary' or 'native'
596
in binary mode, exact contents are written
597
in native mode, the line endings match the
598
default platform endings.
600
:param transport: A transport to write to, for building trees on
601
VFS's. If the transport is readonly or None,
602
"." is opened automatically.
604
# XXX: It's OK to just create them using forward slashes on windows?
605
if transport is None or transport.is_readonly():
606
transport = bzrlib.transport.get_transport(".")
608
self.assert_(isinstance(name, basestring))
610
transport.mkdir(urlescape(name[:-1]))
612
if line_endings == 'binary':
614
elif line_endings == 'native':
617
raise BzrError('Invalid line ending request %r' % (line_endings,))
618
content = "contents of %s%s" % (name, end)
619
transport.put(urlescape(name), StringIO(content))
621
def build_tree_contents(self, shape):
622
build_tree_contents(shape)
624
def failUnlessExists(self, path):
625
"""Fail unless path, which may be abs or relative, exists."""
626
self.failUnless(osutils.lexists(path))
628
def failIfExists(self, path):
629
"""Fail if path, which may be abs or relative, exists."""
630
self.failIf(osutils.lexists(path))
632
def assertFileEqual(self, content, path):
633
"""Fail if path does not contain 'content'."""
634
self.failUnless(osutils.lexists(path))
635
self.assertEqualDiff(content, open(path, 'r').read())
638
class TestCaseWithTransport(TestCaseInTempDir):
639
"""A test case that provides get_url and get_readonly_url facilities.
641
These back onto two transport servers, one for readonly access and one for
644
If no explicit class is provided for readonly access, a
645
ReadonlyTransportDecorator is used instead which allows the use of non disk
646
based read write transports.
648
If an explicit class is provided for readonly access, that server and the
649
readwrite one must both define get_url() as resolving to os.getcwd().
652
def __init__(self, methodName='testMethod'):
653
super(TestCaseWithTransport, self).__init__(methodName)
654
self.__readonly_server = None
656
self.transport_server = default_transport
657
self.transport_readonly_server = None
659
def get_readonly_url(self, relpath=None):
660
"""Get a URL for the readonly transport.
662
This will either be backed by '.' or a decorator to the transport
663
used by self.get_url()
664
relpath provides for clients to get a path relative to the base url.
665
These should only be downwards relative, not upwards.
667
if self.__readonly_server is None:
668
if self.transport_readonly_server is None:
669
# readonly decorator requested
670
# bring up the server
672
self.__readonly_server = ReadonlyServer()
673
self.__readonly_server.setUp(self.__server)
675
self.__readonly_server = self.transport_readonly_server()
676
self.__readonly_server.setUp()
677
self.addCleanup(self.__readonly_server.tearDown)
678
base = self.__readonly_server.get_url()
679
if relpath is not None:
680
if not base.endswith('/'):
682
base = base + relpath
685
def get_url(self, relpath=None):
686
"""Get a URL for the readwrite transport.
688
This will either be backed by '.' or to an equivalent non-file based
690
relpath provides for clients to get a path relative to the base url.
691
These should only be downwards relative, not upwards.
693
if self.__server is None:
694
self.__server = self.transport_server()
695
self.__server.setUp()
696
self.addCleanup(self.__server.tearDown)
697
base = self.__server.get_url()
698
if relpath is not None and relpath != '.':
699
if not base.endswith('/'):
701
base = base + relpath
704
def make_branch(self, relpath):
705
"""Create a branch on the transport at relpath."""
707
url = self.get_url(relpath)
708
segments = relpath.split('/')
709
if segments and segments[-1] not in ('', '.'):
710
parent = self.get_url('/'.join(segments[:-1]))
711
t = bzrlib.transport.get_transport(parent)
713
t.mkdir(segments[-1])
716
return bzrlib.branch.Branch.create(url)
717
except UninitializableFormat:
718
raise TestSkipped("Format %s is not initializable.")
720
def make_branch_and_tree(self, relpath):
721
"""Create a branch on the transport and a tree locally.
725
b = self.make_branch(relpath)
726
return WorkingTree.create(b, relpath)
729
class ChrootedTestCase(TestCaseWithTransport):
730
"""A support class that provides readonly urls outside the local namespace.
732
This is done by checking if self.transport_server is a MemoryServer. if it
733
is then we are chrooted already, if it is not then an HttpServer is used
736
TODO RBC 20060127: make this an option to TestCaseWithTransport so it can
737
be used without needed to redo it when a different
742
super(ChrootedTestCase, self).setUp()
743
if not self.transport_server == bzrlib.transport.memory.MemoryServer:
744
self.transport_readonly_server = bzrlib.transport.http.HttpServer
747
def filter_suite_by_re(suite, pattern):
749
filter_re = re.compile(pattern)
750
for test in iter_suite_tests(suite):
751
if filter_re.search(test.id()):
756
def run_suite(suite, name='test', verbose=False, pattern=".*",
757
stop_on_failure=False, keep_output=False,
759
TestCaseInTempDir._TEST_NAME = name
764
runner = TextTestRunner(stream=sys.stdout,
767
runner.stop_on_failure=stop_on_failure
769
suite = filter_suite_by_re(suite, pattern)
770
result = runner.run(suite)
771
# This is still a little bogus,
772
# but only a little. Folk not using our testrunner will
773
# have to delete their temp directories themselves.
774
if result.wasSuccessful() or not keep_output:
775
if TestCaseInTempDir.TEST_ROOT is not None:
776
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
778
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
779
return result.wasSuccessful()
782
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
785
"""Run the whole test suite under the enhanced runner"""
786
global default_transport
787
if transport is None:
788
transport = default_transport
789
old_transport = default_transport
790
default_transport = transport
793
return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
794
stop_on_failure=stop_on_failure, keep_output=keep_output,
797
default_transport = old_transport
802
"""Build and return TestSuite for the whole program."""
803
from doctest import DocTestSuite
805
global MODULES_TO_DOCTEST
808
'bzrlib.tests.test_ancestry',
809
'bzrlib.tests.test_annotate',
810
'bzrlib.tests.test_api',
811
'bzrlib.tests.test_bad_files',
812
'bzrlib.tests.test_basis_inventory',
813
'bzrlib.tests.test_branch',
814
'bzrlib.tests.test_command',
815
'bzrlib.tests.test_commit',
816
'bzrlib.tests.test_commit_merge',
817
'bzrlib.tests.test_config',
818
'bzrlib.tests.test_conflicts',
819
'bzrlib.tests.test_decorators',
820
'bzrlib.tests.test_diff',
821
'bzrlib.tests.test_doc_generate',
822
'bzrlib.tests.test_fetch',
823
'bzrlib.tests.test_fileid_involved',
824
'bzrlib.tests.test_gpg',
825
'bzrlib.tests.test_graph',
826
'bzrlib.tests.test_hashcache',
827
'bzrlib.tests.test_http',
828
'bzrlib.tests.test_identitymap',
829
'bzrlib.tests.test_inv',
830
'bzrlib.tests.test_lockable_files',
831
'bzrlib.tests.test_log',
832
'bzrlib.tests.test_merge',
833
'bzrlib.tests.test_merge3',
834
'bzrlib.tests.test_merge_core',
835
'bzrlib.tests.test_missing',
836
'bzrlib.tests.test_msgeditor',
837
'bzrlib.tests.test_nonascii',
838
'bzrlib.tests.test_options',
839
'bzrlib.tests.test_osutils',
840
'bzrlib.tests.test_parent',
841
'bzrlib.tests.test_permissions',
842
'bzrlib.tests.test_plugins',
843
'bzrlib.tests.test_revision',
844
'bzrlib.tests.test_revisionnamespaces',
845
'bzrlib.tests.test_revprops',
846
'bzrlib.tests.test_reweave',
847
'bzrlib.tests.test_rio',
848
'bzrlib.tests.test_sampler',
849
'bzrlib.tests.test_selftest',
850
'bzrlib.tests.test_setup',
851
'bzrlib.tests.test_sftp_transport',
852
'bzrlib.tests.test_smart_add',
853
'bzrlib.tests.test_source',
854
'bzrlib.tests.test_store',
855
'bzrlib.tests.test_symbol_versioning',
856
'bzrlib.tests.test_testament',
857
'bzrlib.tests.test_trace',
858
'bzrlib.tests.test_transactions',
859
'bzrlib.tests.test_transport',
860
'bzrlib.tests.test_tsort',
861
'bzrlib.tests.test_ui',
862
'bzrlib.tests.test_uncommit',
863
'bzrlib.tests.test_upgrade',
864
'bzrlib.tests.test_weave',
865
'bzrlib.tests.test_whitebox',
866
'bzrlib.tests.test_workingtree',
867
'bzrlib.tests.test_xml',
869
test_transport_implementations = [
870
'bzrlib.tests.test_transport_implementations']
872
TestCase.BZRPATH = osutils.pathjoin(
873
osutils.realpath(osutils.dirname(bzrlib.__path__[0])), 'bzr')
874
print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
875
print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
878
# python2.4's TestLoader.loadTestsFromNames gives very poor
879
# errors if it fails to load a named module - no indication of what's
880
# actually wrong, just "no such module". We should probably override that
881
# class, but for the moment just load them ourselves. (mbp 20051202)
882
loader = TestLoader()
883
from bzrlib.transport import TransportTestProviderAdapter
884
adapter = TransportTestProviderAdapter()
885
adapt_modules(test_transport_implementations, adapter, loader, suite)
886
for mod_name in testmod_names:
887
mod = _load_module_by_name(mod_name)
888
suite.addTest(loader.loadTestsFromModule(mod))
889
for package in packages_to_test():
890
suite.addTest(package.test_suite())
891
for m in MODULES_TO_TEST:
892
suite.addTest(loader.loadTestsFromModule(m))
893
for m in (MODULES_TO_DOCTEST):
894
suite.addTest(DocTestSuite(m))
895
for name, plugin in bzrlib.plugin.all_plugins().items():
896
if getattr(plugin, 'test_suite', None) is not None:
897
suite.addTest(plugin.test_suite())
901
def adapt_modules(mods_list, adapter, loader, suite):
902
"""Adapt the modules in mods_list using adapter and add to suite."""
903
for mod_name in mods_list:
904
mod = _load_module_by_name(mod_name)
905
for test in iter_suite_tests(loader.loadTestsFromModule(mod)):
906
suite.addTests(adapter.adapt(test))
909
def _load_module_by_name(mod_name):
910
parts = mod_name.split('.')
911
module = __import__(mod_name)
913
# for historical reasons python returns the top-level module even though
914
# it loads the submodule; we need to walk down to get the one we want.
916
module = getattr(module, parts.pop(0))