summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/roots/test-domain-c/field-role.rst4
-rw-r--r--tests/roots/test-domain-cpp/field-role.rst5
-rw-r--r--tests/roots/test-domain-py-python_use_unqualified_type_names/index.rst4
-rw-r--r--tests/roots/test-ext-autodoc/target/instance_variable.py1
-rw-r--r--tests/roots/test-ext-autodoc/target/typehints.py10
-rw-r--r--tests/roots/test-latex-container/conf.py0
-rw-r--r--tests/roots/test-latex-container/index.rst4
-rw-r--r--tests/roots/test-linkcheck/links.txt2
-rw-r--r--tests/roots/test-remote-logo/conf.py5
-rw-r--r--tests/roots/test-remote-logo/index.rst32
-rw-r--r--tests/roots/test-root/lists.txt7
-rw-r--r--tests/test_build_html.py10
-rw-r--r--tests/test_build_latex.py8
-rw-r--r--tests/test_build_linkcheck.py36
-rw-r--r--tests/test_build_manpage.py5
-rw-r--r--tests/test_domain_c.py163
-rw-r--r--tests/test_domain_cpp.py156
-rw-r--r--tests/test_domain_py.py6
-rw-r--r--tests/test_ext_autodoc_autoattribute.py11
-rw-r--r--tests/test_ext_autodoc_configs.py38
-rw-r--r--tests/test_ext_math.py13
-rw-r--r--tests/test_intl.py2
-rw-r--r--tests/test_pycode_ast.py3
-rw-r--r--tests/test_quickstart.py16
24 files changed, 402 insertions, 139 deletions
diff --git a/tests/roots/test-domain-c/field-role.rst b/tests/roots/test-domain-c/field-role.rst
new file mode 100644
index 000000000..5452db5be
--- /dev/null
+++ b/tests/roots/test-domain-c/field-role.rst
@@ -0,0 +1,4 @@
+.. c:function:: void f(int a, int *b)
+
+ :param int a:
+ :param int* b:
diff --git a/tests/roots/test-domain-cpp/field-role.rst b/tests/roots/test-domain-cpp/field-role.rst
new file mode 100644
index 000000000..1711a889c
--- /dev/null
+++ b/tests/roots/test-domain-cpp/field-role.rst
@@ -0,0 +1,5 @@
+.. cpp:function:: void f()
+
+ :throws int:
+ :throws int*:
+
diff --git a/tests/roots/test-domain-py-python_use_unqualified_type_names/index.rst b/tests/roots/test-domain-py-python_use_unqualified_type_names/index.rst
index 599206d8c..a6850a0f4 100644
--- a/tests/roots/test-domain-py-python_use_unqualified_type_names/index.rst
+++ b/tests/roots/test-domain-py-python_use_unqualified_type_names/index.rst
@@ -4,5 +4,9 @@ domain-py-smart_reference
.. py:class:: Name
:module: foo
+ :param name: blah blah
+ :type name: foo.Name
+ :param age: blah blah
+ :type age: foo.Age
.. py:function:: hello(name: foo.Name, age: foo.Age)
diff --git a/tests/roots/test-ext-autodoc/target/instance_variable.py b/tests/roots/test-ext-autodoc/target/instance_variable.py
index ae86d1edb..1d393bc87 100644
--- a/tests/roots/test-ext-autodoc/target/instance_variable.py
+++ b/tests/roots/test-ext-autodoc/target/instance_variable.py
@@ -8,3 +8,4 @@ class Bar(Foo):
def __init__(self):
self.attr2 = None #: docstring bar
self.attr3 = None #: docstring bar
+ self.attr4 = None
diff --git a/tests/roots/test-ext-autodoc/target/typehints.py b/tests/roots/test-ext-autodoc/target/typehints.py
index bb56054c3..9e1504399 100644
--- a/tests/roots/test-ext-autodoc/target/typehints.py
+++ b/tests/roots/test-ext-autodoc/target/typehints.py
@@ -1,5 +1,8 @@
from typing import Any, Tuple, Union
+CONST1: int
+CONST2: int = 1
+
def incr(a: int, b: int = 1) -> int:
return a + b
@@ -11,6 +14,9 @@ def decr(a, b = 1):
class Math:
+ CONST1: int
+ CONST2: int = 1
+
def __init__(self, s: str, o: Any = None) -> None:
pass
@@ -32,6 +38,10 @@ class Math:
# type: (...) -> None
return
+ @property
+ def prop(self) -> int:
+ return 0
+
def tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]:
pass
diff --git a/tests/roots/test-latex-container/conf.py b/tests/roots/test-latex-container/conf.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/roots/test-latex-container/conf.py
diff --git a/tests/roots/test-latex-container/index.rst b/tests/roots/test-latex-container/index.rst
new file mode 100644
index 000000000..899788bd5
--- /dev/null
+++ b/tests/roots/test-latex-container/index.rst
@@ -0,0 +1,4 @@
+.. container:: classname
+
+ text
+ \ No newline at end of file
diff --git a/tests/roots/test-linkcheck/links.txt b/tests/roots/test-linkcheck/links.txt
index b389414c9..c21968250 100644
--- a/tests/roots/test-linkcheck/links.txt
+++ b/tests/roots/test-linkcheck/links.txt
@@ -13,6 +13,8 @@ Some additional anchors to exercise ignore code
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
* `Example valid local file <conf.py>`_
* `Example invalid local file <path/to/notfound>`_
+* https://github.com/sphinx-doc/sphinx#documentation
+* https://github.com/sphinx-doc/sphinx#user-content-testing
.. image:: https://www.google.com/image.png
.. figure:: https://www.google.com/image2.png
diff --git a/tests/roots/test-remote-logo/conf.py b/tests/roots/test-remote-logo/conf.py
new file mode 100644
index 000000000..07949ba91
--- /dev/null
+++ b/tests/roots/test-remote-logo/conf.py
@@ -0,0 +1,5 @@
+latex_documents = [
+ ('index', 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
+]
+html_logo = "https://www.python.org/static/img/python-logo.png"
+html_favicon = "https://www.python.org/static/favicon.ico"
diff --git a/tests/roots/test-remote-logo/index.rst b/tests/roots/test-remote-logo/index.rst
new file mode 100644
index 000000000..48407e643
--- /dev/null
+++ b/tests/roots/test-remote-logo/index.rst
@@ -0,0 +1,32 @@
+The basic Sphinx documentation for testing
+==========================================
+
+Sphinx is a tool that makes it easy to create intelligent and beautiful
+documentation for Python projects (or other documents consisting of multiple
+reStructuredText sources), written by Georg Brandl. It was originally created
+for the new Python documentation, and has excellent facilities for Python
+project documentation, but C/C++ is supported as well, and more languages are
+planned.
+
+Sphinx uses reStructuredText as its markup language, and many of its strengths
+come from the power and straightforwardness of reStructuredText and its parsing
+and translating suite, the Docutils.
+
+features
+--------
+
+Among its features are the following:
+
+* Output formats: HTML (including derivative formats such as HTML Help, Epub
+ and Qt Help), plain text, manual pages and LaTeX or direct PDF output
+ using rst2pdf
+* Extensive cross-references: semantic markup and automatic links
+ for functions, classes, glossary terms and similar pieces of information
+* Hierarchical structure: easy definition of a document tree, with automatic
+ links to siblings, parents and children
+* Automatic indices: general index as well as a module index
+* Code handling: automatic highlighting using the Pygments highlighter
+* Flexible HTML output using the Jinja 2 templating engine
+* Various extensions are available, e.g. for automatic testing of snippets
+ and inclusion of appropriately formatted docstrings
+* Setuptools integration
diff --git a/tests/roots/test-root/lists.txt b/tests/roots/test-root/lists.txt
index 1fa2d11c5..0b5445461 100644
--- a/tests/roots/test-root/lists.txt
+++ b/tests/roots/test-root/lists.txt
@@ -61,3 +61,10 @@ term1
term2 (**stronged partially**)
description
+
+Samp tests
+----------
+
+:samp:`{variable_only}`
+:samp:`{variable} and text`
+:samp:`Show {variable} in the middle`
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index c74552d9e..2e53bdc54 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -1330,6 +1330,16 @@ def test_html_remote_images(app, status, warning):
assert not (app.outdir / 'python-logo.png').exists()
+@pytest.mark.sphinx('html', testroot='remote-logo')
+def test_html_remote_logo(app, status, warning):
+ app.builder.build_all()
+
+ result = (app.outdir / 'index.html').read_text()
+ assert ('<img class="logo" src="https://www.python.org/static/img/python-logo.png" alt="Logo"/>' in result)
+ assert ('<link rel="shortcut icon" href="https://www.python.org/static/favicon.ico"/>' in result)
+ assert not (app.outdir / 'python-logo.png').exists()
+
+
@pytest.mark.sphinx('html', testroot='basic')
def test_html_sidebar(app, status, warning):
ctx = {}
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index bb1904d2c..e0cfce953 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -1599,3 +1599,11 @@ def test_latex_elements_extrapackages(app, status, warning):
def test_latex_nested_tables(app, status, warning):
app.builder.build_all()
assert '' == warning.getvalue()
+
+
+@pytest.mark.sphinx('latex', testroot='latex-container')
+def test_latex_container(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'python.tex').read_text()
+ assert r'\begin{sphinxuseclass}{classname}' in result
+ assert r'\end{sphinxuseclass}' in result
diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py
index bc85e402f..6db0e7512 100644
--- a/tests/test_build_linkcheck.py
+++ b/tests/test_build_linkcheck.py
@@ -66,8 +66,8 @@ def test_defaults_json(app):
"info"]:
assert attr in row
- assert len(content.splitlines()) == 10
- assert len(rows) == 10
+ assert len(content.splitlines()) == 12
+ assert len(rows) == 12
# the output order of the rows is not stable
# due to possible variance in network latency
rowsby = {row["uri"]: row for row in rows}
@@ -88,7 +88,7 @@ def test_defaults_json(app):
assert dnerow['uri'] == 'https://localhost:7777/doesnotexist'
assert rowsby['https://www.google.com/image2.png'] == {
'filename': 'links.txt',
- 'lineno': 18,
+ 'lineno': 20,
'status': 'broken',
'code': 0,
'uri': 'https://www.google.com/image2.png',
@@ -102,6 +102,10 @@ def test_defaults_json(app):
# images should fail
assert "Not Found for url: https://www.google.com/image.png" in \
rowsby["https://www.google.com/image.png"]["info"]
+ # The anchor of the URI for github.com is automatically modified
+ assert 'https://github.com/sphinx-doc/sphinx#documentation' not in rowsby
+ assert 'https://github.com/sphinx-doc/sphinx#user-content-documentation' in rowsby
+ assert 'https://github.com/sphinx-doc/sphinx#user-content-testing' in rowsby
@pytest.mark.sphinx(
@@ -599,3 +603,29 @@ def test_limit_rate_bails_out_after_waiting_max_time(app):
rate_limits)
next_check = worker.limit_rate(FakeResponse())
assert next_check is None
+
+
+class ConnectionResetHandler(http.server.BaseHTTPRequestHandler):
+ def do_HEAD(self):
+ self.connection.close()
+
+ def do_GET(self):
+ self.send_response(200, "OK")
+ self.end_headers()
+
+
+@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
+def test_get_after_head_raises_connection_error(app):
+ with http_server(ConnectionResetHandler):
+ app.build()
+ content = (app.outdir / 'output.txt').read_text()
+ assert not content
+ content = (app.outdir / 'output.json').read_text()
+ assert json.loads(content) == {
+ "filename": "index.rst",
+ "lineno": 1,
+ "status": "working",
+ "code": 0,
+ "uri": "http://localhost:7777/",
+ "info": "",
+ }
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index 0b7ce2396..3680d8651 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -27,6 +27,11 @@ def test_all(app, status, warning):
assert '\n.B term1\n' in content
assert '\nterm2 (\\fBstronged partially\\fP)\n' in content
+ # test samp with braces
+ assert '\n\\fIvariable_only\\fP\n' in content
+ assert '\n\\fIvariable\\fP\\fB and text\\fP\n' in content
+ assert '\n\\fBShow \\fP\\fIvariable\\fP\\fB in the middle\\fP\n' in content
+
assert 'Footnotes' not in content
diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py
index ef4858786..d59c4fc1c 100644
--- a/tests/test_domain_c.py
+++ b/tests/test_domain_c.py
@@ -15,16 +15,20 @@ import pytest
from sphinx import addnodes
from sphinx.addnodes import desc
-from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id
+from sphinx.domains.c import (DefinitionError, DefinitionParser, Symbol, _id_prefix,
+ _macroKeywords, _max_id)
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
+class Config:
+ c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
+ c_paren_attributes = ["paren_attr"]
+ c_extra_keywords = _macroKeywords
+
+
def parse(name, string):
- class Config:
- c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
- c_paren_attributes = ["paren_attr"]
parser = DefinitionParser(string, location=None, config=Config())
parser.allowFallbackExpressionParsing = False
ast = parser.parse_declaration(name, name)
@@ -112,11 +116,8 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
asTextOutput + ';' if asTextOutput is not None else None)
-def test_expressions():
+def test_domain_c_ast_expressions():
def exprCheck(expr, output=None):
- class Config:
- c_id_attributes = ["id_attr"]
- c_paren_attributes = ["paren_attr"]
parser = DefinitionParser(expr, location=None, config=Config())
parser.allowFallbackExpressionParsing = False
ast = parser.parse_expression()
@@ -155,7 +156,8 @@ def test_expressions():
# primary
exprCheck('true')
exprCheck('false')
- ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1']
+ ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1',
+ "0b0'1'0", "00'1'2", "0x0'1'2", "1'2'3"]
unsignedSuffix = ['', 'u', 'U']
longSuffix = ['', 'l', 'L', 'll', 'LL']
for i in ints:
@@ -170,14 +172,18 @@ def test_expressions():
'5e42', '5e+42', '5e-42',
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
- '5.0', '5.0e42', '5.0e+42', '5.0e-42']:
+ '5.0', '5.0e42', '5.0e+42', '5.0e-42',
+ "1'2'3e7'8'9", "1'2'3.e7'8'9",
+ ".4'5'6e7'8'9", "1'2'3.4'5'6e7'8'9"]:
expr = e + suffix
exprCheck(expr)
for e in [
'ApF', 'Ap+F', 'Ap-F',
'A.', 'A.pF', 'A.p+F', 'A.p-F',
'.A', '.ApF', '.Ap+F', '.Ap-F',
- 'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']:
+ 'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F',
+ "A'B'Cp1'2'3", "A'B'C.p1'2'3",
+ ".D'E'Fp1'2'3", "A'B'C.D'E'Fp1'2'3"]:
expr = "0x" + e + suffix
exprCheck(expr)
exprCheck('"abc\\"cba"') # string
@@ -269,7 +275,7 @@ def test_expressions():
exprCheck('a or_eq 5')
-def test_type_definitions():
+def test_domain_c_ast_type_definitions():
check('type', "{key}T", {1: "T"})
check('type', "{key}bool *b", {1: 'b'}, key='typedef')
@@ -290,7 +296,7 @@ def test_type_definitions():
{1: 'gpio_callback_t'}, key='typedef')
-def test_macro_definitions():
+def test_domain_c_ast_macro_definitions():
check('macro', 'M', {1: 'M'})
check('macro', 'M()', {1: 'M'})
check('macro', 'M(arg)', {1: 'M'})
@@ -306,7 +312,7 @@ def test_macro_definitions():
check('macro', 'M(arg1, arg2..., arg3)', {1: 'M'})
-def test_member_definitions():
+def test_domain_c_ast_member_definitions():
check('member', 'void a', {1: 'a'})
check('member', '_Bool a', {1: 'a'})
check('member', 'bool a', {1: 'a'})
@@ -364,7 +370,7 @@ def test_member_definitions():
check('member', 'int b : 3', {1: 'b'})
-def test_function_definitions():
+def test_domain_c_ast_function_definitions():
check('function', 'void f()', {1: 'f'})
check('function', 'void f(int)', {1: 'f'})
check('function', 'void f(int i)', {1: 'f'})
@@ -424,29 +430,29 @@ def test_function_definitions():
check('function', 'void f(void (*p)(int, double), int i)', {1: 'f'})
-def test_nested_name():
+def test_domain_c_ast_nested_name():
check('struct', '{key}.A', {1: "A"})
check('struct', '{key}.A.B', {1: "A.B"})
check('function', 'void f(.A a)', {1: "f"})
check('function', 'void f(.A.B a)', {1: "f"})
-def test_struct_definitions():
+def test_domain_c_ast_struct_definitions():
check('struct', '{key}A', {1: 'A'})
-def test_union_definitions():
+def test_domain_c_ast_union_definitions():
check('union', '{key}A', {1: 'A'})
-def test_enum_definitions():
+def test_domain_c_ast_enum_definitions():
check('enum', '{key}A', {1: 'A'})
check('enumerator', '{key}A', {1: 'A'})
check('enumerator', '{key}A = 42', {1: 'A'})
-def test_anon_definitions():
+def test_domain_c_ast_anon_definitions():
check('struct', '@a', {1: "@a"}, asTextOutput='struct [anonymous]')
check('union', '@a', {1: "@a"}, asTextOutput='union [anonymous]')
check('enum', '@a', {1: "@a"}, asTextOutput='enum [anonymous]')
@@ -454,7 +460,7 @@ def test_anon_definitions():
check('struct', '@a.A', {1: "@a.A"}, asTextOutput='struct [anonymous].A')
-def test_initializers():
+def test_domain_c_ast_initializers():
idsMember = {1: 'v'}
idsFunction = {1: 'f'}
# no init
@@ -473,7 +479,7 @@ def test_initializers():
# TODO: designator-list
-def test_attributes():
+def test_domain_c_ast_attributes():
# style: C++
check('member', '[[]] int f', {1: 'f'})
check('member', '[ [ ] ] int f', {1: 'f'},
@@ -522,6 +528,16 @@ def test_attributes():
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',
{1: 'LGBM_BoosterFree'})
+
+def test_extra_keywords():
+ with pytest.raises(DefinitionError,
+ match='Expected identifier, got user-defined keyword: complex.'):
+ parse('function', 'void f(int complex)')
+ with pytest.raises(DefinitionError,
+ match='Expected identifier, got user-defined keyword: complex.'):
+ parse('function', 'void complex(void)')
+
+
# def test_print():
# # used for getting all the ids out for checking
# for a in ids:
@@ -566,14 +582,14 @@ def extract_role_links(app, filename):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
-def test_build_domain_c(app, status, warning):
+def test_domain_c_build(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
-def test_build_domain_c_namespace(app, status, warning):
+def test_domain_c_build_namespace(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "namespace")
assert len(ws) == 0
@@ -583,7 +599,7 @@ def test_build_domain_c_namespace(app, status, warning):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
-def test_build_domain_c_anon_dup_decl(app, status, warning):
+def test_domain_c_build_anon_dup_decl(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "anon-dup-decl")
assert len(ws) == 2
@@ -592,7 +608,7 @@ def test_build_domain_c_anon_dup_decl(app, status, warning):
@pytest.mark.sphinx(confoverrides={'nitpicky': True})
-def test_build_domain_c_semicolon(app, warning):
+def test_domain_c_build_semicolon(app, warning):
text = """
.. c:member:: int member;
.. c:var:: int var;
@@ -611,7 +627,7 @@ def test_build_domain_c_semicolon(app, warning):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
-def test_build_function_param_target(app, warning):
+def test_domain_c_build_function_param_target(app, warning):
# the anchor for function parameters should be the function
app.builder.build_all()
ws = filter_warnings(warning, "function_param_target")
@@ -624,12 +640,19 @@ def test_build_function_param_target(app, warning):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
-def test_build_ns_lookup(app, warning):
+def test_domain_c_build_ns_lookup(app, warning):
app.builder.build_all()
ws = filter_warnings(warning, "ns_lookup")
assert len(ws) == 0
+@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
+def test_domain_c_build_field_role(app, status, warning):
+ app.builder.build_all()
+ ws = filter_warnings(warning, "field-role")
+ assert len(ws) == 0
+
+
def _get_obj(app, queryName):
domain = app.env.get_domain('c')
for name, dispname, objectType, docname, anchor, prio in domain.get_objects():
@@ -638,49 +661,8 @@ def _get_obj(app, queryName):
return (queryName, "not", "found")
-def test_cfunction(app):
- text = (".. c:function:: PyObject* "
- "PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
- doctree = restructuredtext.parse(app, text)
- assert_node(doctree[1], addnodes.desc, desctype="function",
- domain="c", objtype="function", noindex=False)
-
- entry = _get_obj(app, 'PyType_GenericAlloc')
- assert entry == ('index', 'c.PyType_GenericAlloc', 'function')
-
-
-def test_cmember(app):
- text = ".. c:member:: PyObject* PyTypeObject.tp_bases"
- doctree = restructuredtext.parse(app, text)
- assert_node(doctree[1], addnodes.desc, desctype="member",
- domain="c", objtype="member", noindex=False)
-
- entry = _get_obj(app, 'PyTypeObject.tp_bases')
- assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member')
-
-
-def test_cvar(app):
- text = ".. c:var:: PyObject* PyClass_Type"
- doctree = restructuredtext.parse(app, text)
- assert_node(doctree[1], addnodes.desc, desctype="var",
- domain="c", objtype="var", noindex=False)
-
- entry = _get_obj(app, 'PyClass_Type')
- assert entry == ('index', 'c.PyClass_Type', 'member')
-
-
-def test_noindexentry(app):
- text = (".. c:function:: void f()\n"
- ".. c:function:: void g()\n"
- " :noindexentry:\n")
- doctree = restructuredtext.parse(app, text)
- assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
- assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
- assert_node(doctree[2], addnodes.index, entries=[])
-
-
@pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True})
-def test_intersphinx(tempdir, app, status, warning):
+def test_domain_c_build_intersphinx(tempdir, app, status, warning):
# a splitting of test_ids_vs_tags0 into the primary directives in a remote project,
# and then the references in the test project
origSource = """\
@@ -728,3 +710,44 @@ _var c:member 1 index.html#c.$ -
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0
+
+
+def test_domain_c_parse_cfunction(app):
+ text = (".. c:function:: PyObject* "
+ "PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1], addnodes.desc, desctype="function",
+ domain="c", objtype="function", noindex=False)
+
+ entry = _get_obj(app, 'PyType_GenericAlloc')
+ assert entry == ('index', 'c.PyType_GenericAlloc', 'function')
+
+
+def test_domain_c_parse_cmember(app):
+ text = ".. c:member:: PyObject* PyTypeObject.tp_bases"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1], addnodes.desc, desctype="member",
+ domain="c", objtype="member", noindex=False)
+
+ entry = _get_obj(app, 'PyTypeObject.tp_bases')
+ assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member')
+
+
+def test_domain_c_parse_cvar(app):
+ text = ".. c:var:: PyObject* PyClass_Type"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1], addnodes.desc, desctype="var",
+ domain="c", objtype="var", noindex=False)
+
+ entry = _get_obj(app, 'PyClass_Type')
+ assert entry == ('index', 'c.PyClass_Type', 'member')
+
+
+def test_domain_c_parse_noindexentry(app):
+ text = (".. c:function:: void f()\n"
+ ".. c:function:: void g()\n"
+ " :noindexentry:\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
+ assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
+ assert_node(doctree[2], addnodes.index, entries=[])
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index 52aaad850..e02cd8c1c 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -117,7 +117,7 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
asTextOutput + ';' if asTextOutput is not None else None)
-def test_fundamental_types():
+def test_domain_cpp_ast_fundamental_types():
# see https://en.cppreference.com/w/cpp/language/types
for t, id_v2 in cppDomain._id_fundamental_v2.items():
def makeIdV1():
@@ -126,6 +126,7 @@ def test_fundamental_types():
id = t.replace(" ", "-").replace("long", "l").replace("int", "i")
id = id.replace("bool", "b").replace("char", "c")
id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t")
+ id = id.replace("c8_t", "char8_t")
id = id.replace("c32_t", "char32_t")
return "f__%s" % id
@@ -137,7 +138,7 @@ def test_fundamental_types():
check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2: makeIdV2()})
-def test_expressions():
+def test_domain_cpp_ast_expressions():
def exprCheck(expr, id, id4=None):
ids = 'IE1CIA%s_1aE'
# call .format() on the expr to unescape double curly braces
@@ -173,31 +174,36 @@ def test_expressions():
exprCheck('nullptr', 'LDnE')
exprCheck('true', 'L1E')
exprCheck('false', 'L0E')
- ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1']
+ ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1',
+ "0b0'1'0", "00'1'2", "0x0'1'2", "1'2'3"]
unsignedSuffix = ['', 'u', 'U']
longSuffix = ['', 'l', 'L', 'll', 'LL']
for i in ints:
for u in unsignedSuffix:
for l in longSuffix:
expr = i + u + l
- exprCheck(expr, 'L' + expr + 'E')
+ exprCheck(expr, 'L' + expr.replace("'", "") + 'E')
expr = i + l + u
- exprCheck(expr, 'L' + expr + 'E')
+ exprCheck(expr, 'L' + expr.replace("'", "") + 'E')
decimalFloats = ['5e42', '5e+42', '5e-42',
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
- '5.0', '5.0e42', '5.0e+42', '5.0e-42']
+ '5.0', '5.0e42', '5.0e+42', '5.0e-42',
+ "1'2'3e7'8'9", "1'2'3.e7'8'9",
+ ".4'5'6e7'8'9", "1'2'3.4'5'6e7'8'9"]
hexFloats = ['ApF', 'Ap+F', 'Ap-F',
'A.', 'A.pF', 'A.p+F', 'A.p-F',
'.A', '.ApF', '.Ap+F', '.Ap-F',
- 'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']
+ 'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F',
+ "A'B'Cp1'2'3", "A'B'C.p1'2'3",
+ ".D'E'Fp1'2'3", "A'B'C.D'E'Fp1'2'3"]
for suffix in ['', 'f', 'F', 'l', 'L']:
for e in decimalFloats:
expr = e + suffix
- exprCheck(expr, 'L' + expr + 'E')
+ exprCheck(expr, 'L' + expr.replace("'", "") + 'E')
for e in hexFloats:
expr = "0x" + e + suffix
- exprCheck(expr, 'L' + expr + 'E')
+ exprCheck(expr, 'L' + expr.replace("'", "") + 'E')
exprCheck('"abc\\"cba"', 'LA8_KcE') # string
exprCheck('this', 'fpT')
# character literals
@@ -210,13 +216,13 @@ def test_expressions():
exprCheck("{}'{}'".format(p, c), t + val)
# user-defined literals
for i in ints:
- exprCheck(i + '_udl', 'clL_Zli4_udlEL' + i + 'EE')
- exprCheck(i + 'uludl', 'clL_Zli5uludlEL' + i + 'EE')
+ exprCheck(i + '_udl', 'clL_Zli4_udlEL' + i.replace("'", "") + 'EE')
+ exprCheck(i + 'uludl', 'clL_Zli5uludlEL' + i.replace("'", "") + 'EE')
for f in decimalFloats:
- exprCheck(f + '_udl', 'clL_Zli4_udlEL' + f + 'EE')
- exprCheck(f + 'fudl', 'clL_Zli4fudlEL' + f + 'EE')
+ exprCheck(f + '_udl', 'clL_Zli4_udlEL' + f.replace("'", "") + 'EE')
+ exprCheck(f + 'fudl', 'clL_Zli4fudlEL' + f.replace("'", "") + 'EE')
for f in hexFloats:
- exprCheck('0x' + f + '_udl', 'clL_Zli4_udlEL0x' + f + 'EE')
+ exprCheck('0x' + f + '_udl', 'clL_Zli4_udlEL0x' + f.replace("'", "") + 'EE')
for p, t in charPrefixAndIds:
for c, val in chars:
exprCheck("{}'{}'_udl".format(p, c), 'clL_Zli4_udlE' + t + val + 'E')
@@ -351,7 +357,7 @@ def test_expressions():
exprCheck('a(b(c, 1 + d...)..., e(f..., g))', 'cl1aspcl1b1cspplL1E1dEcl1esp1f1gEE')
-def test_type_definitions():
+def test_domain_cpp_ast_type_definitions():
check("type", "public bool b", {1: "b", 2: "1b"}, "{key}bool b", key='typedef')
check("type", "{key}bool A::b", {1: "A::b", 2: "N1A1bE"}, key='typedef')
check("type", "{key}bool *b", {1: "b", 2: "1b"}, key='typedef')
@@ -396,7 +402,7 @@ def test_type_definitions():
check('type', '{key}T = Q<A::operator bool>', {2: '1T'}, key='using')
-def test_concept_definitions():
+def test_domain_cpp_ast_concept_definitions():
check('concept', 'template<typename Param> {key}A::B::Concept',
{2: 'I0EN1A1B7ConceptE'})
check('concept', 'template<typename A, typename B, typename ...C> {key}Foo',
@@ -407,7 +413,7 @@ def test_concept_definitions():
parse('concept', 'template<typename T> template<typename U> {key}Foo')
-def test_member_definitions():
+def test_domain_cpp_ast_member_definitions():
check('member', ' const std::string & name = 42',
{1: "name__ssCR", 2: "4name"}, output='const std::string &name = 42')
check('member', ' const std::string & name', {1: "name__ssCR", 2: "4name"},
@@ -435,8 +441,11 @@ def test_member_definitions():
# check('member', 'int b : (true ? 8 : a) = 42', {1: 'b__i', 2: '1b'})
check('member', 'int b : 1 || new int{0}', {1: 'b__i', 2: '1b'})
+ check('member', 'inline int n', {1: 'n__i', 2: '1n'})
+ check('member', 'constinit int n', {1: 'n__i', 2: '1n'})
-def test_function_definitions():
+
+def test_domain_cpp_ast_function_definitions():
check('function', 'void f(volatile int)', {1: "f__iV", 2: "1fVi"})
check('function', 'void f(std::size_t)', {1: "f__std::s", 2: "1fNSt6size_tE"})
check('function', 'operator bool() const', {1: "castto-b-operatorC", 2: "NKcvbEv"})
@@ -565,6 +574,9 @@ def test_function_definitions():
check("function", "void f(int *volatile const p)", {1: "f__iPVC", 2: "1fPVCi"})
check('function', 'extern int f()', {1: 'f', 2: '1fv'})
+ check('function', 'consteval int f()', {1: 'f', 2: '1fv'})
+
+ check('function', 'explicit(true) void f()', {1: 'f', 2: '1fv'})
check('function', 'decltype(auto) f()', {1: 'f', 2: "1fv"})
@@ -624,7 +636,7 @@ def test_function_definitions():
check('function', 'void f(void (*p)(int, double), int i)', {2: '1fPFvidEi'})
-def test_operators():
+def test_domain_cpp_ast_operators():
check('function', 'void operator new()', {1: "new-operator", 2: "nwv"})
check('function', 'void operator new[]()', {1: "new-array-operator", 2: "nav"})
check('function', 'void operator delete()', {1: "delete-operator", 2: "dlv"})
@@ -684,14 +696,14 @@ def test_operators():
check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"})
-def test_nested_name():
+def test_domain_cpp_ast_nested_name():
check('class', '{key}::A', {1: "A", 2: "1A"})
check('class', '{key}::A::B', {1: "A::B", 2: "N1A1BE"})
check('function', 'void f(::A a)', {1: "f__A", 2: "1f1A"})
check('function', 'void f(::A::B a)', {1: "f__A::B", 2: "1fN1A1BE"})
-def test_class_definitions():
+def test_domain_cpp_ast_class_definitions():
check('class', 'public A', {1: "A", 2: "1A"}, output='{key}A')
check('class', 'private {key}A', {1: "A", 2: "1A"})
check('class', '{key}A final', {1: 'A', 2: '1A'})
@@ -722,11 +734,11 @@ def test_class_definitions():
{2: 'I_DpiE1TIJX(Is)EEE', 3: 'I_DpiE1TIJX2IsEEE'})
-def test_union_definitions():
+def test_domain_cpp_ast_union_definitions():
check('union', '{key}A', {2: "1A"})
-def test_enum_definitions():
+def test_domain_cpp_ast_enum_definitions():
check('enum', '{key}A', {2: "1A"})
check('enum', '{key}A : std::underlying_type<B>::type', {2: "1A"})
check('enum', '{key}A : unsigned int', {2: "1A"})
@@ -737,7 +749,7 @@ def test_enum_definitions():
check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
-def test_anon_definitions():
+def test_domain_cpp_ast_anon_definitions():
check('class', '@a', {3: "Ut1_a"}, asTextOutput='class [anonymous]')
check('union', '@a', {3: "Ut1_a"}, asTextOutput='union [anonymous]')
check('enum', '@a', {3: "Ut1_a"}, asTextOutput='enum [anonymous]')
@@ -748,7 +760,7 @@ def test_anon_definitions():
asTextOutput='int f(int [anonymous])')
-def test_templates():
+def test_domain_cpp_ast_templates():
check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> {key}A<T>")
# first just check which objects support templating
check('class', "template<> {key}A", {2: "IE1A"})
@@ -854,7 +866,16 @@ def test_templates():
check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
-def test_requires_clauses():
+def test_domain_cpp_ast_placeholder_types():
+ check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'})
+ check('function', 'void f(const Sortable auto &v)', {1: 'f__SortableCR', 2: '1fRK8Sortable'})
+ check('function', 'void f(Sortable decltype(auto) &v)', {1: 'f__SortableR', 2: '1fR8Sortable'})
+ check('function', 'void f(const Sortable decltype(auto) &v)', {1: 'f__SortableCR', 2: '1fRK8Sortable'})
+ check('function', 'void f(Sortable decltype ( auto ) &v)', {1: 'f__SortableR', 2: '1fR8Sortable'},
+ output='void f(Sortable decltype(auto) &v)')
+
+
+def test_domain_cpp_ast_requires_clauses():
check('function', 'template<typename T> requires A auto f() -> void requires B',
{4: 'I0EIQaa1A1BE1fvv'})
check('function', 'template<typename T> requires A || B or C void f()',
@@ -863,7 +884,7 @@ def test_requires_clauses():
{4: 'I0EIQooaa1A1Baa1C1DE1fvv'})
-def test_template_args():
+def test_domain_cpp_ast_template_args():
# from breathe#218
check('function',
"template<typename F> "
@@ -878,7 +899,7 @@ def test_template_args():
key='using')
-def test_initializers():
+def test_domain_cpp_ast_initializers():
idsMember = {1: 'v__T', 2: '1v'}
idsFunction = {1: 'f__T', 2: '1f1T'}
idsTemplate = {2: 'I_1TE1fv', 4: 'I_1TE1fvv'}
@@ -912,7 +933,7 @@ def test_initializers():
check('member', 'T v = T{}', idsMember)
-def test_attributes():
+def test_domain_cpp_ast_attributes():
# style: C++
check('member', '[[]] int f', {1: 'f__i', 2: '1f'})
check('member', '[ [ ] ] int f', {1: 'f__i', 2: '1f'},
@@ -960,7 +981,7 @@ def test_attributes():
check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'})
-def test_xref_parsing():
+def test_domain_cpp_ast_xref_parsing():
def check(target):
class Config:
cpp_id_attributes = ["id_attr"]
@@ -993,7 +1014,7 @@ def filter_warnings(warning, file):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
-def test_build_domain_cpp_multi_decl_lookup(app, status, warning):
+def test_domain_cpp_build_multi_decl_lookup(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "lookup-key-overload")
assert len(ws) == 0
@@ -1003,7 +1024,7 @@ def test_build_domain_cpp_multi_decl_lookup(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
-def test_build_domain_cpp_warn_template_param_qualified_name(app, status, warning):
+def test_domain_cpp_build_warn_template_param_qualified_name(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "warn-template-param-qualified-name")
assert len(ws) == 2
@@ -1012,14 +1033,14 @@ def test_build_domain_cpp_warn_template_param_qualified_name(app, status, warnin
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
-def test_build_domain_cpp_backslash_ok_true(app, status, warning):
+def test_domain_cpp_build_backslash_ok_true(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "backslash")
assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
-def test_build_domain_cpp_semicolon(app, status, warning):
+def test_domain_cpp_build_semicolon(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "semicolon")
assert len(ws) == 0
@@ -1027,7 +1048,7 @@ def test_build_domain_cpp_semicolon(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp',
confoverrides={'nitpicky': True, 'strip_signature_backslash': True})
-def test_build_domain_cpp_backslash_ok_false(app, status, warning):
+def test_domain_cpp_build_backslash_ok_false(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "backslash")
assert len(ws) == 1
@@ -1035,7 +1056,7 @@ def test_build_domain_cpp_backslash_ok_false(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
-def test_build_domain_cpp_anon_dup_decl(app, status, warning):
+def test_domain_cpp_build_anon_dup_decl(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "anon-dup-decl")
assert len(ws) == 2
@@ -1044,7 +1065,7 @@ def test_build_domain_cpp_anon_dup_decl(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp')
-def test_build_domain_cpp_misuse_of_roles(app, status, warning):
+def test_domain_cpp_build_misuse_of_roles(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "roles-targets-ok")
assert len(ws) == 0
@@ -1092,7 +1113,7 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': True})
-def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, warning):
+def test_domain_cpp_build_with_add_function_parentheses_is_True(app, status, warning):
app.builder.build_all()
def check(spec, text, file):
@@ -1133,7 +1154,7 @@ def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, war
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': False})
-def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, warning):
+def test_domain_cpp_build_with_add_function_parentheses_is_False(app, status, warning):
app.builder.build_all()
def check(spec, text, file):
@@ -1174,7 +1195,7 @@ def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, wa
@pytest.mark.sphinx(testroot='domain-cpp')
-def test_xref_consistency(app, status, warning):
+def test_domain_cpp_build_xref_consistency(app, status, warning):
app.builder.build_all()
test = 'xref_consistency.html'
@@ -1237,33 +1258,15 @@ not found in `{test}`
assert any_role.classes == texpr_role.content_classes['a'], expect
-def test_noindexentry(app):
- text = (".. cpp:function:: void f()\n"
- ".. cpp:function:: void g()\n"
- " :noindexentry:\n")
- doctree = restructuredtext.parse(app, text)
- assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
- assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)])
- assert_node(doctree[2], addnodes.index, entries=[])
-
-
-def test_mix_decl_duplicate(app, warning):
- # Issue 8270
- text = (".. cpp:struct:: A\n"
- ".. cpp:function:: void A()\n"
- ".. cpp:struct:: A\n")
- restructuredtext.parse(app, text)
- ws = warning.getvalue().split("\n")
- assert len(ws) == 5
- assert "index.rst:2: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[0]
- assert "Declaration is '.. cpp:function:: void A()'." in ws[1]
- assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2]
- assert "Declaration is '.. cpp:struct:: A'." in ws[3]
- assert ws[4] == ""
+@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
+def test_domain_cpp_build_field_role(app, status, warning):
+ app.builder.build_all()
+ ws = filter_warnings(warning, "field-role")
+ assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-cpp-intersphinx', confoverrides={'nitpicky': True})
-def test_intersphinx(tempdir, app, status, warning):
+def test_domain_cpp_build_intersphinx(tempdir, app, status, warning):
origSource = """\
.. cpp:class:: _class
.. cpp:struct:: _struct
@@ -1323,3 +1326,28 @@ _var cpp:member 1 index.html#_CPPv44$ -
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0
+
+
+def test_domain_cpp_parse_noindexentry(app):
+ text = (".. cpp:function:: void f()\n"
+ ".. cpp:function:: void g()\n"
+ " :noindexentry:\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
+ assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)])
+ assert_node(doctree[2], addnodes.index, entries=[])
+
+
+def test_domain_cpp_parse_mix_decl_duplicate(app, warning):
+ # Issue 8270
+ text = (".. cpp:struct:: A\n"
+ ".. cpp:function:: void A()\n"
+ ".. cpp:struct:: A\n")
+ restructuredtext.parse(app, text)
+ ws = warning.getvalue().split("\n")
+ assert len(ws) == 5
+ assert "index.rst:2: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[0]
+ assert "Declaration is '.. cpp:function:: void A()'." in ws[1]
+ assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2]
+ assert "Declaration is '.. cpp:struct:: A'." in ws[3]
+ assert ws[4] == ""
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index e66f066d4..2ee2d5f2d 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -1147,6 +1147,9 @@ def test_python_python_use_unqualified_type_names(app, status, warning):
assert ('<span class="n"><a class="reference internal" href="#foo.Name" title="foo.Name">'
'<span class="pre">Name</span></a></span>' in content)
assert '<span class="n"><span class="pre">foo.Age</span></span>' in content
+ assert ('<p><strong>name</strong> (<a class="reference internal" href="#foo.Name" '
+ 'title="foo.Name"><em>Name</em></a>) – blah blah</p>' in content)
+ assert '<p><strong>age</strong> (<em>foo.Age</em>) – blah blah</p>' in content
@pytest.mark.sphinx('html', testroot='domain-py-python_use_unqualified_type_names',
@@ -1157,6 +1160,9 @@ def test_python_python_use_unqualified_type_names_disabled(app, status, warning)
assert ('<span class="n"><a class="reference internal" href="#foo.Name" title="foo.Name">'
'<span class="pre">foo.Name</span></a></span>' in content)
assert '<span class="n"><span class="pre">foo.Age</span></span>' in content
+ assert ('<p><strong>name</strong> (<a class="reference internal" href="#foo.Name" '
+ 'title="foo.Name"><em>foo.Name</em></a>) – blah blah</p>' in content)
+ assert '<p><strong>age</strong> (<em>foo.Age</em>) – blah blah</p>' in content
@pytest.mark.sphinx('dummy', testroot='domain-py-xref-warning')
diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_ext_autodoc_autoattribute.py
index 5e7220234..20317b8da 100644
--- a/tests/test_ext_autodoc_autoattribute.py
+++ b/tests/test_ext_autodoc_autoattribute.py
@@ -101,6 +101,17 @@ def test_autoattribute_instance_variable_in_alias(app):
@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autoattribute_instance_variable_without_comment(app):
+ actual = do_autodoc(app, 'attribute', 'target.instance_variable.Bar.attr4')
+ assert list(actual) == [
+ '',
+ '.. py:attribute:: Bar.attr4',
+ ' :module: target.instance_variable',
+ '',
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoattribute_slots_variable_list(app):
actual = do_autodoc(app, 'attribute', 'target.slots.Foo.attr')
assert list(actual) == [
diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py
index e0f08ea77..0f0580404 100644
--- a/tests/test_ext_autodoc_configs.py
+++ b/tests/test_ext_autodoc_configs.py
@@ -554,10 +554,26 @@ def test_autodoc_typehints_signature(app):
'.. py:module:: target.typehints',
'',
'',
+ '.. py:data:: CONST1',
+ ' :module: target.typehints',
+ ' :type: int',
+ '',
+ '',
'.. py:class:: Math(s: str, o: Optional[Any] = None)',
' :module: target.typehints',
'',
'',
+ ' .. py:attribute:: Math.CONST1',
+ ' :module: target.typehints',
+ ' :type: int',
+ '',
+ '',
+ ' .. py:attribute:: Math.CONST2',
+ ' :module: target.typehints',
+ ' :type: int',
+ ' :value: 1',
+ '',
+ '',
' .. py:method:: Math.decr(a: int, b: int = 1) -> int',
' :module: target.typehints',
'',
@@ -574,6 +590,11 @@ def test_autodoc_typehints_signature(app):
' :module: target.typehints',
'',
'',
+ ' .. py:property:: Math.prop',
+ ' :module: target.typehints',
+ ' :type: int',
+ '',
+ '',
'.. py:class:: NewAnnotation(i: int)',
' :module: target.typehints',
'',
@@ -620,10 +641,23 @@ def test_autodoc_typehints_none(app):
'.. py:module:: target.typehints',
'',
'',
+ '.. py:data:: CONST1',
+ ' :module: target.typehints',
+ '',
+ '',
'.. py:class:: Math(s, o=None)',
' :module: target.typehints',
'',
'',
+ ' .. py:attribute:: Math.CONST1',
+ ' :module: target.typehints',
+ '',
+ '',
+ ' .. py:attribute:: Math.CONST2',
+ ' :module: target.typehints',
+ ' :value: 1',
+ '',
+ '',
' .. py:method:: Math.decr(a, b=1)',
' :module: target.typehints',
'',
@@ -640,6 +674,10 @@ def test_autodoc_typehints_none(app):
' :module: target.typehints',
'',
'',
+ ' .. py:property:: Math.prop',
+ ' :module: target.typehints',
+ '',
+ '',
'.. py:class:: NewAnnotation(i)',
' :module: target.typehints',
'',
diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py
index ebe2c0f38..973fc3699 100644
--- a/tests/test_ext_math.py
+++ b/tests/test_ext_math.py
@@ -256,3 +256,16 @@ def test_mathjax_is_not_installed_if_no_equations(app, status, warning):
content = (app.outdir / 'index.html').read_text()
assert 'MathJax.js' not in content
+
+
+@pytest.mark.sphinx('html', testroot='ext-math',
+ confoverrides={'extensions': ['sphinx.ext.mathjax']})
+def test_mathjax_is_installed_if_no_equations_when_forced(app, status, warning):
+ app.set_html_assets_policy('always')
+ app.builder.build_all()
+
+ content = (app.outdir / 'index.html').read_text()
+ assert MATHJAX_URL in content
+
+ content = (app.outdir / 'nomath.html').read_text()
+ assert MATHJAX_URL in content
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 73d94166e..7791b4aee 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -622,7 +622,7 @@ def test_html_meta(app):
assert expected_expr in result
expected_expr = '<meta content="I18N, SPHINX, MARKUP" name="keywords" />'
assert expected_expr in result
- expected_expr = '<p class="caption"><span class="caption-text">HIDDEN TOC</span></p>'
+ expected_expr = '<p class="caption" role="heading"><span class="caption-text">HIDDEN TOC</span></p>'
assert expected_expr in result
diff --git a/tests/test_pycode_ast.py b/tests/test_pycode_ast.py
index e80062351..6ae7050da 100644
--- a/tests/test_pycode_ast.py
+++ b/tests/test_pycode_ast.py
@@ -53,8 +53,9 @@ from sphinx.pycode import ast
("+ a", "+ a"), # UAdd
("- 1", "- 1"), # UnaryOp
("- a", "- a"), # USub
- ("(1, 2, 3)", "(1, 2, 3)"), # Tuple
+ ("(1, 2, 3)", "(1, 2, 3)"), # Tuple
("()", "()"), # Tuple (empty)
+ ("(1,)", "(1,)"), # Tuple (single item)
])
def test_unparse(source, expected):
module = ast.parse(source)
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 94144ef22..6c2f70ec4 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -10,6 +10,7 @@
import time
from io import StringIO
+from os import path
import pytest
@@ -250,3 +251,18 @@ def test_extensions(tempdir):
ns = {}
exec(conffile.read_text(), ns)
assert ns['extensions'] == ['foo', 'bar', 'baz']
+
+
+def test_exits_when_existing_confpy(monkeypatch):
+ # The code detects existing conf.py with path.isfile()
+ # so we mock it as True with pytest's monkeypatch
+ def mock_isfile(path):
+ return True
+ monkeypatch.setattr(path, 'isfile', mock_isfile)
+
+ qs.term_input = mock_input({
+ 'Please enter a new root path (or just Enter to exit)': ''
+ })
+ d = {}
+ with pytest.raises(SystemExit):
+ qs.ask_user(d)