~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to testsweet.py

  • Committer: Robert Collins
  • Date: 2005-08-23 10:44:48 UTC
  • mto: (974.1.50) (1185.1.10) (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1139.
  • Revision ID: robertc@robertcollins.net-20050823104448-fb5d448e7a5a8ee3
relace runTest with test_foo in blackbox tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
 
40
40
import unittest
41
41
import sys
42
 
from bzrlib.selftest import TestUtil
43
42
 
44
43
# XXX: Don't need this anymore now we depend on python2.4
45
44
def _need_subprocess():
55
54
    """Indicates that a test was intentionally skipped, rather than failing."""
56
55
    # XXX: Not used yet
57
56
 
58
 
class EarlyStoppingTestResultAdapter(object):
59
 
    """An adapter for TestResult to stop at the first first failure or error"""
60
 
 
61
 
    def __init__(self, result):
62
 
        self._result = result
63
 
 
64
 
    def addError(self, test, err):
65
 
        self._result.addError(test, err)
66
 
        self._result.stop()
67
 
 
68
 
    def addFailure(self, test, err):
69
 
        self._result.addFailure(test, err)
70
 
        self._result.stop()
71
 
 
72
 
    def __getattr__(self, name):
73
 
        return getattr(self._result, name)
74
 
 
75
 
    def __setattr__(self, name, value):
76
 
        if name == '_result':
77
 
            object.__setattr__(self, name, value)
78
 
        return setattr(self._result, name, value)
 
57
 
 
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.
 
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):
 
72
        super(TestCase, self).setUp()
 
73
        # setup a temporary log for the test 
 
74
        import time
 
75
        import os
 
76
        import tempfile
 
77
        self.TEST_LOG = tempfile.NamedTemporaryFile(mode='wt', bufsize=0)
 
78
        # save stdout & stderr so there's no leakage from code-under-test
 
79
        self.real_stdout = sys.stdout
 
80
        self.real_stderr = sys.stderr
 
81
        sys.stdout = sys.stderr = self.TEST_LOG
 
82
        self.log("%s setup" % self.id())
 
83
 
 
84
    def tearDown(self):
 
85
        sys.stdout = self.real_stdout
 
86
        sys.stderr = self.real_stderr
 
87
        self.log("%s teardown" % self.id())
 
88
        self.log('')
 
89
        super(TestCase, self).tearDown()
 
90
 
 
91
    def log(self, msg):
 
92
        """Log a message to a progress file"""
 
93
        print >>self.TEST_LOG, msg
 
94
 
 
95
    def check_inventory_shape(self, inv, shape):
 
96
        """
 
97
        Compare an inventory to a list of expected names.
 
98
 
 
99
        Fail if they are not precisely equal.
 
100
        """
 
101
        extras = []
 
102
        shape = list(shape)             # copy
 
103
        for path, ie in inv.entries():
 
104
            name = path.replace('\\', '/')
 
105
            if ie.kind == 'dir':
 
106
                name = name + '/'
 
107
            if name in shape:
 
108
                shape.remove(name)
 
109
            else:
 
110
                extras.append(name)
 
111
        if shape:
 
112
            self.fail("expected paths not found in inventory: %r" % shape)
 
113
        if extras:
 
114
            self.fail("unexpected paths found in inventory: %r" % extras)
 
115
     
 
116
    def _get_log(self):
 
117
        """Get the log the test case used. This can only be called once,
 
118
        after which an exception will be raised.
 
119
        """
 
120
        self.TEST_LOG.flush()
 
121
        log = open(self.TEST_LOG.name, 'rt').read()
 
122
        self.TEST_LOG.close()
 
123
        return log
 
124
 
 
125
 
 
126
class FunctionalTestCase(TestCase):
 
127
    """Base class for tests that perform function testing - running bzr,
 
128
    using files on disk, and similar activities.
 
129
 
 
130
    InTempDir is an old alias for FunctionalTestCase.
 
131
    """
 
132
 
 
133
    TEST_ROOT = None
 
