~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-07-06 04:40:52 UTC
  • Revision ID: mbp@sourcefrog.net-20050706044052-afa8a07b9a132888
- Start splitting bzr-independent parts of the test framework into
  testsweet.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
from unittest import TestResult, TestCase
19
 
 
20
 
class CommandFailed(Exception):
21
 
    pass
22
 
 
23
 
 
24
 
class TestBase(TestCase):
25
 
    """Base class for bzr test cases.
26
 
 
27
 
    Just defines some useful helper functions; doesn't actually test
28
 
    anything.
29
 
    """
30
 
    
31
 
    # TODO: Special methods to invoke bzr, so that we can run it
32
 
    # through a specified Python intepreter
33
 
 
34
 
    OVERRIDE_PYTHON = None # to run with alternative python 'python'
35
 
    BZRPATH = 'bzr'
36
 
 
37
 
    _log_buf = ""
38
 
 
39
 
 
40
 
    def formcmd(self, cmd):
41
 
        if isinstance(cmd, basestring):
42
 
            cmd = cmd.split()
43
 
 
44
 
        if cmd[0] == 'bzr':
45
 
            cmd[0] = self.BZRPATH
46
 
            if self.OVERRIDE_PYTHON:
47
 
                cmd.insert(0, self.OVERRIDE_PYTHON)
48
 
 
49
 
        self.log('$ %r' % cmd)
50
 
 
51
 
        return cmd
52
 
 
53
 
 
54
 
    def runcmd(self, cmd, retcode=0):
55
 
        """Run one command and check the return code.
56
 
 
57
 
        Returns a tuple of (stdout,stderr) strings.
58
 
 
59
 
        If a single string is based, it is split into words.
60
 
        For commands that are not simple space-separated words, please
61
 
        pass a list instead."""
62
 
        try:
63
 
            import shutil
64
 
            from subprocess import call
65
 
        except ImportError, e:
66
 
            import sys
67
 
            sys.stderr.write("testbzr: sorry, this test suite requires the subprocess module\n"
68
 
                             "this is shipped with python2.4 and available separately for 2.3\n")
69
 
            raise
70
 
 
71
 
 
72
 
        cmd = self.formcmd(cmd)
73
 
 
74
 
        self.log('$ ' + ' '.join(cmd))
75
 
        actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
76
 
 
77
 
        if retcode != actual_retcode:
78
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
79
 
                                % (cmd, actual_retcode, retcode))
80
 
 
81
 
 
82
 
    def backtick(self, cmd, retcode=0):
83
 
        """Run a command and return its output"""
84
 
        try:
85
 
            import shutil
86
 
            from subprocess import Popen, PIPE
87
 
        except ImportError, e:
88
 
            import sys
89
 
            sys.stderr.write("testbzr: sorry, this test suite requires the subprocess module\n"
90
 
                             "this is shipped with python2.4 and available separately for 2.3\n")
91
 
            raise
92
 
 
93
 
 
94
 
        cmd = self.formcmd(cmd)
95
 
        child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
96
 
        outd, errd = child.communicate()
97
 
        self.log(outd)
98
 
        actual_retcode = child.wait()
99
 
 
100
 
        outd = outd.replace('\r', '')
101
 
 
102
 
        if retcode != actual_retcode:
103
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
104
 
                                % (cmd, actual_retcode, retcode))
105
 
 
106
 
        return outd
107
 
 
108
 
 
109
 
 
110
 
    def build_tree(self, shape):
111
 
        """Build a test tree according to a pattern.
112
 
 
113
 
        shape is a sequence of file specifications.  If the final
114
 
        character is '/', a directory is created.
115
 
 
116
 
        This doesn't add anything to a branch.
117
 
        """
118
 
        # XXX: It's OK to just create them using forward slashes on windows?
119
 
        import os
120
 
        for name in shape:
121
 
            assert isinstance(name, basestring)
122
 
            if name[-1] == '/':
123
 
                os.mkdir(name[:-1])
124
 
            else:
125
 
                f = file(name, 'wt')
126
 
                print >>f, "contents of", name
127
 
                f.close()
128
 
 
129
 
 
130
 
    def log(self, msg):
131
 
        """Log a message to a progress file"""
132
 
        self._log_buf = self._log_buf + str(msg) + '\n'
133
 
        print >>self.TEST_LOG, msg
134
 
 
135
 
 
136
 
    def check_inventory_shape(self, inv, shape):
137
 
        """
138
 
        Compare an inventory to a list of expected names.
139
 
 
140
 
        Fail if they are not precisely equal.
141
 
        """
142
 
        extras = []
143
 
        shape = list(shape)             # copy
144
 
        for path, ie in inv.entries():
145
 
            name = path.replace('\\', '/')
146
 
            if ie.kind == 'dir':
147
 
                name = name + '/'
148
 
            if name in shape:
149
 
                shape.remove(name)
150
 
            else:
151
 
                extras.append(name)
152
 
        if shape:
153
 
            self.fail("expected paths not found in inventory: %r" % shape)
154
 
        if extras:
155
 
            self.fail("unexpected paths found in inventory: %r" % extras)
156
 
 
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" % expected)
163
 
            self.log("actually: %r" % contents)
164
 
            self.fail("contents of %s not as expected")
165
 
            
166
 
 
167
 
 
168
 
