~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-09-09 09:44:03 UTC
  • Revision ID: mbp@sourcefrog.net-20050909094403-ddad5896b0b12c68
- weave commit records per-file ancestors

 - commits of merges are currently forbidden

 - files that existed in the previous revision are recorded with that 
   parent

 'weave annotate' on woven files now gives the correct result!

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
from unittest import TestResult, TestCase
19
 
 
20
 
try:
21
 
    import shutil
22
 
    from subprocess import call, Popen, PIPE
23
 
except ImportError, e:
24
 
    sys.stderr.write("testbzr: sorry, this test suite requires the subprocess module\n"
25
 
                     "this is shipped with python2.4 and available separately for 2.3\n")
26
 
    raise
27
 
 
 
18
import logging
 
19
import unittest
 
20
import tempfile
 
21
import os
 
22
import sys
 
23
import subprocess
 
24
 
 
25
from testsweet import run_suite
 
26
import bzrlib.commands
 
27
 
 
28
import bzrlib.trace
 
29
import bzrlib.fetch
 
30
 
 
31
 
 
32
MODULES_TO_TEST = []
 
33
MODULES_TO_DOCTEST = []
 
34
 
 
35
from logging import debug, warning, error
28
36
 
29
37
class CommandFailed(Exception):
30
38
    pass
31
39
 
32
 
 
33
 
class TestBase(TestCase):
34
 
    """Base class for bzr test cases.
35
 
 
36
 
    Just defines some useful helper functions; doesn't actually test
37
 
    anything.
38
 
    """
 
40
class TestCase(unittest.TestCase):
 
41
    """Base class for bzr unit tests.
39
42
    
40
 
    # TODO: Special methods to invoke bzr, so that we can run it
41
 
    # through a specified Python intepreter
42
 
 
43
 
    OVERRIDE_PYTHON = None # to run with alternative python 'python'
 
43
    Tests that need access to disk resources should subclass 
 
44
    TestCaseInTempDir not TestCase.
 
45
 
 
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().
 
49
       
 
50
    There are also convenience functions to invoke bzr's command-line
 
51
    routine, and to build and check bzr trees."""
 
52
 
44
53
    BZRPATH = 'bzr'
 
54
 
 
55
    def setUp(self):
 
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()
 
60
 
 
61
 
 
62
    def _enable_file_logging(self):
 
63
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
64
 
 
65
        self._log_file = os.fdopen(fileno, 'w+')
 
66
 
 
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)
 
72
        self._log_hdlr = hdlr
 
73
        debug('opened log file %s', name)
 
74
        
 
75
        self._log_file_name = name
 
76
 
 
77
        
 
78
    def tearDown(self):
 
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)
 
84
 
 
85
 
 
86
    def log(self, *args):
 
87
        logging.debug(*args)
 
88
 
 
89
    def _get_log(self):
 
90
        """Return as a string the log for this test"""
 
91
        return open(self._log_file_name).read()
 
92
 
 
93
    def run_bzr(self, *args, **kwargs):
 
94
        """Invoke bzr, as if it were run from the command line.
 
95
 
 
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.)
 
99
 
 
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.
 
102
        """
 
103
        retcode = kwargs.get('retcode', 0)
 
104
        result = self.apply_redirected(None, None, None,
 
105
                                       bzrlib.commands.run_bzr, args)
 
106
        self.assertEquals(result, retcode)
 
107
        
 
108
        
 
109
    def check_inventory_shape(self, inv, shape):
 
110
        """
 
111
        Compare an inventory to a list of expected names.
 
112
 
 
113
        Fail if they are not precisely equal.
 
114
        """
 
115
        extras = []
 
116
        shape = list(shape)             # copy
 
117
        for path, ie in inv.entries():
 
118
            name = path.replace('\\', '/')
 
119
            if ie.kind == 'dir':
 
120
                name = name + '/'
 
121
            if name in shape:
 
122
                shape.remove(name)
 
123
            else:
 
124
                extras.append(name)
 
125
        if shape:
 
