~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_trace.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-23 23:54:45 UTC
  • Revision ID: mbp@sourcefrog.net-20050323235445-a185b1b1f2a86dfe
mention "info" in top-level help

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
# "weren't nothing promised to you.  do i look like i got a promise face?"
18
 
 
19
 
"""Tests for trace library"""
20
 
 
21
 
from cStringIO import StringIO
22
 
import errno
23
 
import os
24
 
import re
25
 
import sys
26
 
import tempfile
27
 
 
28
 
from bzrlib import (
29
 
    errors,
30
 
    )
31
 
from bzrlib.tests import TestCaseInTempDir, TestCase
32
 
from bzrlib.trace import (
33
 
    mutter, mutter_callsite, report_exception,
34
 
    set_verbosity_level, get_verbosity_level, is_quiet, is_verbose, be_quiet,
35
 
    pop_log_file,
36
 
    push_log_file,
37
 
    _rollover_trace_maybe,
38
 
    )
39
 
 
40
 
 
41
 
def _format_exception():
42
 
    """Format an exception as it would normally be displayed to the user"""
43
 
    buf = StringIO()
44
 
    report_exception(sys.exc_info(), buf)
45
 
    return buf.getvalue()
46
 
 
47
 
 
48
 
class TestTrace(TestCase):
49
 
 
50
 
    def test_format_sys_exception(self):
51
 
        try:
52
 
            raise NotImplementedError, "time travel"
53
 
        except NotImplementedError:
54
 
            pass
55
 
        err = _format_exception()
56
 
        self.assertEqualDiff(err.splitlines()[0],
57
 
                'bzr: ERROR: exceptions.NotImplementedError: time travel')
58
 
        self.assertContainsRe(err,
59
 
                r'File.*test_trace.py')
60
 
 
61
 
    def test_format_interrupt_exception(self):
62
 
        try:
63
 
            raise KeyboardInterrupt()
64
 
        except KeyboardInterrupt:
65
 
            # XXX: Some risk that a *real* keyboard interrupt won't be seen
66
 
            pass
67
 
        msg = _format_exception()
68
 
        self.assertTrue(len(msg) > 0)
69
 
        self.assertEqualDiff(msg, 'bzr: interrupted\n')
70
 
 
71
 
    def test_format_os_error(self):
72
 
        try:
73
 
            os.rmdir('nosuchfile22222')
74
 
        except OSError:
75
 
            pass
76
 
        msg = _format_exception()
77
 
        self.assertContainsRe(msg,
78
 
            r'^bzr: ERROR: \[Errno .*\] No such file.*nosuchfile22222')
79
 
 
80
 
    def test_format_io_error(self):
81
 
        try:
82
 
            file('nosuchfile22222')
83
 
        except IOError:
84
 
            pass
85
 
        msg = _format_exception()
86
 
        self.assertContainsRe(msg, r'^bzr: ERROR: \[Errno .*\] No such file.*nosuchfile')
87
 
 
88
 
    def test_format_unicode_error(self):
89
 
        try:
90
 
            raise errors.BzrCommandError(u'argument foo\xb5 does not exist')
91
 
        except errors.BzrCommandError:
92
 
            pass
93
 
        msg = _format_exception()
94
 
 
95
 
    def test_format_exception(self):
96
 
        """Short formatting of bzr exceptions"""
97
 
        try:
98
 
            raise errors.NotBranchError('wibble')
99
 
        except errors.NotBranchError:
100
 
            pass
101
 
        msg = _format_exception()
102
 
        self.assertTrue(len(msg) > 0)
103
 
        self.assertEqualDiff(msg, 'bzr: ERROR: Not a branch: \"wibble\".\n')
104
 
 
105
 
    def test_report_external_import_error(self):
106
 
        """Short friendly message for missing system modules."""
107
 
        try:
108
 
            import ImaginaryModule
109
 
        except ImportError, e:
110
 
            pass
111
 
        else:
112
 
            self.fail("somehow succeeded in importing %r" % ImaginaryModule)
113
 
        msg = _format_exception()
114
 
        self.assertEqual(msg,
115
 
            'bzr: ERROR: No module named ImaginaryModule\n'
116
 
            'You may need to install this Python library separately.\n')
117
 
 
118
 
    def test_report_import_syntax_error(self):
119
 
        try:
120
 
            raise ImportError("syntax error")
121
 
        except ImportError, e:
122
 
            pass
123
 
        msg = _format_exception()
124
 
        self.assertContainsRe(msg,
125
 
            r"Traceback \(most recent call last\)")
126
 
 
127
 
    def test_trace_unicode(self):
128
 
        """Write Unicode to trace log"""
129
 
        self.log(u'the unicode character for benzene is \N{BENZENE RING}')
130
 
        self.assertContainsRe(self._get_log(keep_log_file=True),
131
 
                              "the unicode character for benzene is")
132
 
 
133
 
    def test_trace_argument_unicode(self):
134
 
        """Write a Unicode argument to the trace log"""
135
 
        mutter(u'the unicode character for benzene is %s', u'\N{BENZENE RING}')
136
 
        self.assertContainsRe(self._get_log(keep_log_file=True),
137
 
                              'the unicode character')
138
 
 
139
 
    def test_trace_argument_utf8(self):
140
 
        """Write a Unicode argument to the trace log"""
141
 
        mutter(u'the unicode character for benzene is %s',
142
 
               u'\N{BENZENE RING}'.encode('utf-8'))
143
 
        self.assertContainsRe(self._get_log(keep_log_file=True),
144
 
                              'the unicode character')
