Skip to content

Commit 1aa0123

Browse files
authored
Improve coverage reports (#361)
* [CI,nox] show the 5 longest tests in pytest output * ignore coverage for impractical use cases * test custom admonitions * test code annotations * test content tabs * test keys role * test syntax highlighting * test task lists * test rst-example directive * test mermaid graphs * improve json domain coverage * test sitemap generation * test inline icons * switch to using codecov
1 parent a930823 commit 1aa0123

20 files changed

+640
-8
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,13 @@ jobs:
223223
name: coverage-report
224224
path: htmlcov/
225225
- name: Post coverage summary
226-
run: cat .coverage_.md >> $GITHUB_STEP_SUMMARY
226+
uses: codecov/codecov-action@v4
227+
env:
228+
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
229+
with:
230+
files: ./coverage.xml
231+
fail_ci_if_error: true # optional (default = false)
232+
verbose: true # optional (default = false)
227233

228234
compare-wheels:
229235
runs-on: ubuntu-latest
@@ -258,7 +264,7 @@ jobs:
258264
# Only publish package on push to tag or default branch.
259265
if: ${{ github.event_name == 'push' && github.repository == 'jbms/sphinx-immaterial' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') }}
260266
runs-on: ubuntu-latest
261-
needs: [build, test]
267+
needs: [build, coverage-report]
262268
steps:
263269
- uses: actions/download-artifact@v4
264270
with:

README.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Sphinx-Immaterial Theme
22
=======================
33

4-
|MIT License| |PyPI Package| |CI status|
4+
|MIT License| |PyPI Package| |CI status| |codecov-badge|
55

66
This theme is an adaptation of the popular `mkdocs-material
77
<https://github.com/squidfunk/mkdocs-material/>`__ theme for the `Sphinx
@@ -118,3 +118,7 @@ few differences, primarily due to differences between Sphinx and MkDocs:
118118

119119
.. |CI status| image:: https://github.com/jbms/sphinx-immaterial/actions/workflows/build.yml/badge.svg
120120
:target: https://github.com/jbms/sphinx-immaterial/actions/workflows/build.yml
121+
122+
.. |codecov-badge| image:: https://codecov.io/gh/jbms/sphinx-immaterial/graph/badge.svg?token=IGK0B3WN42
123+
:alt: codecov
124+
:target: https://codecov.io/gh/jbms/sphinx-immaterial

codecov.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
coverage:
2+
status:
3+
patch:
4+
default:
5+
informational: true
6+
project:
7+
default:
8+
target: auto
9+
# adjust accordingly based on how flaky your tests are
10+
# this allows a 2% drop from the previous base commit coverage
11+
threshold: 2%

docs/apidoc/json/domain.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ specifying the JSON schema definition files.
7777
:caption: Setting default roles and highlight language in :file:`conf.py`
7878
7979
rst_prolog = """
80-
.. role json(code)
80+
.. role:: json(code)
8181
:language: json
8282
:class: highlight
8383
"""

noxfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def tests(session: nox.Session, sphinx: str):
215215
# sphinxcontrib deps that dropped support for sphinx v4.x
216216
session.install("-r", "tests/requirements-sphinx4.txt")
217217
session.install("-r", "tests/requirements.txt")
218-
session.run("coverage", "run", "-m", "pytest", "-vv", "-s")
218+
session.run("coverage", "run", "-m", "pytest", "-vv", "-s", "--durations=5")
219219
# session.notify("docs") <- only calls docs(html), not dirhtml or latex builders
220220

221221

@@ -230,5 +230,5 @@ def coverage(session: nox.Session):
230230
f"<details><summary>Coverage is {total}%</summary>\n\n{md}\n\n</details>",
231231
encoding="utf-8",
232232
)
233-
session.run("coverage", "html")
233+
session.run("coverage", "xml")
234234
session.log("Coverage is %d%%", total)

pyproject.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ omit = [
4141

4242
[tool.coverage.report]
4343
skip_empty = true
44+
# Regexes for lines to exclude from consideration
45+
exclude_lines = [
46+
# Have to re-enable the standard pragma
47+
"pragma: no cover",
48+
# Don't complain about missing debug-only code:
49+
"def __repr__",
50+
# the point of unit tests is to test parts of main()
51+
"def main",
52+
# ignore any branch that makes the module executable
53+
'if __name__ == "__main__"',
54+
# ignore missing implementations
55+
"raise NotImplementedError",
56+
# ignore the type checking specific code (only executed by mypy)
57+
"if typing.TYPE_CHECKING",
58+
# ignore import errors for conditional deps (test run with a strict set of deps)
59+
"except ImportError",
60+
]
4461

4562
[tool.coverage.html]
4663
show_contexts = true

sphinx_immaterial/content_tabs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
172172

173173

174174
def depart_tab_set(self: HTMLTranslator, node: content_tab_set):
175-
pass
175+
pass # pragma: no cover
176176

177177

178178
def setup(app: Sphinx):

sphinx_immaterial/inlinesyntaxhighlight.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def visit_literal(self, node: docutils.nodes.literal) -> None:
7373
if "code" not in node["classes"] or not lang:
7474
return orig_visit_literal(self, node)
7575

76-
def warner(msg):
76+
# This isn't used by pygments; it may be for a different highlighter
77+
def warner(msg): # pragma: no cover
7778
self.builder.warn(msg, (self.builder.current_docname, node.line))
7879

7980
highlight_args = dict(node.get("highlight_args", {}), nowrap=True)

tests/admonition_test.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"""Tests related to theme's patched admonitions."""
2+
3+
import pytest
4+
from sphinx.testing.util import SphinxTestApp
5+
from sphinx.errors import ExtensionError
6+
7+
conf = [
8+
{
9+
"name": "legacy",
10+
"color": (236, 64, 11),
11+
"icon": "fontawesome/solid/recycle",
12+
"classes": ["custom-class"],
13+
},
14+
{
15+
"name": "attention",
16+
"classes": ["important"],
17+
},
18+
{
19+
"name": "versionremoved",
20+
"classes": ["warning"],
21+
"override": True,
22+
},
23+
{
24+
"name": "deprecated",
25+
"color": (0, 0, 0),
26+
"override": True,
27+
},
28+
{
29+
# This is impractical, but it covers a condition in production code
30+
"name": "versionchanged",
31+
"override": True,
32+
},
33+
]
34+
35+
36+
def test_admonitions(immaterial_make_app):
37+
app: SphinxTestApp = immaterial_make_app(
38+
extra_conf=f"sphinx_immaterial_custom_admonitions={repr(conf)}",
39+
files={
40+
"index.rst": """
41+
The Test
42+
========
43+
44+
.. deprecated:: 0.0.1 scheduled for removal
45+
46+
.. versionremoved:: 0.1.0 Code was removed!
47+
:collapsible: open
48+
:class: custom-class
49+
50+
Some rationale.
51+
52+
.. legacy::
53+
:collapsible: open
54+
:class: custom-class
55+
56+
Some content.
57+
58+
.. legacy:: A
59+
:title: Custom title
60+
61+
Some content.
62+
63+
""",
64+
},
65+
)
66+
67+
app.build()
68+
assert not app._warning.getvalue() # type: ignore[attr-defined]
69+
70+
71+
def test_admonition_name_error(immaterial_make_app):
72+
# both exceptions must be raised here, so signify this using nested with statements
73+
with pytest.raises(ExtensionError):
74+
with pytest.raises(ValueError):
75+
immaterial_make_app(
76+
extra_conf="sphinx_immaterial_custom_admonitions=[{"
77+
'"name":"legacy!","classes":["note"]}]',
78+
files={
79+
"index.rst": "",
80+
},
81+
)
82+
83+
84+
def test_admonitions_warnings(immaterial_make_app):
85+
app: SphinxTestApp = immaterial_make_app(
86+
files={
87+
"index.rst": """
88+
The Test
89+
========
90+
91+
.. note::
92+
:collapsible:
93+
:no-title:
94+
95+
Some content.
96+
97+
.. deprecated:: 0.1.0
98+
:collapsible:
99+
100+
""",
101+
},
102+
)
103+
104+
app.build()
105+
warnings = app._warning.getvalue() # type: ignore[attr-defined]
106+
assert warnings
107+
assert "title is needed for collapsible admonitions" in warnings
108+
assert "Expected 2 arguments before content in deprecated directive" in warnings
109+
110+
111+
def test_todo_admonition(immaterial_make_app):
112+
app: SphinxTestApp = immaterial_make_app(
113+
extra_conf="extensions.append('sphinx.ext.todo')",
114+
files={
115+
"index.rst": """
116+
The Test
117+
========
118+
119+
.. todo::
120+
Some content.
121+
122+
""",
123+
},
124+
)
125+
126+
app.build()
127+
assert not app._warning.getvalue() # type: ignore[attr-defined]

tests/code_annotations_test.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Tests related to theme's code block annotations."""
2+
3+
from sphinx.testing.util import SphinxTestApp
4+
5+
index_rst = """
6+
The Test
7+
========
8+
9+
.. code-block:: yaml
10+
:caption: A caption for good measure
11+
12+
# (1)!
13+
14+
.. code-annotations::
15+
1. An annotation
16+
17+
.. code-annotations::
18+
#. A regular list not used in annotations.
19+
20+
.. code-block:: python
21+
22+
# A normal code block without annotations
23+
"""
24+
25+
26+
def test_code_annotation(immaterial_make_app):
27+
app: SphinxTestApp = immaterial_make_app(
28+
files={"index.rst": index_rst},
29+
)
30+
31+
app.build()
32+
assert not app._warning.getvalue() # type: ignore[attr-defined]
33+
34+
def test_code_annotation_error(immaterial_make_app):
35+
app: SphinxTestApp = immaterial_make_app(
36+
files={
37+
"index.rst": """
38+
The Test
39+
========
40+
41+
.. code-annotations::
42+
This is not an enumerated list!
43+
44+
"""},
45+
)
46+
47+
app.build()
48+
warnings = app._warning.getvalue() # type: ignore[attr-defined]
49+
assert warnings
50+
assert "The code-annotations directive only accepts an enumerated list" in warnings

0 commit comments

Comments
 (0)