~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to testsweet.py

  • Committer: Martin Pool
  • Date: 2005-08-29 01:25:00 UTC
  • Revision ID: mbp@sourcefrog.net-20050829012500-36f2d20363be4a53
* move bzr-specific code from testsweet into bzrlib.selftest

* logging from within test suites is now done using python logging, so
  the regular .bzr.log is not cluttered and the results can be easily
  seen if the test fails

* don't capture stdout/stderr while running tests, instead let it leak
  through so that we can see places where the library is doing its own
  output and should be fixed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
    # XXX: Not used yet
56
56
 
57
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
 
        import logging
78
 
        
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
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
 
        
90
 
        self.log("%s setup" % self.id())
91
 
 
92
 
    def tearDown(self):
93
 
        # logging.getLogger('').removeHandler(self._handler)
94
 
        sys.stdout = self.real_stdout
95
 
        sys.stderr = self.real_stderr
96
 
        self.log("%s teardown" % self.id())
97
 
        self.log('')
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()
182
 
 
183
 
    def _formcmd(self, cmd):
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
207
 
        cmd = self._formcmd(cmd)
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
 
 
223
 
        cmd = self._formcmd(cmd)
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()
257
 
                
258
 
InTempDir = FunctionalTestCase
259
 
 
260
 
 
261
58
class _MyResult(unittest._TextTestResult):
262
59
    """
263
60
    Custom TestResult.
298
95
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
299
96
            self.stream.writeln(self.separator2)
300
97
            self.stream.writeln("%s" % err)
301
 
            if isinstance(test, TestCase):
 
98
            if hasattr(test, '_get_log'):
302
99
                self.stream.writeln()
303
100
                self.stream.writeln('log from this test:')
304
101
                print >>self.stream, test._get_log()
312
109
 
313
110
def run_suite(suite, name='test', verbose=False):
314
111
    import shutil
 
112
    from bzrlib.selftest import FunctionalTestCase
315
113
    FunctionalTestCase._TEST_NAME = name
316
114
    if verbose:
317
115
        verbosity = 2