Merge lp:~mbp/bzr/417881-selftest-no-apport into lp:bzr
- 417881-selftest-no-apport
- Merge into bzr.dev
Proposed by
Martin Pool
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~mbp/bzr/417881-selftest-no-apport | ||||
Merge into: | lp:bzr | ||||
Diff against target: |
534 lines (+296/-73) 9 files modified
NEWS (+15/-0) bzrlib/builtins.py (+7/-0) bzrlib/config.py (+7/-3) bzrlib/crash.py (+143/-55) bzrlib/tests/__init__.py (+5/-0) bzrlib/tests/test_crash.py (+46/-15) contrib/apport/README (+13/-0) contrib/apport/bzr.conf (+6/-0) contrib/apport/source_bzr.py (+54/-0) |
||||
To merge this branch: | bzr merge lp:~mbp/bzr/417881-selftest-no-apport | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
bzr-core | Pending | ||
Review via email: mp+18489@code.launchpad.net |
This proposal has been superseded by a proposal from 2010-02-03.
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2010-02-02 10:38:47 +0000 | |||
3 | +++ NEWS 2010-02-03 00:05:22 +0000 | |||
4 | @@ -19,6 +19,21 @@ | |||
5 | 19 | New Features | 19 | New Features |
6 | 20 | ************ | 20 | ************ |
7 | 21 | 21 | ||
8 | 22 | * If the Apport crash-reporting tool is available, bzr crashes are now | ||
9 | 23 | stored into the ``/var/crash`` apport spool directory, and the user is | ||
10 | 24 | invited to report them to the developers from there, either | ||
11 | 25 | automatically or by running ``apport-bug``. No information is sent | ||
12 | 26 | without specific permission from the user. (Martin Pool, #515052) | ||
13 | 27 | |||
14 | 28 | Testing | ||
15 | 29 | ******* | ||
16 | 30 | |||
17 | 31 | * Don't use Apport for errors while loading or running tests. | ||
18 | 32 | (Martin Pool, #417881) | ||
19 | 33 | |||
20 | 34 | * Stop sending apport crash files to ``.cache`` in the directory from | ||
21 | 35 | which ``bzr selftest`` was run. (Martin Pool, #422350) | ||
22 | 36 | |||
23 | 22 | bzr 2.1.0 (not released yet) | 37 | bzr 2.1.0 (not released yet) |
24 | 23 | ############################ | 38 | ############################ |
25 | 24 | 39 | ||
26 | 25 | 40 | ||
27 | === modified file 'bzrlib/builtins.py' | |||
28 | --- bzrlib/builtins.py 2010-01-29 10:36:23 +0000 | |||
29 | +++ bzrlib/builtins.py 2010-02-03 00:05:22 +0000 | |||
30 | @@ -3524,6 +3524,13 @@ | |||
31 | 3524 | 3524 | ||
32 | 3525 | # Make deprecation warnings visible, unless -Werror is set | 3525 | # Make deprecation warnings visible, unless -Werror is set |
33 | 3526 | symbol_versioning.activate_deprecation_warnings(override=False) | 3526 | symbol_versioning.activate_deprecation_warnings(override=False) |
34 | 3527 | |||
35 | 3528 | # Whatever you normally want, for the purposes of running selftest you | ||
36 | 3529 | # probably actually want to see the exception, not to turn it into an | ||
37 | 3530 | # apport failure. This is specifically turned off again for tests of | ||
38 | 3531 | # apport. We turn it off here so that eg a SyntaxError loading the | ||
39 | 3532 | # tests is caught. | ||
40 | 3533 | os.environ['APPORT_DISABLE'] = '1' | ||
41 | 3527 | 3534 | ||
42 | 3528 | if cache_dir is not None: | 3535 | if cache_dir is not None: |
43 | 3529 | tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir) | 3536 | tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir) |
44 | 3530 | 3537 | ||
45 | === modified file 'bzrlib/config.py' | |||
46 | --- bzrlib/config.py 2009-12-17 10:01:25 +0000 | |||
47 | +++ bzrlib/config.py 2010-02-03 00:05:22 +0000 | |||
48 | @@ -1,4 +1,4 @@ | |||
50 | 1 | # Copyright (C) 2005, 2007, 2008 Canonical Ltd | 1 | # Copyright (C) 2005, 2007, 2008, 2010 Canonical Ltd |
51 | 2 | # Authors: Robert Collins <robert.collins@canonical.com> | 2 | # Authors: Robert Collins <robert.collins@canonical.com> |
52 | 3 | # and others | 3 | # and others |
53 | 4 | # | 4 | # |
54 | @@ -866,12 +866,16 @@ | |||
55 | 866 | 866 | ||
56 | 867 | This doesn't implicitly create it. | 867 | This doesn't implicitly create it. |
57 | 868 | 868 | ||
59 | 869 | On Windows it's in the config directory; elsewhere in the XDG cache directory. | 869 | On Windows it's in the config directory; elsewhere it's /var/crash |
60 | 870 | which may be monitored by apport. It can be overridden by | ||
61 | 871 | $APPORT_CRASH_DIR. | ||
62 | 870 | """ | 872 | """ |
63 | 871 | if sys.platform == 'win32': | 873 | if sys.platform == 'win32': |
64 | 872 | return osutils.pathjoin(config_dir(), 'Crash') | 874 | return osutils.pathjoin(config_dir(), 'Crash') |
65 | 873 | else: | 875 | else: |
67 | 874 | return osutils.pathjoin(xdg_cache_dir(), 'crash') | 876 | # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp |
68 | 877 | # 2010-01-31 | ||
69 | 878 | return os.environ.get('APPORT_CRASH_DIR', '/var/crash') | ||
70 | 875 | 879 | ||
71 | 876 | 880 | ||
72 | 877 | def xdg_cache_dir(): | 881 | def xdg_cache_dir(): |
73 | 878 | 882 | ||
74 | === modified file 'bzrlib/crash.py' | |||
75 | --- bzrlib/crash.py 2009-08-20 05:47:53 +0000 | |||
76 | +++ bzrlib/crash.py 2010-02-03 00:05:22 +0000 | |||
77 | @@ -1,4 +1,4 @@ | |||
79 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
80 | 2 | # | 2 | # |
81 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
82 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
83 | @@ -16,10 +16,32 @@ | |||
84 | 16 | 16 | ||
85 | 17 | 17 | ||
86 | 18 | """Handling and reporting crashes. | 18 | """Handling and reporting crashes. |
87 | 19 | |||
88 | 20 | A crash is an exception propagated up almost to the top level of Bazaar. | ||
89 | 21 | |||
90 | 22 | If we have apport <https://launchpad.net/apport/>, we store a report of the | ||
91 | 23 | crash using apport into it's /var/crash spool directory, from where the user | ||
92 | 24 | can either manually send it to Launchpad. In some cases (at least Ubuntu | ||
93 | 25 | development releases), Apport may pop up a window asking if they want | ||
94 | 26 | to send it. | ||
95 | 27 | |||
96 | 28 | Without apport, we just write a crash report to stderr and the user can report | ||
97 | 29 | this manually if the wish. | ||
98 | 30 | |||
99 | 31 | We never send crash data across the network without user opt-in. | ||
100 | 32 | |||
101 | 33 | In principle apport can run on any platform though as of Feb 2010 there seem | ||
102 | 34 | to be some portability bugs. | ||
103 | 35 | |||
104 | 36 | To force this off in bzr turn set APPORT_DISBLE in the environment or | ||
105 | 37 | -Dno_apport. | ||
106 | 19 | """ | 38 | """ |
107 | 20 | 39 | ||
108 | 21 | # for interactive testing, try the 'bzr assert-fail' command | 40 | # for interactive testing, try the 'bzr assert-fail' command |
109 | 22 | # or see http://code.launchpad.net/~mbp/bzr/bzr-fail | 41 | # or see http://code.launchpad.net/~mbp/bzr/bzr-fail |
110 | 42 | # | ||
111 | 43 | # to test with apport it's useful to set | ||
112 | 44 | # export APPORT_IGNORE_OBSOLETE_PACKAGES=1 | ||
113 | 23 | 45 | ||
114 | 24 | import os | 46 | import os |
115 | 25 | import platform | 47 | import platform |
116 | @@ -39,39 +61,42 @@ | |||
117 | 39 | 61 | ||
118 | 40 | 62 | ||
119 | 41 | def report_bug(exc_info, stderr): | 63 | def report_bug(exc_info, stderr): |
123 | 42 | if 'no_apport' not in debug.debug_flags: | 64 | if ('no_apport' in debug.debug_flags) or \ |
124 | 43 | try: | 65 | os.environ.get('APPORT_DISABLE', None): |
125 | 44 | report_bug_to_apport(exc_info, stderr) | 66 | return report_bug_legacy(exc_info, stderr) |
126 | 67 | try: | ||
127 | 68 | if report_bug_to_apport(exc_info, stderr): | ||
128 | 69 | # wrote a file; if None then report the old way | ||
129 | 45 | return | 70 | return |
140 | 46 | except ImportError, e: | 71 | except ImportError, e: |
141 | 47 | trace.mutter("couldn't find apport bug-reporting library: %s" % e) | 72 | trace.mutter("couldn't find apport bug-reporting library: %s" % e) |
142 | 48 | pass | 73 | pass |
143 | 49 | except Exception, e: | 74 | except Exception, e: |
144 | 50 | # this should only happen if apport is installed but it didn't | 75 | # this should only happen if apport is installed but it didn't |
145 | 51 | # work, eg because of an io error writing the crash file | 76 | # work, eg because of an io error writing the crash file |
146 | 52 | sys.stderr.write("bzr: failed to report crash using apport:\n " | 77 | stderr.write("bzr: failed to report crash using apport:\n " |
147 | 53 | " %r\n" % e) | 78 | " %r\n" % e) |
148 | 54 | pass | 79 | pass |
149 | 55 | report_bug_legacy(exc_info, stderr) | 80 | return report_bug_legacy(exc_info, stderr) |
150 | 56 | 81 | ||
151 | 57 | 82 | ||
152 | 58 | def report_bug_legacy(exc_info, err_file): | 83 | def report_bug_legacy(exc_info, err_file): |
153 | 59 | """Report a bug by just printing a message to the user.""" | 84 | """Report a bug by just printing a message to the user.""" |
154 | 60 | trace.print_exception(exc_info, err_file) | ||
155 | 61 | err_file.write('\n') | ||
156 | 62 | err_file.write('bzr %s on python %s (%s)\n' % \ | 85 | err_file.write('bzr %s on python %s (%s)\n' % \ |
157 | 63 | (bzrlib.__version__, | 86 | (bzrlib.__version__, |
158 | 64 | bzrlib._format_version_tuple(sys.version_info), | 87 | bzrlib._format_version_tuple(sys.version_info), |
159 | 65 | platform.platform(aliased=1))) | 88 | platform.platform(aliased=1))) |
160 | 89 | err_file.write("plugins:\n") | ||
161 | 90 | err_file.write(_format_plugin_list()) | ||
162 | 66 | err_file.write('arguments: %r\n' % sys.argv) | 91 | err_file.write('arguments: %r\n' % sys.argv) |
163 | 67 | err_file.write( | 92 | err_file.write( |
164 | 68 | 'encoding: %r, fsenc: %r, lang: %r\n' % ( | 93 | 'encoding: %r, fsenc: %r, lang: %r\n' % ( |
165 | 69 | osutils.get_user_encoding(), sys.getfilesystemencoding(), | 94 | osutils.get_user_encoding(), sys.getfilesystemencoding(), |
166 | 70 | os.environ.get('LANG'))) | 95 | os.environ.get('LANG'))) |
169 | 71 | err_file.write("plugins:\n") | 96 | err_file.write('\n') |
170 | 72 | err_file.write(_format_plugin_list()) | 97 | trace.print_exception(exc_info, err_file) |
171 | 73 | err_file.write( | 98 | err_file.write( |
173 | 74 | "\n\n" | 99 | "\n" |
174 | 75 | "*** Bazaar has encountered an internal error. This probably indicates a\n" | 100 | "*** Bazaar has encountered an internal error. This probably indicates a\n" |
175 | 76 | " bug in Bazaar. You can help us fix it by filing a bug report at\n" | 101 | " bug in Bazaar. You can help us fix it by filing a bug report at\n" |
176 | 77 | " https://bugs.launchpad.net/bzr/+filebug\n" | 102 | " https://bugs.launchpad.net/bzr/+filebug\n" |
177 | @@ -81,47 +106,54 @@ | |||
178 | 81 | 106 | ||
179 | 82 | def report_bug_to_apport(exc_info, stderr): | 107 | def report_bug_to_apport(exc_info, stderr): |
180 | 83 | """Report a bug to apport for optional automatic filing. | 108 | """Report a bug to apport for optional automatic filing. |
181 | 109 | |||
182 | 110 | :returns: The name of the crash file, or None if we didn't write one. | ||
183 | 84 | """ | 111 | """ |
185 | 85 | # this is based on apport_package_hook.py, but omitting some of the | 112 | # this function is based on apport_package_hook.py, but omitting some of the |
186 | 86 | # Ubuntu-specific policy about what to report and when | 113 | # Ubuntu-specific policy about what to report and when |
187 | 87 | 114 | ||
190 | 88 | # if this fails its caught at a higher level; we don't want to open the | 115 | # if the import fails, the exception will be caught at a higher level and |
191 | 89 | # crash file unless apport can be loaded. | 116 | # we'll report the error by other means |
192 | 90 | import apport | 117 | import apport |
193 | 91 | 118 | ||
216 | 92 | crash_file = _open_crash_file() | 119 | crash_filename = _write_apport_report_to_file(exc_info) |
217 | 93 | try: | 120 | |
218 | 94 | _write_apport_report_to_file(exc_info, crash_file) | 121 | if crash_filename is None: |
219 | 95 | finally: | 122 | stderr.write("\n" |
220 | 96 | crash_file.close() | 123 | "apport is set to ignore crashes in this version of bzr.\n" |
221 | 97 | 124 | ) | |
222 | 98 | stderr.write("bzr: ERROR: %s.%s: %s\n" | 125 | else: |
223 | 99 | "\n" | 126 | trace.print_exception(exc_info, stderr) |
224 | 100 | "*** Bazaar has encountered an internal error. This probably indicates a\n" | 127 | stderr.write("\n" |
225 | 101 | " bug in Bazaar. You can help us fix it by filing a bug report at\n" | 128 | "You can report this problem to Bazaar's developers by running\n" |
226 | 102 | " https://bugs.launchpad.net/bzr/+filebug\n" | 129 | " apport-bug %s\n" |
227 | 103 | " attaching the crash file\n" | 130 | "if a bug-reporting window does not automatically appear.\n" |
228 | 104 | " %s\n" | 131 | % (crash_filename)) |
229 | 105 | " and including a description of the problem.\n" | 132 | # XXX: on Windows, Mac, and other platforms where we might have the |
230 | 106 | "\n" | 133 | # apport libraries but not have an apport always running, we could |
231 | 107 | " The crash file is plain text and you can inspect or edit it to remove\n" | 134 | # synchronously file now |
232 | 108 | " private information.\n" | 135 | |
233 | 109 | % (exc_info[0].__module__, exc_info[0].__name__, exc_info[1], | 136 | return crash_filename |
234 | 110 | crash_file.name)) | 137 | |
235 | 111 | 138 | ||
236 | 112 | 139 | def _write_apport_report_to_file(exc_info): | |
215 | 113 | def _write_apport_report_to_file(exc_info, crash_file): | ||
237 | 114 | import traceback | 140 | import traceback |
238 | 115 | from apport.report import Report | 141 | from apport.report import Report |
239 | 116 | 142 | ||
240 | 117 | exc_type, exc_object, exc_tb = exc_info | 143 | exc_type, exc_object, exc_tb = exc_info |
241 | 118 | 144 | ||
242 | 119 | pr = Report() | 145 | pr = Report() |
247 | 120 | # add_proc_info gives you the memory map of the process: this seems rarely | 146 | # add_proc_info gets the executable and interpreter path, which is needed, |
248 | 121 | # useful for Bazaar and it does make the report harder to scan, though it | 147 | # plus some less useful stuff like the memory map |
249 | 122 | # does tell you what binary modules are loaded. | 148 | pr.add_proc_info() |
246 | 123 | # pr.add_proc_info() | ||
250 | 124 | pr.add_user_info() | 149 | pr.add_user_info() |
251 | 150 | |||
252 | 151 | # Package and SourcePackage are needed so that apport will report about even | ||
253 | 152 | # non-packaged versions of bzr; also this reports on their packaged | ||
254 | 153 | # dependencies which is useful. | ||
255 | 154 | pr['SourcePackage'] = 'bzr' | ||
256 | 155 | pr['Package'] = 'bzr' | ||
257 | 156 | |||
258 | 125 | pr['CommandLine'] = pprint.pformat(sys.argv) | 157 | pr['CommandLine'] = pprint.pformat(sys.argv) |
259 | 126 | pr['BzrVersion'] = bzrlib.__version__ | 158 | pr['BzrVersion'] = bzrlib.__version__ |
260 | 127 | pr['PythonVersion'] = bzrlib._format_version_tuple(sys.version_info) | 159 | pr['PythonVersion'] = bzrlib._format_version_tuple(sys.version_info) |
261 | @@ -137,18 +169,74 @@ | |||
262 | 137 | traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file) | 169 | traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file) |
263 | 138 | pr['Traceback'] = tb_file.getvalue() | 170 | pr['Traceback'] = tb_file.getvalue() |
264 | 139 | 171 | ||
266 | 140 | pr.write(crash_file) | 172 | _attach_log_tail(pr) |
267 | 173 | |||
268 | 174 | # We want to use the 'bzr' crashdb so that it gets sent directly upstream, | ||
269 | 175 | # which is a reasonable default for most internal errors. However, if we | ||
270 | 176 | # set it here then apport will crash later if it doesn't know about that | ||
271 | 177 | # crashdb. Instead, we rely on the bzr package installing both a | ||
272 | 178 | # source hook telling crashes to go to this crashdb, and a crashdb | ||
273 | 179 | # configuration describing it. | ||
274 | 180 | |||
275 | 181 | # these may contain some sensitive info (smtp_passwords) | ||
276 | 182 | # TODO: strip that out and attach the rest | ||
277 | 183 | # | ||
278 | 184 | #attach_file_if_exists(report, | ||
279 | 185 | # os.path.join(dot_bzr, 'bazaar.conf', 'BzrConfig') | ||
280 | 186 | #attach_file_if_exists(report, | ||
281 | 187 | # os.path.join(dot_bzr, 'locations.conf', 'BzrLocations') | ||
282 | 188 | |||
283 | 189 | # strip username, hostname, etc | ||
284 | 190 | pr.anonymize() | ||
285 | 191 | |||
286 | 192 | if pr.check_ignored(): | ||
287 | 193 | # eg configured off in ~/.apport-ignore.xml | ||
288 | 194 | return None | ||
289 | 195 | else: | ||
290 | 196 | crash_file_name, crash_file = _open_crash_file() | ||
291 | 197 | pr.write(crash_file) | ||
292 | 198 | crash_file.close() | ||
293 | 199 | return crash_file_name | ||
294 | 200 | |||
295 | 201 | |||
296 | 202 | def _attach_log_tail(pr): | ||
297 | 203 | try: | ||
298 | 204 | bzr_log = open(trace._get_bzr_log_filename(), 'rt') | ||
299 | 205 | except (IOError, OSError), e: | ||
300 | 206 | pr['BzrLogTail'] = repr(e) | ||
301 | 207 | return | ||
302 | 208 | try: | ||
303 | 209 | lines = bzr_log.readlines() | ||
304 | 210 | pr['BzrLogTail'] = ''.join(lines[-40:]) | ||
305 | 211 | finally: | ||
306 | 212 | bzr_log.close() | ||
307 | 141 | 213 | ||
308 | 142 | 214 | ||
309 | 143 | def _open_crash_file(): | 215 | def _open_crash_file(): |
310 | 144 | crash_dir = config.crash_dir() | 216 | crash_dir = config.crash_dir() |
311 | 145 | # user-readable only, just in case the contents are sensitive. | ||
312 | 146 | if not osutils.isdir(crash_dir): | 217 | if not osutils.isdir(crash_dir): |
318 | 147 | os.makedirs(crash_dir, mode=0700) | 218 | # on unix this should be /var/crash and should already exist; on |
319 | 148 | filename = 'bzr-%s-%s.crash' % ( | 219 | # Windows or if it's manually configured it might need to be created, |
320 | 149 | osutils.compact_date(time.time()), | 220 | # and then it should be private |
321 | 150 | os.getpid(),) | 221 | os.makedirs(crash_dir, mode=0600) |
322 | 151 | return open(osutils.pathjoin(crash_dir, filename), 'wt') | 222 | date_string = time.strftime('%Y-%m-%dT%H:%M', time.gmtime()) |
323 | 223 | # XXX: getuid doesn't work on win32, but the crash directory is per-user | ||
324 | 224 | if sys.platform == 'win32': | ||
325 | 225 | user_part = '' | ||
326 | 226 | else: | ||
327 | 227 | user_part = '.%d' % os.getuid() | ||
328 | 228 | filename = osutils.pathjoin( | ||
329 | 229 | crash_dir, | ||
330 | 230 | 'bzr%s.%s.crash' % ( | ||
331 | 231 | user_part, | ||
332 | 232 | date_string)) | ||
333 | 233 | # be careful here that people can't play tmp-type symlink mischief in the | ||
334 | 234 | # world-writable directory | ||
335 | 235 | return filename, os.fdopen( | ||
336 | 236 | os.open(filename, | ||
337 | 237 | os.O_WRONLY|os.O_CREAT|os.O_EXCL, | ||
338 | 238 | 0600), | ||
339 | 239 | 'w') | ||
340 | 152 | 240 | ||
341 | 153 | 241 | ||
342 | 154 | def _format_plugin_list(): | 242 | def _format_plugin_list(): |
343 | 155 | 243 | ||
344 | === modified file 'bzrlib/tests/__init__.py' | |||
345 | --- bzrlib/tests/__init__.py 2010-01-25 17:48:22 +0000 | |||
346 | +++ bzrlib/tests/__init__.py 2010-02-03 00:05:22 +0000 | |||
347 | @@ -1540,6 +1540,11 @@ | |||
348 | 1540 | 'ftp_proxy': None, | 1540 | 'ftp_proxy': None, |
349 | 1541 | 'FTP_PROXY': None, | 1541 | 'FTP_PROXY': None, |
350 | 1542 | 'BZR_REMOTE_PATH': None, | 1542 | 'BZR_REMOTE_PATH': None, |
351 | 1543 | # Generally speaking, we don't want apport reporting on crashes in | ||
352 | 1544 | # the test envirnoment unless we're specifically testing apport, | ||
353 | 1545 | # so that it doesn't leak into the real system environment. We | ||
354 | 1546 | # use an env var so it propagates to subprocesses. | ||
355 | 1547 | 'APPORT_DISABLE': '1', | ||
356 | 1543 | } | 1548 | } |
357 | 1544 | self.__old_env = {} | 1549 | self.__old_env = {} |
358 | 1545 | self.addCleanup(self._restoreEnvironment) | 1550 | self.addCleanup(self._restoreEnvironment) |
359 | 1546 | 1551 | ||
360 | === modified file 'bzrlib/tests/test_crash.py' | |||
361 | --- bzrlib/tests/test_crash.py 2009-12-22 15:39:20 +0000 | |||
362 | +++ bzrlib/tests/test_crash.py 2010-02-03 00:05:22 +0000 | |||
363 | @@ -1,4 +1,4 @@ | |||
365 | 1 | # Copyright (C) 2009 Canonical Ltd | 1 | # Copyright (C) 2009, 2010 Canonical Ltd |
366 | 2 | # | 2 | # |
367 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
368 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
369 | @@ -18,31 +18,62 @@ | |||
370 | 18 | from StringIO import StringIO | 18 | from StringIO import StringIO |
371 | 19 | import sys | 19 | import sys |
372 | 20 | 20 | ||
373 | 21 | |||
374 | 22 | import os | ||
375 | 23 | |||
376 | 24 | |||
377 | 21 | from bzrlib import ( | 25 | from bzrlib import ( |
378 | 26 | config, | ||
379 | 22 | crash, | 27 | crash, |
380 | 23 | tests, | 28 | tests, |
387 | 24 | ) | 29 | osutils, |
388 | 25 | 30 | ) | |
389 | 26 | from bzrlib.tests import features | 31 | |
390 | 27 | 32 | from bzrlib.tests import ( | |
391 | 28 | 33 | features, | |
392 | 29 | class TestApportReporting(tests.TestCase): | 34 | TestCaseInTempDir, |
393 | 35 | ) | ||
394 | 36 | from bzrlib.tests.features import ApportFeature | ||
395 | 37 | |||
396 | 38 | |||
397 | 39 | class TestApportReporting(TestCaseInTempDir): | ||
398 | 30 | 40 | ||
399 | 31 | _test_needs_features = [features.apport] | 41 | _test_needs_features = [features.apport] |
400 | 32 | 42 | ||
402 | 33 | def test_apport_report_contents(self): | 43 | def setUp(self): |
403 | 44 | TestCaseInTempDir.setUp(self) | ||
404 | 45 | self.requireFeature(ApportFeature) | ||
405 | 46 | |||
406 | 47 | def test_apport_report(self): | ||
407 | 48 | crash_dir = osutils.joinpath((self.test_base_dir, 'crash')) | ||
408 | 49 | os.mkdir(crash_dir) | ||
409 | 50 | os.environ['APPORT_CRASH_DIR'] = crash_dir | ||
410 | 51 | self.assertEquals(crash_dir, config.crash_dir()) | ||
411 | 52 | |||
412 | 53 | stderr = StringIO() | ||
413 | 54 | |||
414 | 34 | try: | 55 | try: |
415 | 35 | raise AssertionError("my error") | 56 | raise AssertionError("my error") |
416 | 36 | except AssertionError, e: | 57 | except AssertionError, e: |
417 | 37 | pass | 58 | pass |
424 | 38 | outf = StringIO() | 59 | |
425 | 39 | crash._write_apport_report_to_file(sys.exc_info(), outf) | 60 | crash_filename = crash.report_bug_to_apport(sys.exc_info(), |
426 | 40 | report = outf.getvalue() | 61 | stderr) |
427 | 41 | 62 | ||
428 | 42 | self.assertContainsRe(report, '(?m)^BzrVersion:') | 63 | # message explaining the crash |
429 | 43 | # should be in the traceback | 64 | self.assertContainsRe(stderr.getvalue(), |
430 | 65 | " apport-bug %s" % crash_filename) | ||
431 | 66 | |||
432 | 67 | crash_file = open(crash_filename) | ||
433 | 68 | try: | ||
434 | 69 | report = crash_file.read() | ||
435 | 70 | finally: | ||
436 | 71 | crash_file.close() | ||
437 | 72 | |||
438 | 73 | self.assertContainsRe(report, | ||
439 | 74 | '(?m)^BzrVersion:') # should be in the traceback | ||
440 | 44 | self.assertContainsRe(report, 'my error') | 75 | self.assertContainsRe(report, 'my error') |
441 | 45 | self.assertContainsRe(report, 'AssertionError') | 76 | self.assertContainsRe(report, 'AssertionError') |
443 | 46 | self.assertContainsRe(report, 'test_apport_report_contents') | 77 | self.assertContainsRe(report, 'test_apport_report') |
444 | 47 | # should also be in there | 78 | # should also be in there |
445 | 48 | self.assertContainsRe(report, '(?m)^CommandLine:') | 79 | self.assertContainsRe(report, '(?m)^CommandLine:') |
446 | 49 | 80 | ||
447 | === added directory 'contrib/apport' | |||
448 | === added file 'contrib/apport/README' | |||
449 | --- contrib/apport/README 1970-01-01 00:00:00 +0000 | |||
450 | +++ contrib/apport/README 2010-02-03 00:05:22 +0000 | |||
451 | @@ -0,0 +1,13 @@ | |||
452 | 1 | Bazaar supports semi-automatic bug reporting through Apport | ||
453 | 2 | <https://launchpad.net/apport/>. | ||
454 | 3 | |||
455 | 4 | If apport is not installed, an exception is printed to stderr in the usual way. | ||
456 | 5 | |||
457 | 6 | For this to work properly it's suggested that two files be installed when a | ||
458 | 7 | package of bzr is installed: | ||
459 | 8 | |||
460 | 9 | ``bzr.conf`` | ||
461 | 10 | into ``/etc/apport/crashdb.conf.d`` | ||
462 | 11 | |||
463 | 12 | ``source_bzr.py`` | ||
464 | 13 | into ``/usr/share/apport/package-hooks`` | ||
465 | 0 | 14 | ||
466 | === added file 'contrib/apport/bzr.conf' | |||
467 | --- contrib/apport/bzr.conf 1970-01-01 00:00:00 +0000 | |||
468 | +++ contrib/apport/bzr.conf 2010-02-03 00:05:22 +0000 | |||
469 | @@ -0,0 +1,6 @@ | |||
470 | 1 | bzr = { | ||
471 | 2 | # most bzr bugs are upstream bugs; file them there | ||
472 | 3 | 'impl': 'launchpad', | ||
473 | 4 | 'project': 'bzr', | ||
474 | 5 | 'bug_pattern_base': 'http://people.canonical.com/~pitti/bugpatterns', | ||
475 | 6 | } | ||
476 | 0 | 7 | ||
477 | === added file 'contrib/apport/source_bzr.py' | |||
478 | --- contrib/apport/source_bzr.py 1970-01-01 00:00:00 +0000 | |||
479 | +++ contrib/apport/source_bzr.py 2010-02-03 00:05:22 +0000 | |||
480 | @@ -0,0 +1,54 @@ | |||
481 | 1 | '''apport package hook for Bazaar''' | ||
482 | 2 | |||
483 | 3 | # Copyright (c) 2009, 2010 Canonical Ltd. | ||
484 | 4 | # Author: Matt Zimmerman <mdz@canonical.com> | ||
485 | 5 | # and others | ||
486 | 6 | |||
487 | 7 | from apport.hookutils import * | ||
488 | 8 | import os | ||
489 | 9 | |||
490 | 10 | bzr_log = os.path.expanduser('~/.bzr.log') | ||
491 | 11 | dot_bzr = os.path.expanduser('~/.bazaar') | ||
492 | 12 | |||
493 | 13 | def _add_log_tail(report): | ||
494 | 14 | # may have already been added in-process | ||
495 | 15 | if 'BzrLogTail' in report: | ||
496 | 16 | return | ||
497 | 17 | |||
498 | 18 | bzr_log_lines = open(bzr_log).readlines() | ||
499 | 19 | bzr_log_lines.reverse() | ||
500 | 20 | |||
501 | 21 | bzr_log_tail = [] | ||
502 | 22 | blanks = 0 | ||
503 | 23 | for line in bzr_log_lines: | ||
504 | 24 | if line == '\n': | ||
505 | 25 | blanks += 1 | ||
506 | 26 | bzr_log_tail.append(line) | ||
507 | 27 | if blanks >= 2: | ||
508 | 28 | break | ||
509 | 29 | |||
510 | 30 | bzr_log_tail.reverse() | ||
511 | 31 | report['BzrLogTail'] = ''.join(bzr_log_tail) | ||
512 | 32 | |||
513 | 33 | |||
514 | 34 | def add_info(report): | ||
515 | 35 | |||
516 | 36 | _add_log_tail() | ||
517 | 37 | if 'BzrPlugins' not in report: | ||
518 | 38 | # may already be present in-process | ||
519 | 39 | report['BzrPlugins'] = command_output(['bzr', 'plugins', '-v']) | ||
520 | 40 | |||
521 | 41 | # by default assume bzr crashes are upstream bugs; this relies on | ||
522 | 42 | # having a bzr entry under /etc/apport/crashdb.conf.d/ | ||
523 | 43 | report['CrashDB'] = 'bzr' | ||
524 | 44 | |||
525 | 45 | # these may contain some sensitive info (smtp_passwords) | ||
526 | 46 | # TODO: strip that out and attach the rest | ||
527 | 47 | |||
528 | 48 | #attach_file_if_exists(report, | ||
529 | 49 | # os.path.join(dot_bzr, 'bazaar.conf', 'BzrConfig') | ||
530 | 50 | #attach_file_if_exists(report, | ||
531 | 51 | # os.path.join(dot_bzr, 'locations.conf', 'BzrLocations') | ||
532 | 52 | |||
533 | 53 | |||
534 | 54 | # vim: expandtab shiftwidth=4 |
Turn off apport during selftest, and put the traceback at the end where it's more useful to developers and less likely to be scrolled off by plugins.
Fixes bug 417881