~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-08-29 01:25:00 UTC
  • Revision ID: mbp@sourcefrog.net-20050829012500-36f2d20363be4a53
* move bzr-specific code from testsweet into bzrlib.selftest

* logging from within test suites is now done using python logging, so
  the regular .bzr.log is not cluttered and the results can be easily
  seen if the test fails

* don't capture stdout/stderr while running tests, instead let it leak
  through so that we can see places where the library is doing its own
  output and should be fixed.

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 testsweet import TestCase, run_suite, InTempDir
 
18
import logging
 
19
import unittest
 
20
import tempfile
 
21
import os
 
22
 
 
23
from testsweet import run_suite
 
24
 
19
25
import bzrlib.commands
 
26
import bzrlib.trace
20
27
 
21
28
MODULES_TO_TEST = []
22
29
MODULES_TO_DOCTEST = []
23
30
 
24
 
 
25
 
class BzrTestBase(InTempDir):
26
 
    """bzr-specific test base class"""
 
31
from logging import debug, warning, error
 
32
 
 
33
 
 
34
class TestCase(unittest.TestCase):
 
35
    """Base class for bzr unit tests.
 
36
    
 
37
    Tests that need access to disk resources should subclass 
 
38
    FunctionalTestCase not TestCase.
 
39
 
 
40
    Error and debug log messages are redirected from their usual
 
41
    location into a temporary file, the contents of which can be
 
42
    retrieved by _get_log().
 
43
       
 
44
    There are also convenience functions to invoke bzr's command-line
 
45
    routine, and to build and check bzr trees."""
 
46
 
 
47
    BZRPATH = 'bzr'
 
48
 
 
49
    def setUp(self):
 
50
        # this replaces the default testsweet.TestCase; we don't want logging changed
 
51
        unittest.TestCase.setUp(self)
 
52
        bzrlib.trace.disable_default_logging()
 
53
        self._enable_file_logging()
 
54
 
 
55
 
 
56
    def _enable_file_logging(self):
 
57
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
58
 
 
59
        self._log_file = os.fdopen(fileno, 'w+')
 
60
 
 
61
        hdlr = logging.StreamHandler(self._log_file)
 
62
        hdlr.setLevel(logging.DEBUG)
 
63
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
 
64
        logging.getLogger('').addHandler(hdlr)
 
65
        logging.getLogger('').setLevel(logging.DEBUG)
 
66
        self._log_hdlr = hdlr
 
67
        debug('opened log file %s', name)
 
68
        
 
69
        self._log_file_name = name
 
70
 
 
71
        
 
72
    def tearDown(self):
 
73
        logging.getLogger('').removeHandler(self._log_hdlr)
 
74
        bzrlib.trace.enable_default_logging()
 
75
        logging.debug('%s teardown', self.id())
 
76
        self._log_file.close()
 
77
        unittest.TestCase.tearDown(self)
 
78
 
 
79
 
 
80
    def log(self, *args):
 
81
        logging.debug(*args)
 
82
 
 
83
    def _get_log(self):
 
84
        """Return as a string the log for this test"""
 
85
        return open(self._log_file_name).read()
 
86
 
27
87
    def run_bzr(self, *args, **kwargs):
28
88
        """Invoke bzr, as if it were run from the command line.
29
89
 
33
93
 
34
94
        Much of the old code runs bzr by forking a new copy of Python, but
35
95
        that is slower, harder to debug, and generally not necessary.
36
 
        """"
 
96
        """
37
97
        retcode = kwargs.get('retcode', 0)
38
98
        self.assertEquals(bzrlib.commands.run_bzr(args), retcode)
39
99
        
 
100
    def check_inventory_shape(self, inv, shape):
 
101
        """
 
102
        Compare an inventory to a list of expected names.
 
103
 
 
104
        Fail if they are not precisely equal.
 
105
        """
 
106
        extras = []
 
107
        shape = list(shape)             # copy
 
108
        for path, ie in inv.entries():
 
109
            name = path.replace('\\', '/')
 
110
            if ie.kind == 'dir':
 
111
                name = name + '/'
 
112
            if name in shape:
 
113
                shape.remove(name)
 
114
            else:
 
115
                extras.append(name)
 
116
        if shape:
 
117
            self.fail("expected paths not found in inventory: %r" % shape)
 
118
        if extras:
 
119
            self.fail("unexpected paths found in inventory: %r" % extras)
 
120
 
 
121
BzrTestBase = TestCase
 
122
 
 
123
     
 
124
class FunctionalTestCase(TestCase):
 
125
    """Base class for tests that perform function testing - running bzr,
 
126
    using files on disk, and similar activities.
 
127
 
 
128
    InTempDir is an old alias for FunctionalTestCase.
 
