/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-09-01 07:28:14 UTC
  • Revision ID: mbp@sourcefrog.net-20050901072814-b2685355951a6a6a
- add test that 'bzr add' reports the files as they're added

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
17
 
 
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
 
36
 
 
37
class CommandFailed(Exception):
 
38
    pass
 
39
 
 
40
class TestCase(unittest.TestCase):
 
41
    """Base class for bzr unit tests.
 
42
    
 
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
 
 
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
    def check_inventory_shape(self, inv, shape):
 
109
        """
 
110
        Compare an inventory to a list of expected names.
 
111
 
 
112
        Fail if they are not precisely equal.
 
113
        """
 
114
        extras = []
 
115
        shape = list(shape)             # copy
 
116
        for path, ie in inv.entries():
 
117
            name = path.replace('\\', '/')
 
118
            if ie.kind == 'dir':
 
119
                name = name + '/'
 
120
            if name in shape:
 
121
                shape.remove(name)
 
122
            else:
 
123
                extras.append(name)
 
124
        if shape:
 
125
            self.fail("expected paths not found in inventory: %r" % shape)
 
126
        if extras:
 
127
            self.fail("unexpected paths found in inventory: %r" % extras)
 
128
 
 
129
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
130
                         a_callable=None, *args, **kwargs):
 
131
        """Call callable with redirected std io pipes.
 
132
 
 
133
        Returns the return code."""
 
134
        from StringIO import StringIO
 
135
        if not callable(a_callable):
 
136
            raise ValueError("a_callable must be callable.")
 
137
        if stdin is None:
 
138
            stdin = StringIO("")
 
139
        if stdout is None:
 
140
            stdout = self._log_file
 
141
        if stderr is None:
 
142
            stderr = self._log_file
 
143
        real_stdin = sys.stdin
 
144
        real_stdout = sys.stdout
 
145
        real_stderr = sys.stderr
 
146
        try:
 
147
            sys.stdout = stdout
 
148
            sys.stderr = stderr
 
149
            sys.stdin = stdin
 
150
            return a_callable(*args, **kwargs)
 
151
        finally:
 
152
            sys.stdout = real_stdout
 
153
            sys.stderr = real_stderr
 
154
            sys.stdin = real_stdin
 
155
 
 
156
 
 
157
BzrTestBase = TestCase
 
158
 
 
159
     
 
160
class TestCaseInTempDir(TestCase):
 
161
    """Derived class that runs a test within a temporary directory.
 
162
 
 
163
    This is useful for tests that need to create a branch, etc.
 
164
 
 
165
    The directory is created in a slightly complex way: for each
 
166
    Python invocation, a new temporary top-level directory is created.
 
167
    All test cases create their own directory within that.  If the
 
168
    tests complete successfully, the directory is removed.
 
169
 
 
170
    InTempDir is an old alias for FunctionalTestCase.
 
171
    """
 
172
 
 
173
    TEST_ROOT = None
 
174
    _TEST_NAME = 'test'
 
175
    OVERRIDE_PYTHON = 'python'
 
176
 
 
177
    def check_file_contents(self, filename, expect):
 
178
        self.log("check contents of file %s" % filename)
 
179
        contents = file(filename, 'r').read()
 
180
        if contents != expect:
 
181
            self.log("expected: %r" % expect)
 
182
            self.log("actually: %r" % contents)
 
183
            self.fail("contents of %s not as expected")
 
184
 
 
185
    def _make_test_root(self):
 
186
        import os
 
187
        import shutil
 
188
        import tempfile
 
189
        
 
190
        if TestCaseInTempDir.TEST_ROOT is not None:
 
191
            return
 
192
        TestCaseInTempDir.TEST_ROOT = os.path.abspath(
 
193
                                 tempfile.mkdtemp(suffix='.tmp',
 
194
                                                  prefix=self._TEST_NAME + '-',
 
195
                                                  dir=os.curdir))
 
196
    
 
197
        # make a fake bzr directory there to prevent any tests propagating
 
198
        # up onto the source directory's real branch
 
199
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
200
 
 
201
    def setUp(self):
 
202
        super(TestCaseInTempDir, self).setUp()
 
203
        import os
 
204
        self._make_test_root()
 
205
        self._currentdir = os.getcwdu()
 
206
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
207
        os.mkdir(self.test_dir)
 
208
        os.chdir(self.test_dir)
 
209
        
 
210
    def tearDown(self):
 
211
        import os
 
212
        os.chdir(self._currentdir)
 
213
        super(TestCaseInTempDir, self).tearDown()
 
214
 
 
215
    def _formcmd(self, cmd):
 
216
        if isinstance(cmd, basestring):
 
217
            cmd = cmd.split()
 
218
        if cmd[0] == 'bzr':
 
219
            cmd[0] = self.BZRPATH
 
220
            if self.OVERRIDE_PYTHON:
 
221
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
222
        self.log('$ %r' % cmd)
 
223
        return cmd
 
224
 
 
225
    def runcmd(self, cmd, retcode=0):
 
226
        """Run one command and check the return code.
 