145
 
 
146
 
    def test_report_broken_pipe(self):
147
 
        try:
148
 
            raise IOError(errno.EPIPE, 'broken pipe foofofo')
149
 
        except IOError, e:
150
 
            msg = _format_exception()
151
 
            self.assertEquals(msg, "bzr: broken pipe\n")
152
 
        else:
153
 
            self.fail("expected error not raised")
154
 
 
155
 
    def assertLogStartsWith(self, log, string):
156
 
        """Like assertStartsWith, but skips the log timestamp."""
157
 
        self.assertContainsRe(log,
158
 
            '^\\d+\\.\\d+  ' + re.escape(string))
159
 
 
160
 
    def test_mutter_callsite_1(self):
161
 
        """mutter_callsite can capture 1 level of stack frame."""
162
 
        mutter_callsite(1, "foo %s", "a string")
163
 
        log = self._get_log(keep_log_file=True)
164
 
        # begin with the message
165
 
        self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
166
 
        # should show two frame: this frame and the one above
167
 
        self.assertContainsRe(log,
168
 
            'test_trace\\.py", line \\d+, in test_mutter_callsite_1\n')
169
 
        # this frame should be the final one
170
 
        self.assertEndsWith(log, ' "a string")\n')
171
 
 
172
 
    def test_mutter_callsite_2(self):
173
 
        """mutter_callsite can capture 2 levels of stack frame."""
174
 
        mutter_callsite(2, "foo %s", "a string")
175
 
        log = self._get_log(keep_log_file=True)
176
 
        # begin with the message
177
 
        self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
178
 
        # should show two frame: this frame and the one above
179
 
        self.assertContainsRe(log,
180
 
            'test_trace.py", line \d+, in test_mutter_callsite_2\n')
181
 
        # this frame should be the final one
182
 
        self.assertEndsWith(log, ' "a string")\n')
183
 
 
184
 
    def test_mutter_never_fails(self):
185
 
        # Even if the decode/encode stage fails, mutter should not
186
 
        # raise an exception
187
 
        mutter(u'Writing a greek mu (\xb5) works in a unicode string')
188
 
        mutter('But fails in an ascii string \xb5')
189
 
        mutter('and in an ascii argument: %s', '\xb5')
190
 
        log = self._get_log(keep_log_file=True)
191
 
        self.assertContainsRe(log, 'Writing a greek mu')
192
 
        self.assertContainsRe(log, "But fails in an ascii string")
193
 
        self.assertContainsRe(log, u"ascii argument: \xb5")
194
 
 
195
 
    def test_push_log_file(self):
196
 
        """Can push and pop log file, and this catches mutter messages.
197
 
 
198
 
        This is primarily for use in the test framework.
199
 
        """
200
 
        tmp1 = tempfile.NamedTemporaryFile()
201
 
        tmp2 = tempfile.NamedTemporaryFile()
202
 
        try:
203
 
            memento1 = push_log_file(tmp1)
204
 
            mutter("comment to file1")
205
 
            try:
206
 
                memento2 = push_log_file(tmp2)
207
 
                try:
208
 
                    mutter("comment to file2")
209
 
                finally:
210
 
                    pop_log_file(memento2)
211
 
                mutter("again to file1")
212
 
            finally:
213
 
                pop_log_file(memento1)
214
 
            # the files were opened in binary mode, so should have exactly
215
 
            # these bytes.  and removing the file as the log target should
216
 
            # have caused them to be flushed out.  need to match using regexps
217
 
            # as there's a timestamp at the front.
218
 
            tmp1.seek(0)
219
 
            self.assertContainsRe(tmp1.read(),
220
 
                r"\d+\.\d+  comment to file1\n\d+\.\d+  again to file1\n")
221
 
            tmp2.seek(0)
222
 
            self.assertContainsRe(tmp2.read(),
223
 
                r"\d+\.\d+  comment to file2\n")
224
 
        finally:
225
 
            tmp1.close()
226
 
            tmp2.close()
227
 
 
228
 
 
229
 
class TestVerbosityLevel(TestCase):
230
 
 
231
 
    def test_verbosity_level(self):
232
 
        set_verbosity_level(1)
233
 
        self.assertEqual(1, get_verbosity_level())
234
 
        self.assertTrue(is_verbose())
235
 
        self.assertFalse(is_quiet())
236
 
        set_verbosity_level(-1)
237
 
        self.assertEqual(-1, get_verbosity_level())
238
 
        self.assertFalse(is_verbose())
239
 
        self.assertTrue(is_quiet())
240
 
        set_verbosity_level(0)
241
 
        self.assertEqual(0, get_verbosity_level())
242
 
        self.assertFalse(is_verbose())
243
 
        self.assertFalse(is_quiet())
244
 
 
245
 
    def test_be_quiet(self):
246
 
        # Confirm the old API still works
247
 
        be_quiet(True)
248
 
        self.assertEqual(-1, get_verbosity_level())
249
 
        be_quiet(False)
250
 
        self.assertEqual(0, get_verbosity_level())
251
 
 
252
 
 
253
 
class TestBzrLog(TestCaseInTempDir):
254
 
 
255
 
    def test_log_rollover(self):
256
 
        temp_log_name = 'test-log'
257
 
        trace_file = open(temp_log_name, 'at')
258
 
        trace_file.write('test_log_rollover padding\n' * 1000000)
259
 
        trace_file.close()
260
 
        _rollover_trace_maybe(temp_log_name)
261
 
        # should have been rolled over
262
 
        self.assertFalse(os.access(temp_log_name, os.R_OK))