126
            self.fail("expected paths not found in inventory: %r" % shape)
 
127
        if extras:
 
128
            self.fail("unexpected paths found in inventory: %r" % extras)
 
129
 
 
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.
 
133
 
 
134
        Returns the return code."""
 
135
        from StringIO import StringIO
 
136
        if not callable(a_callable):
 
137
            raise ValueError("a_callable must be callable.")
 
138
        if stdin is None:
 
139
            stdin = StringIO("")
 
140
        if stdout is None:
 
141
            stdout = self._log_file
 
142
        if stderr is None:
 
143
            stderr = self._log_file
 
144
        real_stdin = sys.stdin
 
145
        real_stdout = sys.stdout
 
146
        real_stderr = sys.stderr
 
147
        try:
 
148
            sys.stdout = stdout
 
149
            sys.stderr = stderr
 
150
            sys.stdin = stdin
 
151
            return a_callable(*args, **kwargs)
 
152
        finally:
 
153
            sys.stdout = real_stdout
 
154
            sys.stderr = real_stderr
 
155
            sys.stdin = real_stdin
 
156
 
 
157
 
 
158
BzrTestBase = TestCase
 
159
 
 
160
     
 
161
class TestCaseInTempDir(TestCase):
 
162
    """Derived class that runs a test within a temporary directory.
 
163
 
 
164
    This is useful for tests that need to create a branch, etc.
 
165
 
 
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.
 
170
 
 
171
    InTempDir is an old alias for FunctionalTestCase.
 
172
    """
 
173
 
 
174
    TEST_ROOT = None
 
175
    _TEST_NAME = 'test'
 
176
    OVERRIDE_PYTHON = 'python'
 
177
 
 
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")
 
185
 
 
186
    def _make_test_root(self):
 
187
        import os
 
188
        import shutil
 
189
        import tempfile
 
190
        
 
191
        if TestCaseInTempDir.TEST_ROOT is not None:
 
192
            return
 
193
        TestCaseInTempDir.TEST_ROOT = os.path.abspath(
 
194
                                 tempfile.mkdtemp(suffix='.tmp',
 
195
                                                  prefix=self._TEST_NAME + '-',
 
196
                                                  dir=os.curdir))
45
197
    
46
 
 
47
 
    def formcmd(self, cmd):
 
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'))
 
201
 
 
202
    def setUp(self):
 
203
        super(TestCaseInTempDir, self).setUp()
 
204
        import os
 
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)
 
210
        
 
211
    def tearDown(self):
 
212
        import os
 
213
        os.chdir(self._currentdir)
 
214
        super(TestCaseInTempDir, self).tearDown()
 
215
 
 
216
    def _formcmd(self, cmd):
48
217
        if isinstance(cmd, basestring):
49
218
            cmd = cmd.split()
50
 
 
51
219
        if cmd[0] == 'bzr':
52
220
            cmd[0] = self.BZRPATH
53
221
            if self.OVERRIDE_PYTHON:
54
222
                cmd.insert(0, self.OVERRIDE_PYTHON)
55
 
 
56
223
        self.log('$ %r' % cmd)
57
 
 
58
224
        return cmd
59
225
 
60
 
 
61
226
    def runcmd(self, cmd, retcode=0):
62
227
        """Run one command and check the return code.
63
228
 
66
231
        If a single string is based, it is split into words.
67
232
        For commands that are not simple space-separated words, please
