~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to testsweet.py

  • Committer: Robert Collins
  • Date: 2005-08-24 08:34:10 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-20050824083410-98aa4eeb52653394
import and use TestUtil to do regex based partial test runs

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
    """Indicates that a test was intentionally skipped, rather than failing."""
56
56
    # XXX: Not used yet
57
57
 
 
58
 
 
59
class TestCase(unittest.TestCase):
 
60
    """Base class for bzr unit tests.
 
61
    
 
62
    Tests that need access to disk resources should subclass 
 
63
    FunctionalTestCase not TestCase.
 
64
    """
 
65
    
 
66
    # TODO: Special methods to invoke bzr, so that we can run it
 
67
    # through a specified Python intepreter
 
68
 
 
69
    OVERRIDE_PYTHON = None # to run with alternative python 'python'
 
70
    BZRPATH = 'bzr'
 
71
 
 
72
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
73
                         a_callable=None, *args, **kwargs):
 
74
        """Call callable with redirected std io pipes.
 
75
 
 
76
        Returns the return code."""
 
77
        from StringIO import StringIO
 
78
        if not callable(a_callable):
 
79
            raise ValueError("a_callable must be callable.")
 
80
        if stdin is None:
 
81
            stdin = StringIO("")
 
82
        if stdout is None:
 
83
            stdout = self.TEST_LOG
 
84
        if stderr is None:
 
85
            stderr = self.TEST_LOG
 
86
        real_stdin = sys.stdin
 
87
        real_stdout = sys.stdout
 
88
        real_stderr = sys.stderr
 
89
        result = None
 
90
        try:
 
91
            sys.stdout = stdout
 
92
            sys.stderr = stderr
 
93
            sys.stdin = stdin
 
94
            result = a_callable(*args, **kwargs)
 
95
        finally:
 
96
            sys.stdout = real_stdout
 
97
            sys.stderr = real_stderr
 
98
            sys.stdin = real_stdin
 
99
        return result
 
100
 
 
101
    def setUp(self):
 
102
        super(TestCase, self).setUp()
 
103
        # setup a temporary log for the test 
 
104
        import tempfile
 
105
        self.TEST_LOG = tempfile.NamedTemporaryFile(mode='wt', bufsize=0)
 
106
        self.log("%s setup" % self.id())
 
107
 
 
108
    def tearDown(self):
 
109
        self.log("%s teardown" % self.id())
 
110
        self.log('')
 
111
        super(TestCase, self).tearDown()
 
112
 
 
113
    def log(self, msg):
 
114
        """Log a message to a progress file"""
 
115
        print >>self.TEST_LOG, msg
 
116
 
 
117
    def check_inventory_shape(self, inv, shape):
 
118
        """
 
119
        Compare an inventory to a list of expected names.
 
120
 
 
121
        Fail if they are not precisely equal.
 
122
        """
 
123
        extras = []
 
124
        shape = list(shape)             # copy
 
125
        for path, ie in inv.entries():
 
126
            name = path.replace('\\', '/')
 
127
            if ie.kind == 'dir':
 
128
                name = name + '/'
 
129
            if name in shape:
 
130
                shape.remove(name)
 
131
            else:
 
132
                extras.append(name)
 
133
        if shape:
 
134
            self.fail("expected paths not found in inventory: %r" % shape)
 
135
        if extras:
 
136
            self.fail("unexpected paths found in inventory: %r" % extras)
 
137
     
 
138
    def _get_log(self):
 
139
        """Get the log the test case used. This can only be called once,
 
140
        after which an exception will be raised.
 
141
        """
 
142
        self.TEST_LOG.flush()
 
143
        log = open(self.TEST_LOG.name, 'rt').read()
 
144
        self.TEST_LOG.close()
 
145
        return log
 
146
 
 
147
 
 
148
class FunctionalTestCase(TestCase):
 
149
    """Base class for tests that perform function testing - running bzr,
 
150
    using files on disk, and similar activities.
 
151
 
 
152
    InTempDir is an old alias for FunctionalTestCase.
 
153
    """
 
154
 
 
155
    TEST_ROOT = None
 
156
    _TEST_NAME = 'test'
 
157
 
 
158
    def check_file_contents(self, filename, expect):
 
159
        self.log("check contents of file %s" % filename)
 
160
        contents = file(filename, 'r').read()
 
161
        if contents != expect:
 
162
            self.log("expected: %r" % expect)
 
163
            self.log("actually: %r" % contents)
 
164
            self.fail("contents of %s not as expected")
 
165
 
 
166
    def _make_test_root(self):
 
167
        import os
 
168
        import shutil
 
169
        import tempfile
 
170
        
 
171
        if FunctionalTestCase.TEST_ROOT is not None:
 
172
            return
 
173
        FunctionalTestCase.TEST_ROOT = os.path.abspath(
 
174
                                 tempfile.mkdtemp(suffix='.tmp',
 
175
                                                  prefix=self._TEST_NAME + '-',
 
176
                                                  dir=os.curdir))
 
177
    
 
178
        # make a fake bzr directory there to prevent any tests propagating
 
179
        # up onto the source directory's real branch
 
180
        os.mkdir(os.path.join(FunctionalTestCase.TEST_ROOT, '.bzr'))
 
