/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_trace.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-07-28 02:47:10 UTC
  • mfrom: (7519.1.1 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200728024710-a2ylds219f1lsl62
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/388173

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
 
19
19
"""Tests for trace library"""
20
20
 
21
 
from cStringIO import StringIO
22
21
import errno
 
22
from io import StringIO
 
23
import logging
23
24
import os
24
25
import re
25
26
import sys
26
27
import tempfile
27
28
 
28
 
from bzrlib import (
 
29
from .. import (
 
30
    debug,
29
31
    errors,
30
32
    trace,
31
33
    )
32
 
from bzrlib.tests import TestCaseInTempDir, TestCase
33
 
from bzrlib.trace import (
 
34
from . import features, TestCaseInTempDir, TestCase, TestSkipped
 
35
from ..trace import (
34
36
    mutter, mutter_callsite, report_exception,
35
37
    set_verbosity_level, get_verbosity_level, is_quiet, is_verbose, be_quiet,
36
38
    pop_log_file,
51
53
 
52
54
    def test_format_sys_exception(self):
53
55
        # Test handling of an internal/unexpected error that probably
54
 
        # indicates a bug in bzr.  The details of the message may vary
 
56
        # indicates a bug in brz.  The details of the message may vary
55
57
        # depending on whether apport is available or not.  See test_crash for
56
58
        # more.
57
59
        try:
58
 
            raise NotImplementedError, "time travel"
 
60
            raise NotImplementedError("time travel")
59
61
        except NotImplementedError:
60
 
            pass
61
 
        err = _format_exception()
62
 
        self.assertEqualDiff(err.splitlines()[0],
63
 
                'bzr: ERROR: exceptions.NotImplementedError: time travel')
64
 
        self.assertContainsRe(err,
65
 
            'Bazaar has encountered an internal error.')
 
62
            err = _format_exception()
 
63
        self.assertContainsRe(err,
 
64
                              '^brz: ERROR: NotImplementedError: time travel')
 
65
        self.assertContainsRe(err,
 
66
                              'Bazaar has encountered an internal error.')
66
67
 
67
68
    def test_format_interrupt_exception(self):
68
69
        try:
69
70
            raise KeyboardInterrupt()
70
71
        except KeyboardInterrupt:
71
72
            # XXX: Some risk that a *real* keyboard interrupt won't be seen
72
 
            pass
73
 
        msg = _format_exception()
74
 
        self.assertTrue(len(msg) > 0)
75
 
        self.assertEqualDiff(msg, 'bzr: interrupted\n')
 
73
            msg = _format_exception()
 
74
        self.assertEqual(msg, 'brz: interrupted\n')
76
75
 
77
76
    def test_format_memory_error(self):
78
77
        try:
79
78
            raise MemoryError()
80
79
        except MemoryError:
81
 
            pass
82
 
        msg = _format_exception()
83
 
        self.assertEquals(msg,
84
 
            "bzr: out of memory\n")
 
80
            msg = _format_exception()
 
81
        self.assertEqual(
 
82
            msg,
 
83
            "brz: out of memory\nUse -Dmem_dump to dump memory to a file.\n")
 
84
 
 
85
    def test_format_mem_dump(self):
 
86
        self.requireFeature(features.meliae)
 
87
        debug.debug_flags.add('mem_dump')
 
88
        try:
 
89
            raise MemoryError()
 
90
        except MemoryError:
 
91
            msg = _format_exception()
 
92
        self.assertStartsWith(msg,
 
93
                              "brz: out of memory\nMemory dumped to ")
85
94
 
86
95
    def test_format_os_error(self):
87
96
        try:
88
97
            os.rmdir('nosuchfile22222')
89
 
        except OSError, e:
 
98
        except OSError as e:
90
99
            e_str = str(e)
91
 
        msg = _format_exception()
 
100
            msg = _format_exception()
92
101
        # Linux seems to give "No such file" but Windows gives "The system
93
102
        # cannot find the file specified".
94
 
        self.assertEqual('bzr: ERROR: %s\n' % (e_str,), msg)
 
103
        self.assertEqual('brz: ERROR: %s\n' % (e_str,), msg)
95
104
 
96
105
    def test_format_io_error(self):
97
106
        try:
98
 
            file('nosuchfile22222')
 
107
            open('nosuchfile22222')
99
108
        except IOError:
100
 
            pass
101
 
        msg = _format_exception()
 
109
            msg = _format_exception()
102
110
        # Even though Windows and Linux differ for 'os.rmdir', they both give
103
111
        # 'No such file' for open()
 
112
        # However it now gets translated so we can not test for a specific
 
113
        # message
104
114
        self.assertContainsRe(msg,
105
 
            r'^bzr: ERROR: \[Errno .*\] No such file.*nosuchfile')
 
115
                              '^brz: ERROR: \\[Errno .*\\] .*nosuchfile')
 
116
 
 
117
    def test_format_pywintypes_error(self):
 
118
        self.requireFeature(features.pywintypes)
 
119
        import pywintypes
 
120
        import win32file
 
121
        try:
 
122
            win32file.RemoveDirectory('nosuchfile22222')
 
123
        except pywintypes.error:
 
124
            msg = _format_exception()
 
125
        # GZ 2010-05-03: Formatting for pywintypes.error is basic, a 3-tuple
 
126
        #                with errno, function name, and locale error message
 
127
        self.assertContainsRe(
 
128
            msg, "^brz: ERROR: \\(2, 'RemoveDirectory[AW]?', .*\\)")
 
129
 
 
130
    def test_format_sockets_error(self):
 
131
        try:
 
132
            import socket
 
133
            sock = socket.socket()
 
134
            sock.send(b"This should fail.")
 
135
        except socket.error:
 
136
            msg = _format_exception()
 
137
 
 
138
        self.assertNotContainsRe(msg,
 
139
                                 "Traceback \\(most recent call last\\):")
106
140
 
107
141
    def test_format_unicode_error(self):
108
142
        try:
109
 
            raise errors.BzrCommandError(u'argument foo\xb5 does not exist')
110
 
        except errors.BzrCommandError:
111
 
            pass
112
 
        msg = _format_exception()
 
143
            raise errors.CommandError(u'argument foo\xb5 does not exist')
 
144
        except errors.CommandError:
 
145
            msg = _format_exception()
 
146
        expected = 'brz: ERROR: argument foo\xb5 does not exist\n'
 
147
        self.assertEqual(msg, expected)
113
148
 
114
149
    def test_format_exception(self):
115
 
        """Short formatting of bzr exceptions"""
 
150
        """Short formatting of brz exceptions"""
116
151
        try:
117
152
            raise errors.NotBranchError('wibble')
118
153
        except errors.NotBranchError:
119
 
            pass
120
 
        msg = _format_exception()
121
 
        self.assertTrue(len(msg) > 0)
122
 
        self.assertEqualDiff(msg, 'bzr: ERROR: Not a branch: \"wibble\".\n')
 
154
            msg = _format_exception()
 
155
        self.assertEqual(msg, 'brz: ERROR: Not a branch: \"wibble\".\n')
123
156
 
124
157
    def test_report_external_import_error(self):
125
158
        """Short friendly message for missing system modules."""
126
159
        try:
127
160
            import ImaginaryModule
128
 
        except ImportError, e:
129
 
            pass
 
161
        except ImportError:
 
162
            msg = _format_exception()
130
163
        else:
131
164
            self.fail("somehow succeeded in importing %r" % ImaginaryModule)
132
 
        msg = _format_exception()
133
 
        self.assertEqual(msg,
134
 
            'bzr: ERROR: No module named ImaginaryModule\n'
135
 
            'You may need to install this Python library separately.\n')
 
165
        self.assertContainsRe(
 
166
            msg,
 
167
            "^brz: ERROR: No module named '?ImaginaryModule'?\n"
 
168
            "You may need to install this Python library separately.\n$")
136
169
 
137
170
    def test_report_import_syntax_error(self):
138
171
        try:
139
172
            raise ImportError("syntax error")
140
 
        except ImportError, e:
141
 
            pass
142
 
        msg = _format_exception()
 
173
        except ImportError:
 
174
            msg = _format_exception()
143
175
        self.assertContainsRe(msg,
144
 
            r'Bazaar has encountered an internal error')
 
176
                              'Bazaar has encountered an internal error')
145
177
 
146
178
    def test_trace_unicode(self):
147
179
        """Write Unicode to trace log"""
162
194
        log = self.get_log()
163
195
        self.assertContainsRe(log, 'the unicode character')
164
196
 
 
197
    def test_trace_argument_exception(self):
 
198
        err = Exception('an error')
 
199
        mutter(u'can format stringable classes %s', err)
 
200
        log = self.get_log()
 
201
        self.assertContainsRe(log, 'can format stringable classes an error')
 
202
 
165
203
    def test_report_broken_pipe(self):
166
204
        try:
167
205
            raise IOError(errno.EPIPE, 'broken pipe foofofo')
168
 
        except IOError, e:
 
206
        except IOError:
169
207
            msg = _format_exception()
170
 
            self.assertEquals(msg, "bzr: broken pipe\n")
 
208
            self.assertEqual(msg, "brz: broken pipe\n")
171
209
        else:
172
210
            self.fail("expected error not raised")
173
211
 
174
 
    def assertLogStartsWith(self, log, string):
175
 
        """Like assertStartsWith, but skips the log timestamp."""
 
212
    def assertLogContainsLine(self, log, string):
 
213
        """Assert log contains a line including log timestamp."""
 
214
        # Does not check absolute position in log as there may be kipple.
176
215
        self.assertContainsRe(log,
177
 
            '^\\d+\\.\\d+  ' + re.escape(string))
 
216
                              '(?m)^\\d+\\.\\d+  ' + re.escape(string))
178
217
 
179
218
    def test_mutter_callsite_1(self):
180
219
        """mutter_callsite can capture 1 level of stack frame."""
181
220
        mutter_callsite(1, "foo %s", "a string")
182
221
        log = self.get_log()
183
222
        # begin with the message
184
 
        self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
 
223
        self.assertLogContainsLine(log, 'foo a string\nCalled from:\n')
185
224
        # should show two frame: this frame and the one above
186
 
        self.assertContainsRe(log,
187
 
            'test_trace\\.py", line \\d+, in test_mutter_callsite_1\n')
 
225
        self.assertContainsRe(
 
226
            log, 'test_trace\\.py", line \\d+, in test_mutter_callsite_1\n')
188
227
        # this frame should be the final one
189
228
        self.assertEndsWith(log, ' "a string")\n')
190
229
 
193
232
        mutter_callsite(2, "foo %s", "a string")
194
233
        log = self.get_log()
195
234
        # begin with the message
196
 
        self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
 
235
        self.assertLogContainsLine(log, 'foo a string\nCalled from:\n')
197
236
        # should show two frame: this frame and the one above
198
 
        self.assertContainsRe(log,
199
 
            'test_trace.py", line \d+, in test_mutter_callsite_2\n')
 
237
        self.assertContainsRe(
 
238
            log, 'test_trace.py", line \\d+, in test_mutter_callsite_2\n')
200
239
        # this frame should be the final one
201
240
        self.assertEndsWith(log, ' "a string")\n')
202
241
 
203
242
    def test_mutter_never_fails(self):
204
 
        # Even if the decode/encode stage fails, mutter should not
205
 
        # raise an exception
206
 
        # This test checks that mutter doesn't fail; the current behaviour
207
 
        # is that it doesn't fail *and writes non-utf8*.
208
 
        mutter(u'Writing a greek mu (\xb5) works in a unicode string')
209
 
        mutter('But fails in an ascii string \xb5')
210
 
        mutter('and in an ascii argument: %s', '\xb5')
 
243
        """Even with unencodable input mutter should not raise errors."""
 
244
        mutter(u'can write unicode \xa7')
 
245
        mutter('can interpolate unicode %s', u'\xa7')
 
246
        mutter(b'can write bytes \xa7')
 
247
        mutter('can repr bytes %r', b'\xa7')
 
248
        mutter('can interpolate bytes %s', b'\xa7')
 
249
        # Log will always be written as utf-8
211
250
        log = self.get_log()
212
 
        self.assertContainsRe(log, 'Writing a greek mu')
213
 
        self.assertContainsRe(log, "But fails in an ascii string")
214
 
        # However, the log content object does unicode replacement on reading
215
 
        # to let it get unicode back where good data has been written. So we
216
 
        # have to do a replaceent here as well.
217
 
        self.assertContainsRe(log, "ascii argument: \xb5".decode('utf8',
218
 
            'replace'))
219
 
        
 
251
        self.assertContainsRe(
 
252
            log,
 
253
            u'.* +can write unicode \xa7\n'
 
254
            u'.* +can interpolate unicode \xa7\n'
 
255
            u'.* +can write bytes \ufffd\n'
 
256
            u'.* +can repr bytes b\'\\\\xa7\'\n'
 
257
            u'.* +can interpolate bytes (?:\ufffd|b\'\\\\xa7\')\n')
 
258
 
220
259
    def test_show_error(self):
221
260
        show_error('error1')
222
261
        show_error(u'error2 \xb5 blah')
223
262
        show_error('arg: %s', 'blah')
224
 
        show_error('arg2: %(key)s', {'key':'stuff'})
 
263
        show_error('arg2: %(key)s', {'key': 'stuff'})
225
264
        try:
226
265
            raise Exception("oops")
227
 
        except:
 
266
        except BaseException:
228
267
            show_error('kwarg', exc_info=True)
229
268
        log = self.get_log()
230
269
        self.assertContainsRe(log, 'error1')
233
272
        self.assertContainsRe(log, 'arg2: stuff')
234
273
        self.assertContainsRe(log, 'kwarg')
235
274
        self.assertContainsRe(log, 'Traceback \\(most recent call last\\):')
236
 
        self.assertContainsRe(log, 'File ".*test_trace.py", line .*, in test_show_error')
 
275
        self.assertContainsRe(
 
276
            log, 'File ".*test_trace.py", line .*, in test_show_error')
237
277
        self.assertContainsRe(log, 'raise Exception\\("oops"\\)')
238
278
        self.assertContainsRe(log, 'Exception: oops')
239
279
 
262
302
            # as there's a timestamp at the front.
263
303
            tmp1.seek(0)
264
304
            self.assertContainsRe(tmp1.read(),
265
 
                r"\d+\.\d+  comment to file1\n\d+\.\d+  again to file1\n")
 
305
                                  b"\\d+\\.\\d+  comment to file1\n"
 
306
                                  b"\\d+\\.\\d+  again to file1\n")
