~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 10:57:01 UTC
  • mfrom: (1092.1.41)
  • Revision ID: mbp@sourcefrog.net-20050829105701-7aaa81ecf1bfee05
- merge in merge improvements and additional tests 
  from aaron and lifeless

robertc@robertcollins.net-20050825131100-85772edabc817481

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