~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

Exclude more files from dumb-rsync upload

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import unittest
28
28
import time
29
29
 
 
30
from logging import debug, warning, error
 
31
 
30
32
import bzrlib.commands
31
33
import bzrlib.trace
32
 
import bzrlib.fetch
33
34
import bzrlib.osutils as osutils
34
35
from bzrlib.selftest import TestUtil
35
36
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
36
37
from bzrlib.selftest.treeshape import build_tree_contents
 
38
from bzrlib.errors import BzrError
37
39
 
38
40
MODULES_TO_TEST = []
39
41
MODULES_TO_DOCTEST = []
40
42
 
41
 
from logging import debug, warning, error
42
 
 
43
43
 
44
44
 
45
45
class EarlyStoppingTestResultAdapter(object):
159
159
 
160
160
    Error and debug log messages are redirected from their usual
161
161
    location into a temporary file, the contents of which can be
162
 
    retrieved by _get_log().
 
162
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
163
    so that it can also capture file IO.  When the test completes this file
 
164
    is read into memory and removed from disk.
163
165
       
164
166
    There are also convenience functions to invoke bzr's command-line
165
 
    routine, and to build and check bzr trees."""
 
167
    routine, and to build and check bzr trees.
 
168
   
 
169
    In addition to the usual method of overriding tearDown(), this class also
 
170
    allows subclasses to register functions into the _cleanups list, which is
 
171
    run in order as the object is torn down.  It's less likely this will be
 
172
    accidentally overlooked.
 
173
    """
166
174
 
167
175
    BZRPATH = 'bzr'
168
176
    _log_file_name = None
 
177
    _log_contents = ''
169
178
 
170
179
    def setUp(self):
171
180
        unittest.TestCase.setUp(self)
172
 
        self.oldenv = os.environ.get('HOME', None)
173
 
        os.environ['HOME'] = os.getcwd()
174
 
        self.bzr_email = os.environ.get('BZREMAIL')
175
 
        if self.bzr_email is not None:
176
 
            del os.environ['BZREMAIL']
177
 
        self.email = os.environ.get('EMAIL')
178
 
        if self.email is not None:
179
 
            del os.environ['EMAIL']
 
181
        self._cleanups = []
 
182
        self._cleanEnvironment()
180
183
        bzrlib.trace.disable_default_logging()
181
 
        self._enable_file_logging()
 
184
        self._startLogFile()
182
185
 
183
186
    def _ndiff_strings(self, a, b):
184
187
        """Return ndiff between two strings containing lines.
213
216
            raise AssertionError('pattern "%s" not found in "%s"'
214
217
                    % (needle_re, haystack))
215
218
 
216
 
    def _enable_file_logging(self):
 
219
    def _startLogFile(self):
 
220
        """Send bzr and test log messages to a temporary file.
 
221
 
 
222
        The file is removed as the test is torn down.
 
223
        """
217
224
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
218
 
 
219
225
        self._log_file = os.fdopen(fileno, 'w+')
220
 
 
221
 
        hdlr = logging.StreamHandler(self._log_file)
222
 
        hdlr.setLevel(logging.DEBUG)
223
 
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
224
 
        logging.getLogger('').addHandler(hdlr)
225
 
        logging.getLogger('').setLevel(logging.DEBUG)
226
 
        self._log_hdlr = hdlr
 
226
        bzrlib.trace.enable_test_log(self._log_file)
227
227
        debug('opened log file %s', name)
228
 
        
229
228
        self._log_file_name = name
230
 
 
231
 
    def tearDown(self):
232
 
        os.environ['HOME'] = self.oldenv
233
 
        if os.environ.get('BZREMAIL') is not None:
234
 
            del os.environ['BZREMAIL']
235
 
        if self.bzr_email is not None:
236
 
            os.environ['BZREMAIL'] = self.bzr_email
237
 
        if os.environ.get('EMAIL') is not None:
238
 
            del os.environ['EMAIL']
239
 
        if self.email is not None:
240
 
            os.environ['EMAIL'] = self.email
241
 
        logging.getLogger('').removeHandler(self._log_hdlr)
242
 
        bzrlib.trace.enable_default_logging()
243
 
        logging.debug('%s teardown', self.id())
 
229
        self.addCleanup(self._finishLogFile)
 
230
 
 
231
    def _finishLogFile(self):
 
232
        """Finished with the log file.
 