266
307
            tmp2.seek(0)
267
308
            self.assertContainsRe(tmp2.read(),
268
 
                r"\d+\.\d+  comment to file2\n")
 
309
                                  b"\\d+\\.\\d+  comment to file2\n")
269
310
        finally:
270
311
            tmp1.close()
271
312
            tmp2.close()
272
313
 
273
 
    def test__open_bzr_log_uses_stderr_for_failures(self):
274
 
        # If _open_bzr_log cannot open the file, then we should write the
 
314
    def test__open_brz_log_uses_stderr_for_failures(self):
 
315
        # If _open_brz_log cannot open the file, then we should write the
275
316
        # warning to stderr. Since this is normally happening before logging is
276
317
        # set up.
277
318
        self.overrideAttr(sys, 'stderr', StringIO())
278
319
        # Set the log file to something that cannot exist
279
 
        # FIXME: A bit dangerous: we are not in an isolated dir here -- vilajam
280
 
        # 20100125
281
 
        os.environ['BZR_LOG'] = os.getcwd() + '/no-dir/bzr.log'
282
 
        self.overrideAttr(trace, '_bzr_log_filename')
283
 
        logf = trace._open_bzr_log()
284
 
        self.assertIs(None, logf)
285
 
        self.assertContainsRe(sys.stderr.getvalue(),
286
 
                              'failed to open trace file: .*/no-dir/bzr.log')
 
