/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
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
"""Enhanced layer on unittest.
19
20
This does several things:
21
22
* nicer reporting as tests run
23
24
* test code can log messages into a buffer that is recorded to disk
25
  and displayed if the test fails
26
27
* tests can be run in a separate directory, which is useful for code that
28
  wants to create files
29
30
* utilities to run external commands and check their return code
31
  and/or output
32
1102 by Martin Pool
- merge test refactoring from robertc
33
Test cases should normally subclass testsweet.TestCase.  The test runner should
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
34
call runsuite().
35
36
This is meant to become independent of bzr, though that's not quite
37
true yet.
38
"""  
39
1102 by Martin Pool
- merge test refactoring from robertc
40
import unittest
41
import sys
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
42
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
43
# XXX: Don't need this anymore now we depend on python2.4
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
44
def _need_subprocess():
45
    sys.stderr.write("sorry, this test suite requires the subprocess module\n"
46
                     "this is shipped with python2.4 and available separately for 2.3\n")
47
    
48
49
class CommandFailed(Exception):
50
    pass
51
52
53
class TestSkipped(Exception):
54
    """Indicates that a test was intentionally skipped, rather than failing."""
55
    # XXX: Not used yet
56
57
1102 by Martin Pool
- merge test refactoring from robertc
58
class TestCase(unittest.TestCase):
59
    """Base class for bzr unit tests.
60
    
61
    Tests that need access to disk resources should subclass 
62
    FunctionalTestCase not TestCase.
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
63
    """
64
    
65
    # TODO: Special methods to invoke bzr, so that we can run it
66
    # through a specified Python intepreter
67
68
    OVERRIDE_PYTHON = None # to run with alternative python 'python'
69
    BZRPATH = 'bzr'
70
71
    def setUp(self):
1102 by Martin Pool
- merge test refactoring from robertc
72
        super(TestCase, self).setUp()
73
        # setup a temporary log for the test 
74
        import time
75
        import os
76
        import tempfile
1121 by Martin Pool
- make TestCase._formcommand private
77
        import logging
78
        
1102 by Martin Pool
- merge test refactoring from robertc
79
        self.TEST_LOG = tempfile.NamedTemporaryFile(mode='wt', bufsize=0)
80
        # save stdout & stderr so there's no leakage from code-under-test
81
        self.real_stdout = sys.stdout
82
        self.real_stderr = sys.stderr
83
        sys.stdout = sys.stderr = self.TEST_LOG
1121 by Martin Pool
- make TestCase._formcommand private
84
        
85
        # set up default python log handler
86
        #self._handler = logging.StreamHandler(self.TEST_LOG)
87
        #self._handler.setLevel(logging.DEBUG)
88
        #logging.getLogger('').addHandler(self._handler)
89
        
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
90
        self.log("%s setup" % self.id())
91
92
    def tearDown(self):
1121 by Martin Pool
- make TestCase._formcommand private
93
        # logging.getLogger('').removeHandler(self._handler)
1102 by Martin Pool
- merge test refactoring from robertc
94
        sys.stdout = self.real_stdout
95
        sys.stderr = self.real_stderr
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
96
        self.log("%s teardown" % self.id())
97
        self.log('')
1102 by Martin Pool
- merge test refactoring from robertc
98
        super(TestCase, self).tearDown()
99
100
    def log(self, msg):
101
        """Log a message to a progress file"""
102
        print >>self.TEST_LOG, msg
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
    def _get_log(self):
126
        """Get the log the test case used. This can only be called once,
127
        after which an exception will be raised.
128
        """
129
        self.TEST_LOG.flush()
130
        log = open(self.TEST_LOG.name, 'rt').read()
131
        self.TEST_LOG.close()
132
        return log
133
134
135
class FunctionalTestCase(TestCase):
136
    """Base class for tests that perform function testing - running bzr,
137
    using files on disk, and similar activities.
138
139
    InTempDir is an old alias for FunctionalTestCase.
140
    """
141
142
    TEST_ROOT = None
143
    _TEST_NAME = 'test'
144
145
    def check_file_contents(self, filename, expect):
146
        self.log("check contents of file %s" % filename)
147
        contents = file(filename, 'r').read()
148
        if contents != expect:
149
            self.log("expected: %r" % expect)
150
            self.log("actually: %r" % contents)
151
            self.fail("contents of %s not as expected")
152
153
    def _make_test_root(self):
154
        import os
155
        import shutil
156
        import tempfile
157
        
158
        if FunctionalTestCase.TEST_ROOT is not None:
159
            return
160
        FunctionalTestCase.TEST_ROOT = os.path.abspath(
161
                                 tempfile.mkdtemp(suffix='.tmp',
162
                                                  prefix=self._TEST_NAME + '-',
163
                                                  dir=os.curdir))
164
    
165
        # make a fake bzr directory there to prevent any tests propagating
166
        # up onto the source directory's real branch
167
        os.mkdir(os.path.join(FunctionalTestCase.TEST_ROOT, '.bzr'))
168
169
    def setUp(self):
170
        super(FunctionalTestCase, self).setUp()
171
        import os
172
        self._make_test_root()
173
        self._currentdir = os.getcwdu()
174
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
175
        os.mkdir(self.test_dir)
176
        os.chdir(self.test_dir)
177
        
178
    def tearDown(self):
179
        import os
180
        os.chdir(self._currentdir)
181
        super(FunctionalTestCase, self).tearDown()
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
182
1121 by Martin Pool
- make TestCase._formcommand private
183
    def _formcmd(self, cmd):
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
184
        if isinstance(cmd, basestring):
185
            cmd = cmd.split()
186
        if cmd[0] == 'bzr':
187
            cmd[0] = self.BZRPATH
