Skip to content

Commit eb0fc15

Browse files
author
Release Manager
committed
gh-36938: CI Build & Test: Show doctest failures, warnings as annotations in the 'Files changed' tab <!-- ^^^^^ Please provide a concise, informative and self-explanatory title. Don't put issue numbers in there, do this in the PR body below. For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" --> <!-- Describe your changes here in detail --> This prints doctest failures as github annotations. For illustrating this new feature, the branch on #36558 provokes some errors. As seen in the "Files changed" tab over there (https://github.com/sagemath/sage/pull/36558/files): - Doctest failures show as errors next to the doctest in source code - Doctest warnings show as "warnings" - Doctest failures from files that "failed in baseline" show as "notices" These messages can also be seen in the Summary page of the workflow (https://github.com/sagemath/sage/actions/runs/7359388426?pr=36558). Clicking on a message leads to the source code location. <!-- Why is this change required? What problem does it solve? --> <!-- If this PR resolves an open issue, please link to it here. For example "Fixes #12345". --> <!-- If your change requires a documentation PR, please link it appropriately. --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [ ] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> - Depends on #36936 <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #36938 Reported by: Matthias Köppe Reviewer(s): Alex J Best, Matthias Köppe, Tobias Diez
2 parents caf7cf8 + 0c52169 commit eb0fc15

File tree

5 files changed

+64
-13
lines changed

5 files changed

+64
-13
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,15 @@ jobs:
197197
- name: Test all files (sage -t --all --long)
198198
if: (success() || failure()) && steps.build.outcome == 'success'
199199
run: |
200-
../sage -python -m pip install coverage
201-
../sage -python -m coverage run ./bin/sage-runtests --all --long -p2 --random-seed=286735480429121101562228604801325644303
202-
working-directory: ./worktree-image/src
200+
./sage -python -m pip install coverage
201+
./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --all --long -p2 --format github --random-seed=286735480429121101562228604801325644303
202+
working-directory: ./worktree-image
203203

204204
- name: Prepare coverage results
205205
if: (success() || failure()) && steps.build.outcome == 'success'
206206
run: |
207-
./venv/bin/python3 -m coverage combine src/.coverage/
208-
./venv/bin/python3 -m coverage xml
207+
./sage -python -m coverage combine --rcfile=src/tox.ini
208+
./sage -python -m coverage xml --rcfile=src/tox.ini
209209
mkdir -p coverage-report
210210
mv coverage.xml coverage-report/
211211
working-directory: ./worktree-image

src/bin/sage-runtests

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ if __name__ == "__main__":
3434
what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library")
3535
parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE")
3636

37+
parser.add_argument("--format", choices=["sage", "github"], default="sage",
38+
help="set format of error messages and warnings")
3739
parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'")
3840
parser.add_argument("-s", "--short", dest="target_walltime", nargs='?',
3941
type=int, default=-1, const=300, metavar="SECONDS",

src/sage/doctest/control.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def __init__(self, **kwds):
139139
self.show_skipped = False
140140
self.target_walltime = -1
141141
self.baseline_stats_path = None
142+
self.format = "sage"
142143

143144
# sage-runtests contains more optional tags. Technically, adding
144145
# auto_optional_tags here is redundant, since that is added

src/sage/doctest/forker.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,16 +1235,57 @@ def _failure_header(self, test, example, message='Failed example:'):
12351235
"""
12361236
out = [self.DIVIDER]
12371237
with OriginalSource(example):
1238-
if test.filename:
1239-
if test.lineno is not None and example.lineno is not None:
1240-
lineno = test.lineno + example.lineno + 1
1238+
if self.options.format == 'sage':
1239+
if test.filename:
1240+
if test.lineno is not None and example.lineno is not None:
1241+
lineno = test.lineno + example.lineno + 1
1242+
else:
1243+
lineno = '?'
1244+
out.append('File "%s", line %s, in %s' %
1245+
(test.filename, lineno, test.name))
1246+
else:
1247+
out.append('Line %s, in %s' % (example.lineno + 1, test.name))
1248+
out.append(message)
1249+
elif self.options.format == 'github':
1250+
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#using-workflow-commands-to-access-toolkit-functions
1251+
if message.startswith('Warning: '):
1252+
command = f'::warning title={message}'
1253+
message = message[len('Warning: '):]
1254+
elif self.baseline.get('failed', False):
1255+
command = f'::notice title={message}'
1256+
message += ' [failed in baseline]'
12411257
else:
1242-
lineno = '?'
1243-
out.append('File "%s", line %s, in %s' %
1244-
(test.filename, lineno, test.name))
1258+
command = f'::error title={message}'
1259+
if extra := getattr(example, 'extra', None):
1260+
message += f': {extra}'
1261+
if test.filename:
1262+
command += f',file={test.filename}'
1263+
if test.lineno is not None and example.lineno is not None:
1264+
lineno = test.lineno + example.lineno + 1
1265+
command += f',line={lineno}'
1266+
lineno = None
1267+
else:
1268+
command += f',line={example.lineno + 1}'
1269+
#
1270+
# Urlencoding trick for multi-line annotations
1271+
# https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448
1272+
#
1273+
# This only affects the display in the workflow Summary, after clicking "Show more";
1274+
# the message needs to be long enough so that "Show more" becomes available.
1275+
# https://github.com/actions/toolkit/issues/193#issuecomment-1867084340
1276+
#
1277+
# Unfortunately, this trick does not make the annotations in the diff view multi-line.
1278+
#
1279+
if '\n' in message:
1280+
message = message.replace('\n', '%0A')
1281+
# The actual threshold for "Show more" to appear depends on the window size.
1282+
show_more_threshold = 500
1283+
if (pad := show_more_threshold - len(message)) > 0:
1284+
message += ' ' * pad
1285+
command += f'::{message}'
1286+
out.append(command)
12451287
else:
1246-
out.append('Line %s, in %s' % (example.lineno + 1, test.name))
1247-
out.append(message)
1288+
raise ValueError(f'unknown format option: {self.options.format}')
12481289
source = example.source
12491290
out.append(doctest._indent(source))
12501291
return '\n'.join(out)
@@ -1417,6 +1458,7 @@ def report_failure(self, out, test, example, got, globs):
14171458
"""
14181459
if not self.options.initial or self.no_failure_yet:
14191460
self.no_failure_yet = False
1461+
example.extra = f'Got: {got}'
14201462
returnval = doctest.DocTestRunner.report_failure(self, out, test, example, got)
14211463
if self.options.debug:
14221464
self._fakeout.stop_spoofing()
@@ -1569,6 +1611,8 @@ def report_unexpected_exception(self, out, test, example, exc_info):
15691611
"""
15701612
if not self.options.initial or self.no_failure_yet:
15711613
self.no_failure_yet = False
1614+
1615+
example.extra = "Exception raised:\n" + "".join(traceback.format_exception(*exc_info))
15721616
returnval = doctest.DocTestRunner.report_unexpected_exception(self, out, test, example, exc_info)
15731617
if self.options.debug:
15741618
self._fakeout.stop_spoofing()

src/tox.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,7 @@ source = sage
299299
concurrency = multiprocessing
300300
data_file = .coverage/.coverage
301301
disable_warnings = no-data-collected
302+
303+
[coverage:report]
304+
ignore_errors = True
305+
skip_empty = True

0 commit comments

Comments
 (0)