233
 
 
234
        Read contents into memory, close, and delete.
 
235
        """
 
236
        bzrlib.trace.disable_test_log()
 
237
        self._log_file.seek(0)
 
238
        self._log_contents = self._log_file.read()
244
239
        self._log_file.close()
 
240
        os.remove(self._log_file_name)
 
241
        self._log_file = self._log_file_name = None
 
242
 
 
243
    def addCleanup(self, callable):
 
244
        """Arrange to run a callable when this case is torn down.
 
245
 
 
246
        Callables are run in the reverse of the order they are registered, 
 
247
        ie last-in first-out.
 
248
        """
 
249
        if callable in self._cleanups:
 
250
            raise ValueError("cleanup function %r already registered on %s" 
 
251
                    % (callable, self))
 
252
        self._cleanups.append(callable)
 
253
 
 
254
    def _cleanEnvironment(self):
 
255
        new_env = {
 
256
            'HOME': os.getcwd(),
 
257
            'APPDATA': os.getcwd(),
 
258
            'BZREMAIL': None,
 
259
            'EMAIL': None,
 
260
        }
 
261
        self.__old_env = {}
 
262
        self.addCleanup(self._restoreEnvironment)
 
263
        for name, value in new_env.iteritems():
 
264
            self._captureVar(name, value)
 
265
 
 
266
 
 
267
    def _captureVar(self, name, newvalue):
 
268
        """Set an environment variable, preparing it to be reset when finished."""
 
269
        self.__old_env[name] = os.environ.get(name, None)
 
270
        if newvalue is None:
 
271
            if name in os.environ:
 
272
                del os.environ[name]
 
273
        else:
 
274
            os.environ[name] = newvalue
 
275
 
 
276
    @staticmethod
 
277
    def _restoreVar(name, value):
 
278
        if value is None:
 
279
            if name in os.environ:
 
280
                del os.environ[name]
 
281
        else:
 
282
            os.environ[name] = value
 
283
 
 
284
    def _restoreEnvironment(self):
 
285
        for name, value in self.__old_env.iteritems():
 
286
            self._restoreVar(name, value)
 
287
 
 
288
    def tearDown(self):
 
289
        self._runCleanups()
245
290
        unittest.TestCase.tearDown(self)
246
291
 
 
292
    def _runCleanups(self):
 
293
        """Run registered cleanup functions. 
 
294
 
 
295
        This should only be called from TestCase.tearDown.
 
296
        """
 
297
        for callable in reversed(self._cleanups):
 
298
            callable()
 
299
 
247
300
    def log(self, *args):
248
301
        logging.debug(*args)
249
302
 
252
305
        if self._log_file_name:
253
306
            return open(self._log_file_name).read()
254
307
        else:
255
 
            return ''
 
308
            return self._log_contents
256
309
 
257
 
    def capture(self, cmd):
 
310
    def capture(self, cmd, retcode=0):
258
311
        """Shortcut that splits cmd into words, runs, and returns stdout"""
259
 
        return self.run_bzr_captured(cmd.split())[0]
 
312
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
260
313
 
261
314
    def run_bzr_captured(self, argv, retcode=0):
262
 
        """Invoke bzr and return (result, stdout, stderr).
 
315
        """Invoke bzr and return (stdout, stderr).
263
316
 
264
317
        Useful for code that wants to check the contents of the
265
318
        output, the way error messages are presented, etc.
401
454
            return
402
455
        i = 0
403
456
        while True:
404
 
            root = 'test%04d.tmp' % i
 
457
            root = u'test%04d.tmp' % i
405
458
            try:
406
459
                os.mkdir(root)
407
460
            except OSError, e:
418
471
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
419
472
 
420
473
    def setUp(self):
 
474
        super(TestCaseInTempDir, self).setUp()
421
475
        self._make_test_root()
422
 
        self._currentdir = os.getcwdu()
 
476
        _currentdir = os.getcwdu()
423
477
        short_id = self.id().replace('bzrlib.selftest.', '') \
424
478
                   .replace('__main__.', '')
425
479
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
426
480
        os.mkdir(self.test_dir)
427
481
        os.chdir(self.test_dir)
428
 
        super(TestCaseInTempDir, self).setUp()
 
482
        os.environ['HOME'] = self.test_dir
 
483
        def _leaveDirectory():
 
484
            os.chdir(_currentdir)
 
485
        self.addCleanup(_leaveDirectory)
429
486
        
430
 
    def tearDown(self):
431
 
        os.chdir(self._currentdir)
432
 
        super(TestCaseInTempDir, self).tearDown()
433
 
 
434
 
    def build_tree(self, shape):
 
487
    def build_tree(self, shape, line_endings='native'):
435
488
        """Build a test tree according to a pattern.