68
233
        pass a list instead."""
69
 
        cmd = self.formcmd(cmd)
70
 
 
 
234
        cmd = self._formcmd(cmd)
71
235
        self.log('$ ' + ' '.join(cmd))
72
 
        actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
73
 
 
 
236
        actual_retcode = subprocess.call(cmd, stdout=self._log_file,
 
237
                                         stderr=self._log_file)
74
238
        if retcode != actual_retcode:
75
239
            raise CommandFailed("test failed: %r returned %d, expected %d"
76
240
                                % (cmd, actual_retcode, retcode))
77
241
 
78
 
 
79
242
    def backtick(self, cmd, retcode=0):
80
 
        cmd = self.formcmd(cmd)
81
 
        child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
 
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)
82
246
        outd, errd = child.communicate()
83
247
        self.log(outd)
84
248
        actual_retcode = child.wait()
93
257
 
94
258
 
95
259
 
96
 
 
97
 
    def log(self, msg):
98
 
        """Log a message to a progress file"""
99
 
        print >>self.TEST_LOG, msg
100
 
               
101
 
 
102
 
class InTempDir(TestBase):
103
 
    """Base class for tests run in a temporary branch."""
104
 
    def setUp(self):
105
 
        import os
106
 
        self.test_dir = os.path.join(self.TEST_ROOT, self.__class__.__name__)
107
 
        os.mkdir(self.test_dir)
108
 
        os.chdir(self.test_dir)
109
 
        
110
 
    def tearDown(self):
111
 
        import os
112
 
        os.chdir(self.TEST_ROOT)
113
 
 
114
 
 
115
 
 
116
 
 
117
 
 
118
 
class _MyResult(TestResult):
119
 
    """
120
 
    Custom TestResult.
121
 
 
122
 
    No special behaviour for now.
123
 
    """
124
 
    def startTest(self, test):
125
 
        print str(test).ljust(60),
126
 
        TestResult.startTest(self, test)
127
 
 
128
 
    def stopTest(self, test):
129
 
        # print
130
 
        TestResult.stopTest(self, test)
131
 
 
132
 
 
133
 
    def addError(self, test, err):
134
 
        print 'ERROR'
135
 
        TestResult.addError(self, test, err)
136
 
 
137
 
    def addFailure(self, test, err):
138
 
        print 'FAILURE'
139
 
        TestResult.addFailure(self, test, err)
140
 
 
141
 
    def addSuccess(self, test):
142
 
        print 'OK'
143
 
        TestResult.addSuccess(self, test)
144
 
 
145
 
 
146
 
 
147
 
def selftest():
148
 
    from unittest import TestLoader, TestSuite
149
 
    import bzrlib
150
 
    import bzrlib.selftest.whitebox
151
 
    import bzrlib.selftest.blackbox
 
260
    def build_tree(self, shape):
 
261
        """Build a test tree according to a pattern.
 
262
 
 
263
        shape is a sequence of file specifications.  If the final
 
264
        character is '/', a directory is created.
 
265
 
 
266
        This doesn't add anything to a branch.
 
