Show More
@@ -0,0 +1,73 b'' | |||||
|
1 | # logexceptions.py - Write files containing info about Mercurial exceptions | |||
|
2 | # | |||
|
3 | # Copyright 2017 Matt Mackall <mpm@selenic.com> | |||
|
4 | # | |||
|
5 | # This software may be used and distributed according to the terms of the | |||
|
6 | # GNU General Public License version 2 or any later version. | |||
|
7 | ||||
|
8 | from __future__ import absolute_import | |||
|
9 | ||||
|
10 | import inspect | |||
|
11 | import os | |||
|
12 | import sys | |||
|
13 | import traceback | |||
|
14 | import uuid | |||
|
15 | ||||
|
16 | from mercurial import ( | |||
|
17 | dispatch, | |||
|
18 | extensions, | |||
|
19 | ) | |||
|
20 | ||||
|
21 | def handleexception(orig, ui): | |||
|
22 | res = orig(ui) | |||
|
23 | ||||
|
24 | if not ui.environ.get(b'HGEXCEPTIONSDIR'): | |||
|
25 | return res | |||
|
26 | ||||
|
27 | dest = os.path.join(ui.environ[b'HGEXCEPTIONSDIR'], | |||
|
28 | str(uuid.uuid4()).encode('ascii')) | |||
|
29 | ||||
|
30 | exc_type, exc_value, exc_tb = sys.exc_info() | |||
|
31 | ||||
|
32 | stack = [] | |||
|
33 | tb = exc_tb | |||
|
34 | while tb: | |||
|
35 | stack.append(tb) | |||
|
36 | tb = tb.tb_next | |||
|
37 | stack.reverse() | |||
|
38 | ||||
|
39 | hgframe = 'unknown' | |||
|
40 | hgline = 'unknown' | |||
|
41 | ||||
|
42 | # Find the first Mercurial frame in the stack. | |||
|
43 | for tb in stack: | |||
|
44 | mod = inspect.getmodule(tb) | |||
|
45 | if not mod.__name__.startswith(('hg', 'mercurial')): | |||
|
46 | continue | |||
|
47 | ||||
|
48 | frame = tb.tb_frame | |||
|
49 | ||||
|
50 | try: | |||
|
51 | with open(inspect.getsourcefile(tb), 'r') as fh: | |||
|
52 | hgline = fh.readlines()[frame.f_lineno - 1].strip() | |||
|
53 | except (IndexError, OSError): | |||
|
54 | pass | |||
|
55 | ||||
|
56 | hgframe = '%s:%d' % (frame.f_code.co_filename, frame.f_lineno) | |||
|
57 | break | |||
|
58 | ||||
|
59 | primary = traceback.extract_tb(exc_tb)[-1] | |||
|
60 | primaryframe = '%s:%d' % (primary.filename, primary.lineno) | |||
|
61 | ||||
|
62 | with open(dest, 'wb') as fh: | |||
|
63 | parts = [ | |||
|
64 | str(exc_value), | |||
|
65 | primaryframe, | |||
|
66 | hgframe, | |||
|
67 | hgline, | |||
|
68 | ] | |||
|
69 | fh.write(b'\0'.join(p.encode('utf-8', 'replace') for p in parts)) | |||
|
70 | ||||
|
71 | def extsetup(ui): | |||
|
72 | extensions.wrapfunction(dispatch, 'handlecommandexception', | |||
|
73 | handleexception) |
@@ -24,6 +24,7 b' tests/.testtimes*' | |||||
24 | tests/.hypothesis |
|
24 | tests/.hypothesis | |
25 | tests/hypothesis-generated |
|
25 | tests/hypothesis-generated | |
26 | tests/annotated |
|
26 | tests/annotated | |
|
27 | tests/exceptions | |||
27 | tests/*.err |
|
28 | tests/*.err | |
28 | tests/htmlcov |
|
29 | tests/htmlcov | |
29 | build |
|
30 | build |
@@ -46,6 +46,7 b'' | |||||
46 | from __future__ import absolute_import, print_function |
|
46 | from __future__ import absolute_import, print_function | |
47 |
|
47 | |||
48 | import argparse |
|
48 | import argparse | |
|
49 | import collections | |||
49 | import difflib |
|
50 | import difflib | |
50 | import distutils.version as version |
|
51 | import distutils.version as version | |
51 | import errno |
|
52 | import errno | |
@@ -373,7 +374,7 b' def getparser():' | |||||
373 | help="install and use chg wrapper in place of hg") |
|
374 | help="install and use chg wrapper in place of hg") | |
374 | hgconf.add_argument("--compiler", |
|
375 | hgconf.add_argument("--compiler", | |
375 | help="compiler to build with") |
|
376 | help="compiler to build with") | |
376 | hgconf.add_argument('--extra-config-opt', action="append", |
|
377 | hgconf.add_argument('--extra-config-opt', action="append", default=[], | |
377 | help='set the given config opt in the test hgrc') |
|
378 | help='set the given config opt in the test hgrc') | |
378 | hgconf.add_argument("-l", "--local", action="store_true", |
|
379 | hgconf.add_argument("-l", "--local", action="store_true", | |
379 | help="shortcut for --with-hg=<testdir>/../hg, " |
|
380 | help="shortcut for --with-hg=<testdir>/../hg, " | |
@@ -404,6 +405,8 b' def getparser():' | |||||
404 | help="colorisation: always|auto|never (default: auto)") |
|
405 | help="colorisation: always|auto|never (default: auto)") | |
405 | reporting.add_argument("-c", "--cover", action="store_true", |
|
406 | reporting.add_argument("-c", "--cover", action="store_true", | |
406 | help="print a test coverage report") |
|
407 | help="print a test coverage report") | |
|
408 | reporting.add_argument('--exceptions', action='store_true', | |||
|
409 | help='log all exceptions and generate an exception report') | |||
407 | reporting.add_argument("-H", "--htmlcov", action="store_true", |
|
410 | reporting.add_argument("-H", "--htmlcov", action="store_true", | |
408 | help="create an HTML report of the coverage of the files") |
|
411 | help="create an HTML report of the coverage of the files") | |
409 | reporting.add_argument("--json", action="store_true", |
|
412 | reporting.add_argument("--json", action="store_true", | |
@@ -2115,6 +2118,18 b' class TextTestRunner(unittest.TextTestRu' | |||||
2115 | os.environ['PYTHONHASHSEED']) |
|
2118 | os.environ['PYTHONHASHSEED']) | |
2116 | if self._runner.options.time: |
|
2119 | if self._runner.options.time: | |
2117 | self.printtimes(result.times) |
|
2120 | self.printtimes(result.times) | |
|
2121 | ||||
|
2122 | if self._runner.options.exceptions: | |||
|
2123 | exceptions = aggregateexceptions( | |||
|
2124 | os.path.join(self._runner._outputdir, b'exceptions')) | |||
|
2125 | total = sum(exceptions.values()) | |||
|
2126 | ||||
|
2127 | self.stream.writeln('Exceptions Report:') | |||
|
2128 | self.stream.writeln('%d total from %d frames' % | |||
|
2129 | (total, len(exceptions))) | |||
|
2130 | for (frame, line, exc), count in exceptions.most_common(): | |||
|
2131 | self.stream.writeln('%d\t%s: %s' % (count, frame, exc)) | |||
|
2132 | ||||
2118 | self.stream.flush() |
|
2133 | self.stream.flush() | |
2119 |
|
2134 | |||
2120 | return result |
|
2135 | return result | |
@@ -2501,6 +2516,23 b' class TestRunner(object):' | |||||
2501 |
|
2516 | |||
2502 | self._coveragefile = os.path.join(self._testdir, b'.coverage') |
|
2517 | self._coveragefile = os.path.join(self._testdir, b'.coverage') | |
2503 |
|
2518 | |||
|
2519 | if self.options.exceptions: | |||
|
2520 | exceptionsdir = os.path.join(self._outputdir, b'exceptions') | |||
|
2521 | try: | |||
|
2522 | os.makedirs(exceptionsdir) | |||
|
2523 | except OSError as e: | |||
|
2524 | if e.errno != errno.EEXIST: | |||
|
2525 | raise | |||
|
2526 | ||||
|
2527 | # Remove all existing exception reports. | |||
|
2528 | for f in os.listdir(exceptionsdir): | |||
|
2529 | os.unlink(os.path.join(exceptionsdir, f)) | |||
|
2530 | ||||
|
2531 | osenvironb[b'HGEXCEPTIONSDIR'] = exceptionsdir | |||
|
2532 | logexceptions = os.path.join(self._testdir, b'logexceptions.py') | |||
|
2533 | self.options.extra_config_opt.append( | |||
|
2534 | 'extensions.logexceptions=%s' % logexceptions.decode('utf-8')) | |||
|
2535 | ||||
2504 | vlog("# Using TESTDIR", self._testdir) |
|
2536 | vlog("# Using TESTDIR", self._testdir) | |
2505 | vlog("# Using RUNTESTDIR", osenvironb[b'RUNTESTDIR']) |
|
2537 | vlog("# Using RUNTESTDIR", osenvironb[b'RUNTESTDIR']) | |
2506 | vlog("# Using HGTMP", self._hgtmp) |
|
2538 | vlog("# Using HGTMP", self._hgtmp) | |
@@ -2953,6 +2985,24 b' class TestRunner(object):' | |||||
2953 | print("WARNING: Did not find prerequisite tool: %s " % |
|
2985 | print("WARNING: Did not find prerequisite tool: %s " % | |
2954 | p.decode("utf-8")) |
|
2986 | p.decode("utf-8")) | |
2955 |
|
2987 | |||
|
2988 | def aggregateexceptions(path): | |||
|
2989 | exceptions = collections.Counter() | |||
|
2990 | ||||
|
2991 | for f in os.listdir(path): | |||
|
2992 | with open(os.path.join(path, f), 'rb') as fh: | |||
|
2993 | data = fh.read().split(b'\0') | |||
|
2994 | if len(data) != 4: | |||
|
2995 | continue | |||
|
2996 | ||||
|
2997 | exc, mainframe, hgframe, hgline = data | |||
|
2998 | exc = exc.decode('utf-8') | |||
|
2999 | mainframe = mainframe.decode('utf-8') | |||
|
3000 | hgframe = hgframe.decode('utf-8') | |||
|
3001 | hgline = hgline.decode('utf-8') | |||
|
3002 | exceptions[(hgframe, hgline, exc)] += 1 | |||
|
3003 | ||||
|
3004 | return exceptions | |||
|
3005 | ||||
2956 | if __name__ == '__main__': |
|
3006 | if __name__ == '__main__': | |
2957 | runner = TestRunner() |
|
3007 | runner = TestRunner() | |
2958 |
|
3008 |
General Comments 0
You need to be logged in to leave comments.
Login now