227
 
 
228
        Returns a tuple of (stdout,stderr) strings.
 
229
 
 
230
        If a single string is based, it is split into words.
 
231
        For commands that are not simple space-separated words, please
 
232
        pass a list instead."""
 
233
        cmd = self._formcmd(cmd)
 
234
        self.log('$ ' + ' '.join(cmd))
 
235
        actual_retcode = subprocess.call(cmd, stdout=self._log_file,
 
236
                                         stderr=self._log_file)
 
237
        if retcode != actual_retcode:
 
238
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
239
                                % (cmd, actual_retcode, retcode))
 
240
 
 
241
    def backtick(self, cmd, retcode=0):
 
242
        """Run a command and return its output"""
 
243
        cmd = self._formcmd(cmd)
 
244
        child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
 
245
        outd, errd = child.communicate()
 
246
        self.log(outd)
 
247
        actual_retcode = child.wait()
 
248
 
 
249
        outd = outd.replace('\r', '')
 
250
 
 
251
        if retcode != actual_retcode:
 
252
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
253
                                % (cmd, actual_retcode, retcode))
 
254
 
 
255
        return outd
 
256
 
 
257
 
 
258
 
 
259
    def build_tree(self, shape):
 
260
        """Build a test tree according to a pattern.
 
261
 
 
262
        shape is a sequence of file specifications.  If the final
 
263
        character is '/', a directory is created.
 
264
 
 
265
        This doesn't add anything to a branch.
 
266
        """
 
267
        # XXX: It's OK to just create them using forward slashes on windows?
 
268
        import os
 
269
        for name in shape:
 
270
            assert isinstance(name, basestring)
 
271
            if name[-1] == '/':
 
272
                os.mkdir(name[:-1])
 
273
            else:
 
274
                f = file(name, 'wt')
 
275
                print >>f, "contents of", name
 
276
                f.close()
 
277
                
 
278
 
 
279
 
 
280
class MetaTestLog(TestCase):
 
281
    def test_logging(self):
 
282
        """Test logs are captured when a test fails."""
 
283
        logging.info('an info message')
 
284
        warning('something looks dodgy...')
 
285
        logging.debug('hello, test is running')
 
286
        ##assert 0
 
287
 
 
288
 
 
289
def selftest(verbose=False, pattern=".*"):
 
290
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
291
 
 
292
 
 
293
def test_suite():
 
294
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
295
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
 
296
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
297
    from doctest import DocTestSuite
 
298
    import os
 
299
    import shutil
 
300
    import time
 
301
    import sys
 
302
 
 
303
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
304
 
 
305
    testmod_names = \
 
306
                  ['bzrlib.selftest.MetaTestLog',
 
307
                   'bzrlib.selftest.test_parent',
 
308
                   'bzrlib.selftest.testinv',
 
309
                   'bzrlib.selftest.testfetch',
 
310
                   'bzrlib.selftest.versioning',
 
311
                   'bzrlib.selftest.whitebox',
 
312
                   'bzrlib.selftest.testmerge3',
 
313
                   'bzrlib.selftest.testhashcache',
 
314
                   'bzrlib.selftest.teststatus',
 
315
                   'bzrlib.selftest.testlog',
 
316
                   'bzrlib.selftest.blackbox',
 
317
                   'bzrlib.selftest.testrevisionnamespaces',
 
318
                   'bzrlib.selftest.testbranch',
 
319
                   'bzrlib.selftest.testrevision',
 
320
                   'bzrlib.selftest.test_merge_core',
 
321
                   'bzrlib.selftest.test_smart_add',
 
322
                   'bzrlib.selftest.testdiff',
 
323
                   'bzrlib.fetch'
 
324
                   ]
 
325
 
 
326
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
327
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
328
        if m not in MODULES_TO_DOCTEST:
 
329
            MODULES_TO_DOCTEST.append(m)
 
330
 
 
331
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
332
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
333
    print
 
334
    suite = TestSuite()
 
335
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
336
    for m in MODULES_TO_TEST:
 
337
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
338
    for m in (MODULES_TO_DOCTEST):
 
339
        suite.addTest(DocTestSuite(m))
 
340
    for p in bzrlib.plugin.all_plugins:
 
341
        if hasattr(p, 'test_suite'):
 
342
            suite.addTest(p.test_suite())
 
343
    return suite
 
344