134
    _TEST_NAME = 'test'
 
135
 
 
136
    def check_file_contents(self, filename, expect):
 
137
        self.log("check contents of file %s" % filename)
 
138
        contents = file(filename, 'r').read()
 
139
        if contents != expect:
 
140
            self.log("expected: %r" % expect)
 
141
            self.log("actually: %r" % contents)
 
142
            self.fail("contents of %s not as expected")
 
143
 
 
144
    def _make_test_root(self):
 
145
        import os
 
146
        import shutil
 
147
        import tempfile
 
148
        
 
149
        if FunctionalTestCase.TEST_ROOT is not None:
 
150
            return
 
151
        FunctionalTestCase.TEST_ROOT = os.path.abspath(
 
152
                                 tempfile.mkdtemp(suffix='.tmp',
 
153
                                                  prefix=self._TEST_NAME + '-',
 
154
                                                  dir=os.curdir))
 
155
    
 
156
        # make a fake bzr directory there to prevent any tests propagating
 
157
        # up onto the source directory's real branch
 
158
        os.mkdir(os.path.join(FunctionalTestCase.TEST_ROOT, '.bzr'))
 
159
 
 
160
    def setUp(self):
 
161
        super(FunctionalTestCase, self).setUp()
 
162
        import os
 
163
        self._make_test_root()
 
164
        self._currentdir = os.getcwdu()
 
165
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
166
        os.mkdir(self.test_dir)
 
167
        os.chdir(self.test_dir)
 
168
        
 
169
    def tearDown(self):
 
170
        import os
 
171
        os.chdir(self._currentdir)
 
172
        super(FunctionalTestCase, self).tearDown()
 
173
 
 
174
    def formcmd(self, cmd):
 
175
        if isinstance(cmd, basestring):
 
176
            cmd = cmd.split()
 
177
        if cmd[0] == 'bzr':
 
178
            cmd[0] = self.BZRPATH
 
179
            if self.OVERRIDE_PYTHON:
 
180
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
181
        self.log('$ %r' % cmd)
 
182
        return cmd
 
183
 
 
184
    def runcmd(self, cmd, retcode=0):
 
185
        """Run one command and check the return code.
 
186
 
 
187
        Returns a tuple of (stdout,stderr) strings.
 
188
 
 
189
        If a single string is based, it is split into words.
 
190
        For commands that are not simple space-separated words, please
 
191
        pass a list instead."""
 
192
        try:
 
193
            import shutil
 
194
            from subprocess import call
 
195
        except ImportError, e:
 
196
            _need_subprocess()
 
197
            raise
 
198
        cmd = self.formcmd(cmd)
 
199
        self.log('$ ' + ' '.join(cmd))
 
200
        actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
 
201
        if retcode != actual_retcode:
 
202
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
203
                                % (cmd, actual_retcode, retcode))
 
204
 
 
205
    def backtick(self, cmd, retcode=0):
 
206
        """Run a command and return its output"""
 
207
        try:
 
208
            import shutil
 
209
            from subprocess import Popen, PIPE
 
210
        except ImportError, e:
 
211
            _need_subprocess()
 
212
            raise
 
213
 
 
214
        cmd = self.formcmd(cmd)
 
215
        child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
 
216
        outd, errd = child.communicate()
 
217
        self.log(outd)
 
218
        actual_retcode = child.wait()
 
219
 
 
220
        outd = outd.replace('\r', '')
 
221
 
 
222
        if retcode != actual_retcode:
 
223
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
224
                                % (cmd, actual_retcode, retcode))
 
225
 
 
226
        return outd
 
227
 
 
228
 
 
229
 
 
230
    def build_tree(self, shape):
 
231
        """Build a test tree according to a pattern.
 
232
 
 
233
        shape is a sequence of file specifications.  If the final
 
234
        character is '/', a directory is created.
 
235
 
 
236
        This doesn't add anything to a branch.
 
237
        """
 