267
        """
 
268
        # XXX: It's OK to just create them using forward slashes on windows?
 
269
        import os
 
270
        for name in shape:
 
271
            assert isinstance(name, basestring)
 
272
            if name[-1] == '/':
 
273
                os.mkdir(name[:-1])
 
274
            else:
 
275
                f = file(name, 'wt')
 
276
                print >>f, "contents of", name
 
277
                f.close()
 
278
                
 
279
 
 
280
 
 
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')
 
287
        ##assert 0
 
288
 
 
289
 
 
290
def selftest(verbose=False, pattern=".*"):
 
291
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
292
 
 
293
 
 
294
def test_suite():
 
295
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
296
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
 
297
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
152
298
    from doctest import DocTestSuite
153
299
    import os
154
300
    import shutil
155
301
    import time
156
 
 
157
 
    _setup_test_log()
158
 
    _setup_test_dir()
159
 
 
 
302
    import sys
 
303
 
 
304
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
305
 
 
306
    testmod_names = \
 
307
                  ['bzrlib.selftest.MetaTestLog',
 
308
                   'bzrlib.selftest.test_parent',
 
309
                   'bzrlib.selftest.testinv',
 
310
                   'bzrlib.selftest.testfetch',
 
311
                   'bzrlib.selftest.versioning',
 
312
                   'bzrlib.selftest.whitebox',
 
313
                   'bzrlib.selftest.testmerge3',
 
314
                   'bzrlib.selftest.testhashcache',
 
315
                   'bzrlib.selftest.teststatus',
 
316
                   'bzrlib.selftest.testlog',
 
317
                   'bzrlib.selftest.blackbox',
 
318
                   'bzrlib.selftest.testrevisionnamespaces',
 
319
                   'bzrlib.selftest.testbranch',
 
320
                   'bzrlib.selftest.testrevision',
 
321
                   'bzrlib.selftest.test_merge_core',
 
322
                   'bzrlib.selftest.test_smart_add',
 
323
                   'bzrlib.selftest.testdiff',
 
324
                   'bzrlib.selftest.test_xml',
 
325
                   'bzrlib.fetch',
 
326
                   'bzrlib.selftest.teststore',
 
327
                   ]
 
328
 
 
329
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
330
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
331
        if m not in MODULES_TO_DOCTEST:
 
332
            MODULES_TO_DOCTEST.append(m)
 
333
 
 
334
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
335
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
336
    print
160
337
    suite = TestSuite()
161
 
    tl = TestLoader()
162
 
 
163
 
    for m in bzrlib.selftest.whitebox, :
164
 
        suite.addTest(tl.loadTestsFromModule(m))
165
 
 
166
 
    suite.addTest(bzrlib.selftest.blackbox.suite())
167
 
 
168
 
    for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
169
 
            bzrlib.commands:
 
338
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
339
    for m in MODULES_TO_TEST:
 
340
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
341
    for m in (MODULES_TO_DOCTEST):
170
342
        suite.addTest(DocTestSuite(m))
171
 
 
172
 
    result = _MyResult()
173
 
    suite.run(result)
174
 
 
175
 
    _show_results(result)
176
 
 
177
 
    return result.wasSuccessful()
178
 
 
179
 
 
180
 
def _setup_test_log():
181
 
    import time
182
 
    import os
183
 
    
184
 
    log_filename = os.path.abspath('testbzr.log')
185
 
    TestBase.TEST_LOG = open(log_filename, 'wt', buffering=1) # line buffered
186
 
 
187
 
    print >>TestBase.TEST_LOG, "bzr tests run at " + time.ctime()
188
 
    print '%-30s %s' % ('test log', log_filename)
189
 
 
190
 
 
191
 
def _setup_test_dir():
192
 
    import os
193
 
    import shutil
194
 
    
195
 
    TestBase.ORIG_DIR = os.getcwdu()
196
 
    TestBase.TEST_ROOT = os.path.abspath("testbzr.tmp")
197
 
 
198
 
    print '%-30s %s' % ('running tests in', TestBase.TEST_ROOT)
199
 
 
200
 
    if os.path.exists(TestBase.TEST_ROOT):
201
 
        shutil.rmtree(TestBase.TEST_ROOT)
202
 
    os.mkdir(TestBase.TEST_ROOT)
203
 
    os.chdir(TestBase.TEST_ROOT)
204
 
 
205
 
    # make a fake bzr directory there to prevent any tests propagating
206
 
    # up onto the source directory's real branch
207
 
    os.mkdir(os.path.join(TestBase.TEST_ROOT, '.bzr'))
208
 
 
209
 
    
210
 
 
211
 
def _show_results(result):
212
 
     for case, tb in result.errors:
213
 
         _show_test_failure('ERROR', case, tb)
214
 
 
215
 
     for case, tb in result.failures:
216
 
         _show_test_failure('FAILURE', case, tb)
217
 
         
218
 
     print
219
 
     print '%4d tests run' % result.testsRun
220
 
     print '%4d errors' % len(result.errors)
221
 
     print '%4d failures' % len(result.failures)
222
 
 
223
 
 
224
 
 
225
 
def _show_test_failure(kind, case, tb):
226
 
     print (kind + '! ').ljust(60, '-')
227
 
     print case
228
 
     print tb
229
 
     print ''.ljust(60, '-')
230
 
    
 
343
    for p in bzrlib.plugin.all_plugins:
 
344
        if hasattr(p, 'test_suite'):
 
345
            suite.addTest(p.test_suite())
 
346
    return suite
 
347