| ... |
... |
@@ -1493,6 +1493,14 @@ def _newTestDir(name: TestName, opts: TestOptions, tempdir, dir): |
|
1493
|
1493
|
opts.testdir_raw = Path(os.path.join(tempdir, testdir, name + testdir_suffix))
|
|
1494
|
1494
|
opts.compiler_always_flags = config.compiler_always_flags
|
|
1495
|
1495
|
|
|
|
1496
|
+def _result_directory(opts: TestOptions) -> str:
|
|
|
1497
|
+ # The test's source directory, relative to the GHC source root, so it reads
|
|
|
1498
|
+ # the same regardless of which directory `make` was invoked from.
|
|
|
1499
|
+ try:
|
|
|
1500
|
+ return os.path.relpath(opts.srcdir, config.top.parent)
|
|
|
1501
|
+ except Exception:
|
|
|
1502
|
+ return ''
|
|
|
1503
|
+
|
|
1496
|
1504
|
# -----------------------------------------------------------------------------
|
|
1497
|
1505
|
# Actually doing tests
|
|
1498
|
1506
|
|
| ... |
... |
@@ -1817,7 +1825,7 @@ async def do_test(name: TestName, |
|
1817
|
1825
|
if opts.expect not in ['pass', 'fail', 'missing-lib']:
|
|
1818
|
1826
|
framework_fail(name, way, 'bad expected ' + opts.expect)
|
|
1819
|
1827
|
|
|
1820
|
|
- directory = str_removeprefix(str_removeprefix(str(opts.testdir), './'), '.\\')
|
|
|
1828
|
+ directory = _result_directory(opts)
|
|
1821
|
1829
|
|
|
1822
|
1830
|
if way in opts.fragile_ways:
|
|
1823
|
1831
|
if_verbose(1, '*** fragile test %s resulted in %s' % (full_name, 'pass' if result.passed else 'fail'))
|
| ... |
... |
@@ -1868,7 +1876,7 @@ def framework_fail(name: Optional[TestName], way: Optional[WayName], reason: str |
|
1868
|
1876
|
# so we need to take care not to blow up with the wrong way
|
|
1869
|
1877
|
# and report the actual reason for the failure.
|
|
1870
|
1878
|
try:
|
|
1871
|
|
- directory = str_removeprefix(str_removeprefix(str(opts.testdir), './'), '.\\')
|
|
|
1879
|
+ directory = _result_directory(opts)
|
|
1872
|
1880
|
except:
|
|
1873
|
1881
|
directory = ''
|
|
1874
|
1882
|
full_name = '%s(%s)' % (name, way)
|
| ... |
... |
@@ -1881,7 +1889,7 @@ def framework_fail(name: Optional[TestName], way: Optional[WayName], reason: str |
|
1881
|
1889
|
|
|
1882
|
1890
|
def framework_warn(name: TestName, way: WayName, reason: str) -> None:
|
|
1883
|
1891
|
opts = getTestOpts()
|
|
1884
|
|
- directory = str_removeprefix(str_removeprefix(str(opts.testdir), './'), '.\\')
|
|
|
1892
|
+ directory = _result_directory(opts)
|
|
1885
|
1893
|
full_name = name + '(' + way + ')'
|
|
1886
|
1894
|
if_verbose(1, '*** framework warning for %s %s ' % (full_name, reason))
|
|
1887
|
1895
|
t.framework_warnings.append(TestResult(directory, name, reason, way))
|
| ... |
... |
@@ -3531,17 +3539,17 @@ def summary(t: TestRun, file: TextIO, color=False, junit_path: Optional[Path]=No |
|
3531
|
3539
|
|
|
3532
|
3540
|
file.write('\n')
|
|
3533
|
3541
|
|
|
|
3542
|
+ too_many_failures = len(t.unexpected_failures) > MAX_SUMMARY_OUTPUT_TESTS
|
|
|
3543
|
+
|
|
3534
|
3544
|
if t.unexpected_failures:
|
|
3535
|
|
- if len(t.unexpected_failures) > MAX_SUMMARY_OUTPUT_TESTS:
|
|
3536
|
|
- # junit.xml only exists when a path was requested (e.g. in CI); don't
|
|
3537
|
|
- # point at a file that a local run never wrote.
|
|
3538
|
|
- where = 'see {}'.format(junit_path) if junit_path \
|
|
3539
|
|
- else 'rerun them individually'
|
|
3540
|
|
- file.write('Output of {} unexpected failures omitted (limit {}); {}.\n\n'
|
|
3541
|
|
- .format(len(t.unexpected_failures), MAX_SUMMARY_OUTPUT_TESTS, where))
|
|
3542
|
|
- else:
|
|
|
3545
|
+ if not too_many_failures:
|
|
3543
|
3546
|
file.write('Output of unexpected failures:\n\n')
|
|
3544
|
3547
|
printTestOutputSummary(file, t.unexpected_failures, color, junit_path)
|
|
|
3548
|
+ else:
|
|
|
3549
|
+ where = '; see {}'.format(junit_path) if junit_path else ''
|
|
|
3550
|
+ file.write('Unexpected failures (output omitted, more than {}{}):\n'
|
|
|
3551
|
+ .format(MAX_SUMMARY_OUTPUT_TESTS, where))
|
|
|
3552
|
+ printTestInfosSummary(file, t.unexpected_failures)
|
|
3545
|
3553
|
|
|
3546
|
3554
|
printUnexpectedTests(file,
|
|
3547
|
3555
|
[t.unexpected_passes, t.unexpected_failures,
|
| ... |
... |
@@ -3593,10 +3601,6 @@ def summary(t: TestRun, file: TextIO, color=False, junit_path: Optional[Path]=No |
|
3593
|
3601
|
file.write('Unexpected passes:\n')
|
|
3594
|
3602
|
printTestInfosSummary(file, t.unexpected_passes)
|
|
3595
|
3603
|
|
|
3596
|
|
- if t.unexpected_failures:
|
|
3597
|
|
- file.write('Unexpected failures:\n')
|
|
3598
|
|
- printTestInfosSummary(file, t.unexpected_failures)
|
|
3599
|
|
-
|
|
3600
|
3604
|
if t.unexpected_stat_failures:
|
|
3601
|
3605
|
file.write('Unexpected stat failures:\n')
|
|
3602
|
3606
|
printTestInfosSummary(file, t.unexpected_stat_failures)
|
| ... |
... |
@@ -3634,8 +3638,15 @@ def printTestOutputSummary(file: TextIO, testInfos, color: bool=False, |
|
3634
|
3638
|
# Repeat failing tests' captured output in the summary, so one needn't
|
|
3635
|
3639
|
# hunt for it earlier in a possibly very long log; see #16720.
|
|
3636
|
3640
|
where = ', see {}'.format(junit_path) if junit_path else ''
|
|
3637
|
|
- for result in sorted(testInfos, key=lambda r: (r.testname.lower(), r.way, r.directory)):
|
|
3638
|
|
- header = '=====> {}({}) [{}]'.format(result.testname, result.way, result.reason)
|
|
|
3641
|
+ # Tests that fail identically in several ways (e.g. normal and g1) share one
|
|
|
3642
|
+ # output block, with the ways collected in the header.
|
|
|
3643
|
+ groups = collections.OrderedDict() # type: ignore
|
|
|
3644
|
+ for result in sorted(testInfos, key=lambda r: (r.testname.lower(), r.directory, r.way)):
|
|
|
3645
|
+ key = (result.testname, result.directory, result.reason, result.stdout, result.stderr)
|
|
|
3646
|
+ groups.setdefault(key, (result, []))[1].append(result.way)
|
|
|
3647
|
+ for result, ways in groups.values():
|
|
|
3648
|
+ header = '=====> {}({}) ({}) [{}]'.format(
|
|
|
3649
|
+ result.testname, ', '.join(ways), result.directory + os.sep, result.reason)
|
|
3639
|
3650
|
if color:
|
|
3640
|
3651
|
header = colored(Color.RED, header)
|
|
3641
|
3652
|
file.write(header + '\n')
|
| ... |
... |
@@ -3654,19 +3665,15 @@ def printTestOutputSummary(file: TextIO, testInfos, color: bool=False, |
|
3654
|
3665
|
# cannot represent; replace rather than crash (cf safe_print).
|
|
3655
|
3666
|
enc = getattr(file, 'encoding', None) or 'utf-8'
|
|
3656
|
3667
|
file.write(s.encode(enc, errors='replace').decode(enc))
|
|
3657
|
|
- file.write('\n')
|
|
3658
|
3668
|
footer = '<===== end of output of unexpected failures'
|
|
3659
|
3669
|
if color:
|
|
3660
|
3670
|
footer = colored(Color.RED, footer)
|
|
3661
|
3671
|
file.write(footer + '\n\n')
|
|
3662
|
3672
|
|
|
3663
|
3673
|
def printTestInfosSummary(file: TextIO, testInfos):
|
|
3664
|
|
- maxDirLen = max(len(tr.directory) for tr in testInfos)
|
|
3665
|
3674
|
for result in sorted(testInfos, key=lambda r: (r.testname.lower(), r.way, r.directory)):
|
|
3666
|
|
- directory = result.directory.ljust(maxDirLen)
|
|
3667
|
|
- file.write(' {directory} {r.testname} [{r.reason}] ({r.way})\n'.format(
|
|
3668
|
|
- r = result,
|
|
3669
|
|
- directory = directory))
|
|
|
3675
|
+ path = os.path.join(result.directory, result.testname)
|
|
|
3676
|
+ file.write(' {path} [{r.reason}] ({r.way})\n'.format(r=result, path=path))
|
|
3670
|
3677
|
file.write('\n')
|
|
3671
|
3678
|
|
|
3672
|
3679
|
def modify_lines(s: str, f: Callable[[str], str]) -> str:
|