~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Martin Pool
  • Date: 2005-06-24 07:06:38 UTC
  • Revision ID: mbp@sourcefrog.net-20050624070638-4b1230afde50b1a8
- files are only reported as modified if their name or parent has changed,
  not if their parent is renamed

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