238
        # XXX: It's OK to just create them using forward slashes on windows?
 
239
        import os
 
240
        for name in shape:
 
241
            assert isinstance(name, basestring)
 
242
            if name[-1] == '/':
 
243
                os.mkdir(name[:-1])
 
244
            else:
 
245
                f = file(name, 'wt')
 
246
                print >>f, "contents of", name
 
247
                f.close()
 
248
                
 
249
InTempDir = FunctionalTestCase
79
250
 
80
251
 
81
252
class _MyResult(unittest._TextTestResult):
88
259
    def startTest(self, test):
89
260
        unittest.TestResult.startTest(self, test)
90
261
        # TODO: Maybe show test.shortDescription somewhere?
91
 
        what = test.shortDescription() or test.id()        
 
262
        what = test.id()
 
263
        # python2.3 has the bad habit of just "runit" for doctests
 
264
        if what == 'runit':
 
265
            what = test.shortDescription()
92
266
        if self.showAll:
93
 
            self.stream.write('%-70.70s' % what)
 
267
            self.stream.write('%-60.60s' % what)
94
268
        self.stream.flush()
95
269
 
96
270
    def addError(self, test, err):
115
289
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
116
290
            self.stream.writeln(self.separator2)
117
291
            self.stream.writeln("%s" % err)
118
 
            if hasattr(test, '_get_log'):
 
292
            if isinstance(test, TestCase):
119
293
                self.stream.writeln()
120
294
                self.stream.writeln('log from this test:')
121
295
                print >>self.stream, test._get_log()
124
298
class TextTestRunner(unittest.TextTestRunner):
125
299
 
126
300
    def _makeResult(self):
127
 
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
128
 
        return EarlyStoppingTestResultAdapter(result)
129
 
 
130
 
 
131
 
class filteringVisitor(TestUtil.TestVisitor):
132
 
    """I accruse all the testCases I visit that pass a regexp filter on id
133
 
    into my suite
134
 
    """
135
 
 
136
 
    def __init__(self, filter):
137
 
        import re
138
 
        TestUtil.TestVisitor.__init__(self)
139
 
        self._suite=None
140
 
        self.filter=re.compile(filter)
141
 
 
142
 
    def suite(self):
143
 
        """answer the suite we are building"""
144
 
        if self._suite is None:
145
 
            self._suite=TestUtil.TestSuite()
146
 
        return self._suite
147
 
 
148
 
    def visitCase(self, aCase):
149
 
        if self.filter.match(aCase.id()):
150
 
            self.suite().addTest(aCase)
151
 
 
152
 
 
153
 
def run_suite(suite, name='test', verbose=False, pattern=".*"):
 
301
        return _MyResult(self.stream, self.descriptions, self.verbosity)
 
302
 
 
303
 
 
304
def run_suite(suite, name='test', verbose=False):
154
305
    import shutil
155
 
    from bzrlib.selftest import TestCaseInTempDir
156
 
    TestCaseInTempDir._TEST_NAME = name
 
306
    FunctionalTestCase._TEST_NAME = name
157
307
    if verbose:
158
308
        verbosity = 2
159
309
    else:
161
311
    runner = TextTestRunner(stream=sys.stdout,
162
312
                            descriptions=0,
163
313
                            verbosity=verbosity)
164
 
    visitor = filteringVisitor(pattern)
165
 
    suite.visit(visitor)
166
 
    result = runner.run(visitor.suite())
 
314
    result = runner.run(suite)
167
315
    # This is still a little bogus, 
168
316
    # but only a little. Folk not using our testrunner will
169
317
    # have to delete their temp directories themselves.
170
318
    if result.wasSuccessful():
171
 
        if TestCaseInTempDir.TEST_ROOT is not None:
172
 
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
319
        shutil.rmtree(FunctionalTestCase.TEST_ROOT) 
173
320
    else:
174
 
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
321
        print "Failed tests working directories are in '%s'\n" % FunctionalTestCase.TEST_ROOT
175
322
    return result.wasSuccessful()