320
        self.overrideEnv('BRZ_LOG', '/no-such-dir/brz.log')
 
321
        self.overrideAttr(trace, '_brz_log_filename')
 
322
        logf = trace._open_brz_log()
 
323
        if os.path.isdir('/no-such-dir'):
 
324
            raise TestSkipped('directory creation succeeded')
 
325
        self.assertIs(None, logf)
 
326
        self.assertContainsRe(
 
327
            sys.stderr.getvalue(),
 
328
            "failed to open trace file: .* '/no-such-dir/brz.log'$")
 
329
 
 
330
    def test__open_brz_log_ignores_cache_dir_error(self):
 
331
        # If the cache directory can not be created and _open_brz_log can thus
 
332
        # not open the file, then we should write the warning to stderr. Since
 
333
        # this is normally happening before logging is set up.
 
334
        self.overrideAttr(sys, 'stderr', StringIO())
 
335
        # Set the cache directory to something that cannot exist
 
336
        self.overrideEnv('BRZ_LOG', None)
 
337
        self.overrideEnv('BRZ_HOME', '/no-such-dir')
 
338
        self.overrideEnv('XDG_CACHE_HOME', '/no-such-dir')
 
339
        self.overrideAttr(trace, '_brz_log_filename')
 
340
        logf = trace._open_brz_log()
 
