~bzr-pqm/bzr/bzr.dev

608 by Martin Pool
- Split selftests out into a new module and start changing them
1
# Copyright (C) 2005 by Canonical Ltd
2
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.
7
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.
12
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
16
609 by Martin Pool
- cleanup test code
17
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
18
import logging
19
import unittest
20
import tempfile
21
import os
1139 by Martin Pool
- merge in merge improvements and additional tests
22
import sys
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
23
24
from testsweet import run_suite
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
25
import bzrlib.commands
1139 by Martin Pool
- merge in merge improvements and additional tests
26
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
27
import bzrlib.trace
1092.1.18 by Robert Collins
merge from mpool
28
import bzrlib.fetch
719 by Martin Pool
- reorganize selftest code
29
855 by Martin Pool
- Patch from John to allow plugins to add their own tests.
30
MODULES_TO_TEST = []
31
MODULES_TO_DOCTEST = []
720 by Martin Pool
- start moving external tests into the testsuite framework
32
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
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
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
89
    def run_bzr(self, *args, **kwargs):
1119 by Martin Pool
doc
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.
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
98
        """
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
99
        retcode = kwargs.get('retcode', 0)
1092.1.16 by Robert Collins
provide a helper to redirect output as desired
100
        result = self.apply_redirected(None, None, None,
101
                                       bzrlib.commands.run_bzr, args)
102
        self.assertEquals(result, retcode)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
103
        
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
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
1139 by Martin Pool
- merge in merge improvements and additional tests
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
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
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
294
1092.1.20 by Robert Collins
import and use TestUtil to do regex based partial test runs
295
def selftest(verbose=False, pattern=".*"):
296
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
1092.1.17 by Robert Collins
remove TEST_CLASSES dead code and provide a bzrlib.test_suite() convenience method
297
298
299
def test_suite():
1092.1.20 by Robert Collins
import and use TestUtil to do regex based partial test runs
300
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
855 by Martin Pool
- Patch from John to allow plugins to add their own tests.
301
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
908 by Martin Pool
- merge john's plugins-have-test_suite.patch:
302
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
721 by Martin Pool
- framework for running external commands from unittest suite
303
    from doctest import DocTestSuite
304
    import os
305
    import shutil
306
    import time
745 by Martin Pool
- redirect stdout/stderr while running tests
307
    import sys
721 by Martin Pool
- framework for running external commands from unittest suite
308
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
309
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
310
311
    testmod_names = \
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
312
                  ['bzrlib.selftest.MetaTestLog',
313
                   'bzrlib.selftest.testinv',
314
                   'bzrlib.selftest.testfetch',
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
315
                   'bzrlib.selftest.versioning',
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
316
                   'bzrlib.selftest.whitebox',
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
317
                   'bzrlib.selftest.testmerge3',
318
                   'bzrlib.selftest.testhashcache',
319
                   'bzrlib.selftest.teststatus',
320
                   'bzrlib.selftest.testlog',
321
                   'bzrlib.selftest.blackbox',
322
                   'bzrlib.selftest.testrevisionnamespaces',
323
                   'bzrlib.selftest.testbranch',
324
                   'bzrlib.selftest.testrevision',
1092.1.23 by Robert Collins
move merge_core tests into the selftest package. Also reduce double-run of those tests
325
                   'bzrlib.selftest.test_merge_core',
1092.1.26 by Robert Collins
start writing star-topology test, realise we need smart-add change
326
                   'bzrlib.selftest.test_smart_add',
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
327
                   'bzrlib.selftest.testdiff',
1092.1.18 by Robert Collins
merge from mpool
328
                   'bzrlib.fetch'
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
329
                   ]
330
855 by Martin Pool
- Patch from John to allow plugins to add their own tests.
331
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
332
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
333
        if m not in MODULES_TO_DOCTEST:
334
            MODULES_TO_DOCTEST.append(m)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
335
1102 by Martin Pool
- merge test refactoring from robertc
336
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
337
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
744 by Martin Pool
- show nicer descriptions while running tests
338
    print
721 by Martin Pool
- framework for running external commands from unittest suite
339
    suite = TestSuite()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
340
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
855 by Martin Pool
- Patch from John to allow plugins to add their own tests.
341
    for m in MODULES_TO_TEST:
342
         suite.addTest(TestLoader().loadTestsFromModule(m))
343
    for m in (MODULES_TO_DOCTEST):
721 by Martin Pool
- framework for running external commands from unittest suite
344
        suite.addTest(DocTestSuite(m))
908 by Martin Pool
- merge john's plugins-have-test_suite.patch:
345
    for p in bzrlib.plugin.all_plugins:
346
        if hasattr(p, 'test_suite'):
347
            suite.addTest(p.test_suite())
1092.1.17 by Robert Collins
remove TEST_CLASSES dead code and provide a bzrlib.test_suite() convenience method
348
    return suite
764 by Martin Pool
- log messages from a particular test are printed if that test fails
349