129
    """
 
130
 
 
131
    TEST_ROOT = None
 
132
    _TEST_NAME = 'test'
 
133
    OVERRIDE_PYTHON = 'python'
 
134
 
 
135
    def check_file_contents(self, filename, expect):
 
136
        self.log("check contents of file %s" % filename)
 
137
        contents = file(filename, 'r').read()
 
138
        if contents != expect:
 
139
            self.log("expected: %r" % expect)
 
140
            self.log("actually: %r" % contents)
 
141
            self.fail("contents of %s not as expected")
 
142
 
 
143
    def _make_test_root(self):
 
144
        import os
 
145
        import shutil
 
146
        import tempfile
 
147
        
 
148
        if FunctionalTestCase.TEST_ROOT is not None:
 
149
            return
 
150
        FunctionalTestCase.TEST_ROOT = os.path.abspath(
 
151
                                 tempfile.mkdtemp(suffix='.tmp',
 
152
                                                  prefix=self._TEST_NAME + '-',
 
153
                                                  dir=os.curdir))
 
154
    
 
155
        # make a fake bzr directory there to prevent any tests propagating
 
156
        # up onto the source directory's real branch
 
157
        os.mkdir(os.path.join(FunctionalTestCase.TEST_ROOT, '.bzr'))
 
158
 
 
159
    def setUp(self):
 
160
        super(FunctionalTestCase, self).setUp()
 
161
        import os
 
162
        self._make_test_root()
 
163
        self._currentdir = os.getcwdu()
 
164
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
165
        os.mkdir(self.test_dir)
 
166
        os.chdir(self.test_dir)
 
167
        
 
168
    def tearDown(self):
 
169
        import os
 
170
        os.chdir(self._currentdir)
 
171
        super(FunctionalTestCase, self).tearDown()
 
172
 
 
173
    def _formcmd(self, cmd):
 
174
        if isinstance(cmd, basestring):
 
175
            cmd = cmd.split()
 
176
        if cmd[0] == 'bzr':
 
177
            cmd[0] = self.BZRPATH
 
178
            if self.OVERRIDE_PYTHON:
 
179
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
180
        self.log('$ %r' % cmd)
 
181
        return cmd
 
182
 
 
183
    def runcmd(self, cmd, retcode=0):
 
184
        """Run one command and check the return code.
 
185
 
 
186
        Returns a tuple of (stdout,stderr) strings.
 
187
 
 
188
        If a single string is based, it is split into words.
 
189
        For commands that are not simple space-separated words, please
 
190
        pass a list instead."""
 
191
        try:
 
192
            import shutil
 
193
            from subprocess import call
 
194
        except ImportError, e:
 
195
            _need_subprocess()
 
196
            raise
 
197
        cmd = self._formcmd(cmd)
 
198
        self.log('$ ' + ' '.join(cmd))
 
199
        actual_retcode = call(cmd, stdout=self._log_file, stderr=self._log_file)
 
200
        if retcode != actual_retcode:
 
201
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
202
                                % (cmd, actual_retcode, retcode))
 
203
 
 
204
    def backtick(self, cmd, retcode=0):
 
205
        """Run a command and return its output"""
 
206
        try:
 
207
            import shutil
 
208
            from subprocess import Popen, PIPE
 
209
        except ImportError, e:
 
210
            _need_subprocess()
 
211
            raise
 
212
 
 
213
        cmd = self._formcmd(cmd)
 
214
        child = Popen(cmd, stdout=PIPE, stderr=self._log_file)
 
215
        outd, errd = child.communicate()
 
216
        self.log(outd)
 
217
        actual_retcode = child.wait()
 
218
 
 
219
        outd = outd.replace('\r', '')
 
220
 
 
221
        if retcode != actual_retcode:
 
222
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
223
                                % (cmd, actual_retcode, retcode))
 
224
 
 
225
        return outd
 
226
 
 
227
 
 
228
 
 
229
    def build_tree(self, shape):
 
230
        """Build a test tree according to a pattern.
 
231
 
 
232
        shape is a sequence of file specifications.  If the final
 
233
        character is '/', a directory is created.
 
234
 
 
235
        This doesn't add anything to a branch.
 
236
        """
 
237
        # XXX: It's OK to just create them using forward slashes on windows?
 
238
        import os
 
239
        for name in shape:
 
240
            assert isinstance(name, basestring)
 
241
            if name[-1] == '/':
 
242
                os.mkdir(name[:-1])
 
243
            else:
 
244
                f = file(name, 'wt')
 
245
                print >>f, "contents of", name
 
246
                f.close()
 
247
                
 
248
 
 
249
 
 
250
class MetaTestLog(TestCase):
 
251
    def test_logging(self):
 
252
        """Test logs are captured when a test fails."""
 
253
        logging.info('an info message')
 
254
        warning('something looks dodgy...')
 
255
        logging.debug('hello, test is running')
 
256
        ##assert 0
 
257
 
 
258
 
 
259
InTempDir = FunctionalTestCase
 
260
 
40
261
 
41
262
def selftest(verbose=False):
42
263
    from unittest import TestLoader, TestSuite
47
268
    import shutil
48
269
    import time
49
270
    import sys
50
 
    import unittest
51
271
 
52
272
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
53
273
 
54
274
    testmod_names = \
55
 
                  ['bzrlib.selftest.whitebox',
 
275
                  ['bzrlib.selftest.MetaTestLog',
 
276
                   'bzrlib.selftest.testinv',
 
277
                   'bzrlib.selftest.testfetch',
56
278
                   'bzrlib.selftest.versioning',
57
 
                   'bzrlib.selftest.testfetch',
58
 
                   'bzrlib.selftest.testinv',
 
279
                   'bzrlib.selftest.whitebox',
59
280
                   'bzrlib.selftest.testmerge3',
60
281
                   'bzrlib.selftest.testhashcache',
61
282
                   'bzrlib.selftest.teststatus',