181
 
 
182
    def setUp(self):
 
183
        super(FunctionalTestCase, self).setUp()
 
184
        import os
 
185
        self._make_test_root()
 
186
        self._currentdir = os.getcwdu()
 
187
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
188
        os.mkdir(self.test_dir)
 
189
        os.chdir(self.test_dir)
 
190
        
 
191
    def tearDown(self):
 
192
        import os
 
193
        os.chdir(self._currentdir)
 
194
        super(FunctionalTestCase, self).tearDown()
 
195
 
 
196
    def formcmd(self, cmd):
 
197
        if isinstance(cmd, basestring):
 
198
            cmd = cmd.split()
 
199
        if cmd[0] == 'bzr':
 
200
            cmd[0] = self.BZRPATH
 
201
            if self.OVERRIDE_PYTHON:
 
202
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
203
        self.log('$ %r' % cmd)
 
204
        return cmd
 
205
 
 
206
    def runcmd(self, cmd, retcode=0):
 
207
        """Run one command and check the return code.
 
208
 
 
209
        Returns a tuple of (stdout,stderr) strings.
 
210
 
 
211
        If a single string is based, it is split into words.
 
212
        For commands that are not simple space-separated words, please
 
213
        pass a list instead."""
 
214
        try:
 
215
            import shutil
 
216
            from subprocess import call
 
217
        except ImportError, e:
 
218
            _need_subprocess()
 
219
            raise
 
220
        cmd = self.formcmd(cmd)
 
221
        self.log('$ ' + ' '.join(cmd))
 
222
        actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
 
223
        if retcode != actual_retcode:
 
224
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
225
                                % (cmd, actual_retcode, retcode))
 
226
 
 
227
    def backtick(self, cmd, retcode=0):
 
228
        """Run a command and return its output"""
 
229
        try:
 
230
            import shutil
 
231
            from subprocess import Popen, PIPE
 
232
        except ImportError, e:
 
233
            _need_subprocess()
 
234
            raise
 
235
        cmd = self.formcmd(cmd)
 
236
        child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
 
237
        outd, errd = child.communicate()
 
238
        self.log(outd)
 
239
        actual_retcode = child.wait()
 
240
        outd = outd.replace('\r', '')
 
241
        if retcode != actual_retcode:
 
242
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
243
                                % (cmd, actual_retcode, retcode))
 
244
        return outd
 
245
 
 
246
    def build_tree(self, shape):
 
247
        """Build a test tree according to a pattern.
 
248
 
 
249
        shape is a sequence of file specifications.  If the final
 
250
        character is '/', a directory is created.
 
251
 
 
252
        This doesn't add anything to a branch.
 
253
        """
 
254
        # XXX: It's OK to just create them using forward slashes on windows?
 
255
        import os
 
256
        for name in shape:
 
257
            assert isinstance(name, basestring)
 
258
            if name[-1] == '/':
 
259
                os.mkdir(name[:-1])
 
260
            else:
 
261
                f = file(name, 'wt')
 
262
                print >>f, "contents of", name
 
263
                f.close()
 
264
                
 
265
InTempDir = FunctionalTestCase
 
266
 
 
267
 
58
268
class EarlyStoppingTestResultAdapter(object):
59
269
    """An adapter for TestResult to stop at the first first failure or error"""
60
270
 
88
298
    def startTest(self, test):
89
299
        unittest.TestResult.startTest(self, test)
90
300
        # TODO: Maybe show test.shortDescription somewhere?
91
 
        what = test.shortDescription() or test.id()        
 
301
        what = test.id()
 
302
        # python2.3 has the bad habit of just "runit" for doctests
 
303
        if what == 'runit':
 
304
            what = test.shortDescription()
92
305
        if self.showAll:
93
 
            self.stream.write('%-70.70s' % what)
 
306
            self.stream.write('%-60.60s' % what)
94
307
        self.stream.flush()
95
308
 
96
309
    def addError(self, test, err):
115
328
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
116
329
            self.stream.writeln(self.separator2)
117
330
            self.stream.writeln("%s" % err)
118
 
            if hasattr(test, '_get_log'):
 
331
            if isinstance(test, TestCase):
119
332
                self.stream.writeln()
120
333
                self.stream.writeln('log from this test:')
121
334
                print >>self.stream, test._get_log()
152
365
 
153
366
def run_suite(suite, name='test', verbose=False, pattern=".*"):
154
367
    import shutil
155
 
    from bzrlib.selftest import TestCaseInTempDir
156
 
    TestCaseInTempDir._TEST_NAME = name
 
368
    FunctionalTestCase._TEST_NAME = name
157
369
    if verbose:
158
370
        verbosity = 2
159
371
    else:
168
380
    # but only a little. Folk not using our testrunner will
169
381
    # have to delete their temp directories themselves.
170
382
    if result.wasSuccessful():
171
 
        if TestCaseInTempDir.TEST_ROOT is not None:
172
 
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
383
        if FunctionalTestCase.TEST_ROOT is not None:
 
384
            shutil.rmtree(FunctionalTestCase.TEST_ROOT) 
173
385
    else:
174
 
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
386
        print "Failed tests working directories are in '%s'\n" % FunctionalTestCase.TEST_ROOT
175
387
    return result.wasSuccessful()