Simon Jakobi pushed to branch wip/sjakobi/T16720 at Glasgow Haskell Compiler / GHC
Commits:
-
3d1919a5
by Simon Jakobi at 2026-06-11T08:32:19+02:00
3 changed files:
Changes:
| ... | ... | @@ -644,6 +644,10 @@ function test_hadrian() { |
| 644 | 644 | check_msys2_deps _build/stage1/bin/ghc --version
|
| 645 | 645 | check_release_build
|
| 646 | 646 | |
| 647 | + # The GitLab log viewer renders ANSI colors, but the testsuite driver only
|
|
| 648 | + # emits them when stdout is a tty unless they are forced.
|
|
| 649 | + RUNTEST_ARGS="${RUNTEST_ARGS:-} --force-colors"
|
|
| 650 | + |
|
| 647 | 651 | # Ensure that statically-linked builds are actually static
|
| 648 | 652 | if [[ "${BUILD_FLAVOUR}" = *static* ]]; then
|
| 649 | 653 | bad_execs=""
|
| ... | ... | @@ -94,6 +94,8 @@ parser.add_argument("--ignore-perf-failures", choices=['increases','decreases',' |
| 94 | 94 | help="Do not fail due to out-of-tolerance perf tests")
|
| 95 | 95 | parser.add_argument("--only-report-hadrian-deps", type=Path,
|
| 96 | 96 | help="Dry run the testsuite and report all extra hadrian dependencies needed on the given file")
|
| 97 | +parser.add_argument("--force-colors", action="store_true",
|
|
| 98 | + help="emit ANSI colors even when stdout is not a tty (e.g. for CI logs)")
|
|
| 97 | 99 | |
| 98 | 100 | args = parser.parse_args()
|
| 99 | 101 | |
| ... | ... | @@ -259,7 +261,9 @@ def supports_colors(): |
| 259 | 261 | return True
|
| 260 | 262 | |
| 261 | 263 | config.supports_colors = supports_colors()
|
| 262 | -term_color.enable_color = config.supports_colors
|
|
| 264 | +# Note that config.supports_colors deliberately stays tty-based: it also
|
|
| 265 | +# guards terminal-title updates, which must not end up in a CI log.
|
|
| 266 | +term_color.enable_color = config.supports_colors or args.force_colors
|
|
| 263 | 267 | |
| 264 | 268 | # This has to come after arg parsing as the args can change the compiler
|
| 265 | 269 | get_compiler_info()
|
| ... | ... | @@ -587,7 +591,7 @@ else: |
| 587 | 591 | print(Perf.allow_changes_string([(m.change, m.stat) for m in t.metrics]))
|
| 588 | 592 | print('-' * 25)
|
| 589 | 593 | |
| 590 | - summary(t, sys.stdout, color=config.supports_colors)
|
|
| 594 | + summary(t, sys.stdout, color=term_color.enable_color)
|
|
| 591 | 595 | |
| 592 | 596 | # Write perf stats if any exist or if a metrics file is specified.
|
| 593 | 597 | stats_metrics = [stat for (_, stat, __) in t.metrics] # type: List[PerfStat]
|
| ... | ... | @@ -3543,7 +3543,8 @@ def summary(t: TestRun, file: TextIO, color=False) -> None: |
| 3543 | 3543 | summary_color = Color.GREEN
|
| 3544 | 3544 | |
| 3545 | 3545 | assert t.start_time is not None
|
| 3546 | - file.write(colored(summary_color, 'SUMMARY') + ' for test run started at '
|
|
| 3546 | + summary_header = colored(summary_color, 'SUMMARY') if color else 'SUMMARY'
|
|
| 3547 | + file.write(summary_header + ' for test run started at '
|
|
| 3547 | 3548 | + t.start_time.strftime("%c %Z") + '\n'
|
| 3548 | 3549 | + str(datetime.datetime.now() - t.start_time).rjust(8)
|
| 3549 | 3550 | + ' spent to go through\n'
|
| ... | ... | @@ -3597,7 +3598,7 @@ def summary(t: TestRun, file: TextIO, color=False) -> None: |
| 3597 | 3598 | |
| 3598 | 3599 | if t.unexpected_failures:
|
| 3599 | 3600 | file.write('Output of unexpected failures:\n\n')
|
| 3600 | - printTestOutputSummary(file, t.unexpected_failures)
|
|
| 3601 | + printTestOutputSummary(file, t.unexpected_failures, color)
|
|
| 3601 | 3602 | |
| 3602 | 3603 | if stopping():
|
| 3603 | 3604 | file.write('WARNING: Testsuite run was terminated early\n')
|
| ... | ... | @@ -3616,22 +3617,27 @@ def printUnexpectedTests(file: TextIO, testInfoss): |
| 3616 | 3617 | # per stream in the final summary (see printTestOutputSummary).
|
| 3617 | 3618 | MAX_SUMMARY_OUTPUT_LINES = 100
|
| 3618 | 3619 | |
| 3619 | -def printTestOutputSummary(file: TextIO, testInfos) -> None:
|
|
| 3620 | +def printTestOutputSummary(file: TextIO, testInfos, color: bool=False) -> None:
|
|
| 3620 | 3621 | # Repeat the captured output of failing tests at the end of the run, so
|
| 3621 | 3622 | # that one can see why a test failed without hunting for its output
|
| 3622 | 3623 | # earlier in the (possibly very long) log; see #16720.
|
| 3624 | + # The captured output is indented to set it apart from the headers.
|
|
| 3623 | 3625 | for result in sorted(testInfos, key=lambda r: (r.testname.lower(), r.way, r.directory)):
|
| 3624 | - file.write(colored(Color.RED,
|
|
| 3625 | - '=====> {}({}) [{}]'.format(result.testname, result.way, result.reason))
|
|
| 3626 | - + '\n')
|
|
| 3626 | + header = '=====> {}({}) [{}]'.format(result.testname, result.way, result.reason)
|
|
| 3627 | + if color:
|
|
| 3628 | + header = colored(Color.RED, header)
|
|
| 3629 | + file.write(header + '\n')
|
|
| 3627 | 3630 | for stream_name, contents in [('stdout', result.stdout), ('stderr', result.stderr)]:
|
| 3628 | 3631 | if contents and contents.strip():
|
| 3632 | + label = 'Captured {}:'.format(stream_name)
|
|
| 3633 | + if color:
|
|
| 3634 | + label = colored(Color.CYAN, label)
|
|
| 3629 | 3635 | lines = contents.rstrip('\n').split('\n')
|
| 3630 | 3636 | if len(lines) > MAX_SUMMARY_OUTPUT_LINES:
|
| 3631 | 3637 | omitted = len(lines) - MAX_SUMMARY_OUTPUT_LINES
|
| 3632 | 3638 | lines = lines[:MAX_SUMMARY_OUTPUT_LINES] \
|
| 3633 | 3639 | + ['... ({} more lines omitted, see junit.xml)'.format(omitted)]
|
| 3634 | - s = 'Captured {}:\n{}\n'.format(stream_name, '\n'.join(lines))
|
|
| 3640 | + s = label + '\n' + ''.join(' ' + l + '\n' for l in lines)
|
|
| 3635 | 3641 | # Test output can contain characters that file's encoding
|
| 3636 | 3642 | # cannot represent; replace rather than crash (cf safe_print).
|
| 3637 | 3643 | enc = getattr(file, 'encoding', None) or 'utf-8'
|