188
            if self.OVERRIDE_PYTHON:
189
                cmd.insert(0, self.OVERRIDE_PYTHON)
190
        self.log('$ %r' % cmd)
191
        return cmd
192
193
    def runcmd(self, cmd, retcode=0):
194
        """Run one command and check the return code.
195
196
        Returns a tuple of (stdout,stderr) strings.
197
198
        If a single string is based, it is split into words.
199
        For commands that are not simple space-separated words, please
200
        pass a list instead."""
201
        try:
202
            import shutil
203
            from subprocess import call
204
        except ImportError, e:
205
            _need_subprocess()
206
            raise
1121 by Martin Pool
- make TestCase._formcommand private
207
        cmd = self._formcmd(cmd)
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
208
        self.log('$ ' + ' '.join(cmd))
209
        actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
210
        if retcode != actual_retcode:
211
            raise CommandFailed("test failed: %r returned %d, expected %d"
212
                                % (cmd, actual_retcode, retcode))
213
214
    def backtick(self, cmd, retcode=0):
215
        """Run a command and return its output"""
216
        try:
217
            import shutil
218
            from subprocess import Popen, PIPE
219
        except ImportError, e:
220
            _need_subprocess()
221
            raise
222
1121 by Martin Pool
- make TestCase._formcommand private
223
        cmd = self._formcmd(cmd)
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
224
        child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
225
        outd, errd = child.communicate()
226
        self.log(outd)
227
        actual_retcode = child.wait()
228
229
        outd = outd.replace('\r', '')
230
231
        if retcode != actual_retcode:
232
            raise CommandFailed("test failed: %r returned %d, expected %d"
233
                                % (cmd, actual_retcode, retcode))
234
235
        return outd
236
237
238
239
    def build_tree(self, shape):
240
        """Build a test tree according to a pattern.
241
242
        shape is a sequence of file specifications.  If the final
243
        character is '/', a directory is created.
244
245
        This doesn't add anything to a branch.
246
        """
247
        # XXX: It's OK to just create them using forward slashes on windows?
248
        import os
249
        for name in shape:
250
            assert isinstance(name, basestring)
251
            if name[-1] == '/':
252
                os.mkdir(name[:-1])
253
            else:
254
                f = file(name, 'wt')
255
                print >>f, "contents of", name
256
                f.close()
1102 by Martin Pool
- merge test refactoring from robertc
257
                
258
InTempDir = FunctionalTestCase
259
260
261
class _MyResult(unittest._TextTestResult):
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
262
    """
263
    Custom TestResult.
264
265
    No special behaviour for now.
266
    """
267
268
    def startTest(self, test):
1102 by Martin Pool
- merge test refactoring from robertc
269
        unittest.TestResult.startTest(self, test)
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
270
        # TODO: Maybe show test.shortDescription somewhere?
842 by Martin Pool
- don't say runit when running tests under python2.3 dammit
271
        what = test.id()
272
        # python2.3 has the bad habit of just "runit" for doctests
273
        if what == 'runit':
274
            what = test.shortDescription()
1102 by Martin Pool
- merge test refactoring from robertc
275
        if self.showAll:
276
            self.stream.write('%-60.60s' % what)
277
        self.stream.flush()
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
278
279
    def addError(self, test, err):
1102 by Martin Pool
- merge test refactoring from robertc
280
        super(_MyResult, self).addError(test, err)
281
        self.stream.flush()
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
282
283
    def addFailure(self, test, err):
1102 by Martin Pool
- merge test refactoring from robertc
284
        super(_MyResult, self).addFailure(test, err)
285
        self.stream.flush()
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
286
287
    def addSuccess(self, test):
1102 by Martin Pool
- merge test refactoring from robertc
288
        if self.showAll:
289
            self.stream.writeln('OK')
290
        elif self.dots:
291
            self.stream.write('~')
292
        self.stream.flush()
293
        unittest.TestResult.addSuccess(self, test)
294
295
    def printErrorList(self, flavour, errors):
296
        for test, err in errors:
297
            self.stream.writeln(self.separator1)
298
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
299
            self.stream.writeln(self.separator2)
300
            self.stream.writeln("%s" % err)
301
            if isinstance(test, TestCase):
302
                self.stream.writeln()
303
                self.stream.writeln('log from this test:')
304
                print >>self.stream, test._get_log()
305
306
307
class TextTestRunner(unittest.TextTestRunner):
308
309
    def _makeResult(self):
310
        return _MyResult(self.stream, self.descriptions, self.verbosity)
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
311
312
965 by Martin Pool
- selftest is less verbose by default, and takes a -v option if you want it
313
def run_suite(suite, name='test', verbose=False):
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
314
    import shutil
1102 by Martin Pool
- merge test refactoring from robertc
315
    FunctionalTestCase._TEST_NAME = name
316
    if verbose:
317
        verbosity = 2
318
    else:
319
        verbosity = 1
320
    runner = TextTestRunner(stream=sys.stdout,
321
                            descriptions=0,
322
                            verbosity=verbosity)
323
    result = runner.run(suite)
324
    # This is still a little bogus, 
325
    # but only a little. Folk not using our testrunner will
326
    # have to delete their temp directories themselves.
327
    if result.wasSuccessful():
1122 by Martin Pool
- only delete the test scratch directory if it was created
328
        if FunctionalTestCase.TEST_ROOT:
329
            shutil.rmtree(FunctionalTestCase.TEST_ROOT) 
1102 by Martin Pool
- merge test refactoring from robertc
330
    else:
331
        print "Failed tests working directories are in '%s'\n" % FunctionalTestCase.TEST_ROOT
841 by Martin Pool
- Start splitting bzr-independent parts of the test framework into
332
    return result.wasSuccessful()