341
        if os.path.isdir('/no-such-dir'):
 
342
            raise TestSkipped('directory creation succeeded')
 
343
        self.assertIs(None, logf)
 
344
        self.assertContainsRe(
 
345
            sys.stderr.getvalue(),
 
346
            "failed to open trace file: .* '/no-such-dir'$")
287
347
 
288
348
 
289
349
class TestVerbosityLevel(TestCase):
310
370
        self.assertEqual(0, get_verbosity_level())
311
371
 
312
372
 
 
373
class TestLogging(TestCase):
 
374
    """Check logging functionality robustly records information"""
 
375
 
 
376
    def test_note(self):
 
377
        trace.note("Noted")
 
378
        self.assertEqual("    INFO  Noted\n", self.get_log())
 
379
 
 
380
    def test_warning(self):
 
381
        trace.warning("Warned")
 
382
        self.assertEqual(" WARNING  Warned\n", self.get_log())
 
383
 
 
384
    def test_log(self):
 
385
        logging.getLogger("brz").error("Errored")
 
386
        self.assertEqual("   ERROR  Errored\n", self.get_log())
 
387
 
 
388
    def test_log_sub(self):
 
389
        logging.getLogger("brz.test_log_sub").debug("Whispered")
 
390
        self.assertEqual("   DEBUG  Whispered\n", self.get_log())
 