class InTempDir(TestBase):
169
 
    """Base class for tests run in a temporary branch."""
170
 
    def setUp(self):
171
 
        import os
172
 
        self.test_dir = os.path.join(self.TEST_ROOT, self.__class__.__name__)
173
 
        os.mkdir(self.test_dir)
174
 
        os.chdir(self.test_dir)
175
 
        
176
 
    def tearDown(self):
177
 
        import os
178
 
        os.chdir(self.TEST_ROOT)
179
 
 
180
 
 
181
 
 
182
 
 
183
 
 
184
 
class _MyResult(TestResult):
185
 
    """
186
 
    Custom TestResult.
187
 
 
188
 
    No special behaviour for now.
189
 
    """
190
 
    def __init__(self, out):
191
 
        self.out = out
192
 
        TestResult.__init__(self)
193
 
 
194
 
    def startTest(self, test):
195
 
        # TODO: Maybe show test.shortDescription somewhere?
196
 
        print >>self.out, '%-60.60s' % test.id(),
197
 
        self.out.flush()
198
 
        TestResult.startTest(self, test)
199
 
 
200
 
    def stopTest(self, test):
201
 
        # print
202
 
        TestResult.stopTest(self, test)
203
 
 
204
 
 
205
 
    def addError(self, test, err):
206
 
        print >>self.out, 'ERROR'
207
 
        TestResult.addError(self, test, err)
208
 
        _show_test_failure('error', test, err, self.out)
209
 
 
210
 
    def addFailure(self, test, err):
211
 
        print >>self.out, 'FAILURE'
212
 
        TestResult.addFailure(self, test, err)
213
 
        _show_test_failure('failure', test, err, self.out)
214
 
 
215
 
    def addSuccess(self, test):
216
 
        print >>self.out, 'OK'
217
 
        TestResult.addSuccess(self, test)
218
 
 
 
18
from testsweet import TestBase, run_suite, InTempDir
219
19
 
220
20
 
221
21
def selftest():
236
36
    TestBase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
237
37
    print '%-30s %s' % ('bzr binary', TestBase.BZRPATH)
238
38
 
239
 
    _setup_test_log()
240
 
    _setup_test_dir()
241
39
    print
242
40
 
243
41
    suite = TestSuite()
258
56
 
259
57
    suite.addTest(bzrlib.selftest.blackbox.suite())
260
58
 
261
 
    # save stdout & stderr so there's no leakage from code-under-test
262
 
    real_stdout = sys.stdout
263
 
    real_stderr = sys.stderr
264
 
    sys.stdout = sys.stderr = TestBase.TEST_LOG
265
 
    try:
266
 
        result = _MyResult(real_stdout)
267
 
        suite.run(result)
268
 
    finally:
269
 
        sys.stdout = real_stdout
270
 
        sys.stderr = real_stderr
271
 
 
272
 
    _show_results(result)
273
 
 
274
 
    return result.wasSuccessful()
275
 
 
276
 
 
277
 
 
278
 
 
279
 
def _setup_test_log():
280
 
    import time
281
 
    import os
282
 
    
283
 
    log_filename = os.path.abspath('testbzr.log')
284
 
    TestBase.TEST_LOG = open(log_filename, 'wt', buffering=1) # line buffered
285
 
 
286
 
    print >>TestBase.TEST_LOG, "bzr tests run at " + time.ctime()
287
 
    print '%-30s %s' % ('test log', log_filename)
288
 
 
289
 
 
290
 
def _setup_test_dir():
291
 
    import os
292
 
    import shutil
293
 
    
294
 
    TestBase.ORIG_DIR = os.getcwdu()
295
 
    TestBase.TEST_ROOT = os.path.abspath("testbzr.tmp")
296
 
 
297
 
    print '%-30s %s' % ('running tests in', TestBase.TEST_ROOT)
298
 
 
299
 
    if os.path.exists(TestBase.TEST_ROOT):
300
 
        shutil.rmtree(TestBase.TEST_ROOT)
301
 
    os.mkdir(TestBase.TEST_ROOT)
302
 
    os.chdir(TestBase.TEST_ROOT)
303
 
 
304
 
    # make a fake bzr directory there to prevent any tests propagating
305
 
    # up onto the source directory's real branch
306
 
    os.mkdir(os.path.join(TestBase.TEST_ROOT, '.bzr'))
307
 
 
308
 
    
309
 
 
310
 
def _show_results(result):
311
 
     print
312
 
     print '%4d tests run' % result.testsRun
313
 
     print '%4d errors' % len(result.errors)
314
 
     print '%4d failures' % len(result.failures)
315
 
 
316
 
 
317
 
 
318
 
def _show_test_failure(kind, case, exc_info, out):
319
 
    from traceback import print_exception
320
 
    
321
 
    print >>out, '-' * 60
322
 
    print >>out, case
323
 
    
324
 
    desc = case.shortDescription()
325
 
    if desc:
326
 
        print >>out, '   (%s)' % desc
327
 
         
328
 
    print_exception(exc_info[0], exc_info[1], exc_info[2], None, out)
329
 
        
330
 
    if isinstance(case, TestBase):
331
 
        print >>out
332
 
        print >>out, 'log from this test:'
333
 
        print >>out, case._log_buf
334
 
         
335
 
    print >>out, '-' * 60
336
 
    
 
59
    return run_suite(suite)
 
60
 
 
61
 
337
62