436
489
 
437
490
        shape is a sequence of file specifications.  If the final
438
491
        character is '/', a directory is created.
439
492
 
440
493
        This doesn't add anything to a branch.
 
494
        :param line_endings: Either 'binary' or 'native'
 
495
                             in binary mode, exact contents are written
 
496
                             in native mode, the line endings match the
 
497
                             default platform endings.
441
498
        """
442
499
        # XXX: It's OK to just create them using forward slashes on windows?
443
500
        for name in shape:
444
 
            assert isinstance(name, basestring)
 
501
            self.assert_(isinstance(name, basestring))
445
502
            if name[-1] == '/':
446
503
                os.mkdir(name[:-1])
447
504
            else:
448
 
                f = file(name, 'wt')
 
505
                if line_endings == 'binary':
 
506
                    f = file(name, 'wb')
 
507
                elif line_endings == 'native':
 
508
                    f = file(name, 'wt')
 
509
                else:
 
510
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
449
511
                print >>f, "contents of", name
450
512
                f.close()
451
513
 
465
527
class MetaTestLog(TestCase):
466
528
    def test_logging(self):
467
529
        """Test logs are captured when a test fails."""
468
 
        logging.info('an info message')
469
 
        warning('something looks dodgy...')
470
 
        logging.debug('hello, test is running')
471
 
        ## assert 0
 
530
        self.log('a test message')
 
531
        self.assertContainsRe(self._get_log(), 'a test message\n')
472
532
 
473
533
 
474
534
def filter_suite_by_re(suite, pattern):
481
541
 
482
542
 
483
543
def run_suite(suite, name='test', verbose=False, pattern=".*",
484
 
              stop_on_failure=False):
 
544
              stop_on_failure=False, keep_output=False):
485
545
    TestCaseInTempDir._TEST_NAME = name
486
546
    if verbose:
487
547
        verbosity = 2
497
557
    # This is still a little bogus, 
498
558
    # but only a little. Folk not using our testrunner will
499
559
    # have to delete their temp directories themselves.
500
 
    if result.wasSuccessful():
 
560
    if result.wasSuccessful() or not keep_output:
501
561
        if TestCaseInTempDir.TEST_ROOT is not None:
502
562
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
503
563
    else:
505
565
    return result.wasSuccessful()
506
566
 
507
567
 
508
 
def selftest(verbose=False, pattern=".*", stop_on_failure=True):
 
568
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
569
             keep_output=False):
509
570
    """Run the whole test suite under the enhanced runner"""
510
571
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
511
 
                     stop_on_failure=stop_on_failure)
 
572
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
512
573
 
513
574
 
514
575
def test_suite():
519
580
 
520
581
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
521
582
 
 
583
    # FIXME: If these fail to load, e.g. because of a syntax error, the
 
584
    # exception is hidden by unittest.  Sucks.  Should either fix that or
 
585
    # perhaps import them and pass them to unittest as modules.
522
586
    testmod_names = \
523
587
                  ['bzrlib.selftest.MetaTestLog',
 
588
                   'bzrlib.selftest.testapi',
524
589
                   'bzrlib.selftest.testgpg',
525
590
                   'bzrlib.selftest.testidentitymap',
526
591
                   'bzrlib.selftest.testinv',
553
618
                   'bzrlib.selftest.testsampler',
554
619
                   'bzrlib.selftest.testtransactions',
555
620
                   'bzrlib.selftest.testtransport',
 
621
                   'bzrlib.selftest.testsftp',
556
622
                   'bzrlib.selftest.testgraph',
557
623
                   'bzrlib.selftest.testworkingtree',
558
624
                   'bzrlib.selftest.test_upgrade',
563
629
                   'bzrlib.selftest.testoptions',
564
630
                   'bzrlib.selftest.testhttp',
565
631
                   'bzrlib.selftest.testnonascii',
 
632
                   'bzrlib.selftest.testreweave',
 
633
                   'bzrlib.selftest.testtsort',
 
634
                   'bzrlib.selftest.testtrace',
566
635
                   ]
567
636
 
568
637
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,