391
 
 
392
    def test_log_unicode_msg(self):
 
393
        logging.getLogger("brz").debug(u"\xa7")
 
394
        self.assertEqual(u"   DEBUG  \xa7\n", self.get_log())
 
395
 
 
396
    def test_log_unicode_arg(self):
 
397
        logging.getLogger("brz").debug("%s", u"\xa7")
 
398
        self.assertEqual(u"   DEBUG  \xa7\n", self.get_log())
 
399
 
 
400
    def test_log_utf8_msg(self):
 
401
        logging.getLogger("brz").debug(b"\xc2\xa7")
 
402
        self.assertEqual(u"   DEBUG  \xa7\n", self.get_log())
 
403
 
 
404
    def test_log_utf8_arg(self):
 
405
        logging.getLogger("brz").debug(b"%s", b"\xc2\xa7")
 
406
        expected = u"   DEBUG  b'\\xc2\\xa7'\n"
 
407
        self.assertEqual(expected, self.get_log())
 
408
 
 
409
    def test_log_bytes_msg(self):
 
410
        logging.getLogger("brz").debug(b"\xa7")
 
411
        log = self.get_log()
 
412
        self.assertContainsString(log, "UnicodeDecodeError: ")
 
413
        self.assertContainsRe(
 
414
            log, "Logging record unformattable: b?'\\\\xa7' % \\(\\)\n")
 
415
 
 
416
    def test_log_bytes_arg(self):
 
417
        logging.getLogger("brz").debug(b"%s", b"\xa7")
 
418
        log = self.get_log()
 
419
        self.assertEqual(u"   DEBUG  b'\\xa7'\n", self.get_log())
 
420
 
 
421
    def test_log_mixed_strings(self):
 
422
        logging.getLogger("brz").debug(u"%s", b"\xa7")
 
423
        log = self.get_log()
 
424
        self.assertEqual(u"   DEBUG  b'\\xa7'\n", self.get_log())
 
425
 
 
426
    def test_log_repr_broken(self):
 
427
        class BadRepr(object):
 
428
            def __repr__(self):
 
429
                raise ValueError("Broken object")
 
430
        logging.getLogger("brz").debug("%s", BadRepr())
 
431
        log = self.get_log()
 
432
        self.assertContainsRe(log, "ValueError: Broken object\n")
 
433
        self.assertContainsRe(log, "Logging record unformattable: '%s' % .*\n")
 
434
 
 
435
 
313
436
class TestBzrLog(TestCaseInTempDir):
314
437
 
315
438
    def test_log_rollover(self):
320
443
        _rollover_trace_maybe(temp_log_name)
321
444
        # should have been rolled over
322
445
        self.assertFalse(os.access(temp_log_name, os.R_OK))
 
446
 
 
447
 
 
448
class TestTraceConfiguration(TestCaseInTempDir):
 
449
 
 
450
    def test_default_config(self):
 
451
        config = trace.DefaultConfig()
 
452
        self.overrideAttr(trace, "_brz_log_filename", None)
 
453
        trace._brz_log_filename = None
 
454
        expected_filename = trace._get_brz_log_filename()
 
455
        self.assertEqual(None, trace._brz_log_filename)
 
456
        config.__enter__()
 
457
        try:
 
458
            # Should have entered and setup a default filename.
 
459
            self.assertEqual(expected_filename, trace._brz_log_filename)
 
460
        finally:
 
461
            config.__exit__(None, None, None)
 
462
            # Should have exited and cleaned up.
 
463
            self.assertEqual(None, trace._brz_log_filename)