summaryrefslogtreecommitdiff
path: root/tests/test_domain_c.py
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2021-01-24 16:34:47 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2021-01-24 16:34:47 +0900
commit51d500833e391c182f536e83a5d62d5e90ce8ca9 (patch)
treefb854309b759773feb83e7e4bbc91e3ed3cb2b00 /tests/test_domain_c.py
parent375fb52fe402d46d633e321ce8f20c1aa61c49b9 (diff)
parent41ee2d6e6595d0eefb4a2b752fd79a3451382d5a (diff)
downloadsphinx-git-51d500833e391c182f536e83a5d62d5e90ce8ca9.tar.gz
Merge branch '3.x' into 7774_remove_develop.rst
Diffstat (limited to 'tests/test_domain_c.py')
-rw-r--r--tests/test_domain_c.py163
1 files changed, 138 insertions, 25 deletions
diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py
index 10a20b9d1..2cfcf74fa 100644
--- a/tests/test_domain_c.py
+++ b/tests/test_domain_c.py
@@ -4,14 +4,19 @@
Tests the C Domain
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+
+import zlib
+from xml.etree import ElementTree
+
import pytest
from sphinx import addnodes
-from sphinx.domains.c import DefinitionParser, DefinitionError
-from sphinx.domains.c import _max_id, _id_prefix, Symbol
+from sphinx.addnodes import desc
+from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id
+from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
@@ -51,8 +56,8 @@ def _check(name, input, idDict, output, key, asTextOutput):
print("Result: ", res)
print("Expected: ", outputAst)
raise DefinitionError("")
- rootSymbol = Symbol(None, None, None, None)
- symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
+ rootSymbol = Symbol(None, None, None, None, None)
+ symbol = rootSymbol.add_declaration(ast, docname="TestDoc", line=42)
parentNode = addnodes.desc()
signode = addnodes.desc_signature(input, '')
parentNode += signode
@@ -73,12 +78,12 @@ def _check(name, input, idDict, output, key, asTextOutput):
idExpected.append(idExpected[i - 1])
idActual = [None]
for i in range(1, _max_id + 1):
- #try:
+ # try:
id = ast.get_id(version=i)
assert id is not None
idActual.append(id[len(_id_prefix[i]):])
- #except NoOldIdError:
- # idActual.append(None)
+ # except NoOldIdError:
+ # idActual.append(None)
res = [True]
for i in range(1, _max_id + 1):
@@ -92,7 +97,7 @@ def _check(name, input, idDict, output, key, asTextOutput):
print("Error in id version %d." % i)
print("result: %s" % idActual[i])
print("expected: %s" % idExpected[i])
- #print(rootSymbol.dump(0))
+ # print(rootSymbol.dump(0))
raise DefinitionError("")
@@ -104,7 +109,7 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
if name != 'macro':
# Second, check with semicolon
_check(name, input + ' ;', idDict, output + ';', key,
- asTextOutput + ';' if asTextOutput is not None else None)
+ asTextOutput + ';' if asTextOutput is not None else None)
def test_expressions():
@@ -295,6 +300,10 @@ def test_macro_definitions():
check('macro', 'M(arg, ...)', {1: 'M'})
check('macro', 'M(arg1, arg2, ...)', {1: 'M'})
check('macro', 'M(arg1, arg2, arg3, ...)', {1: 'M'})
+ # GNU extension
+ check('macro', 'M(arg1, arg2, arg3...)', {1: 'M'})
+ with pytest.raises(DefinitionError):
+ check('macro', 'M(arg1, arg2..., arg3)', {1: 'M'})
def test_member_definitions():
@@ -416,7 +425,7 @@ def test_nested_name():
check('function', 'void f(.A.B a)', {1: "f"})
-def test_union_definitions():
+def test_struct_definitions():
check('struct', '{key}A', {1: 'A'})
@@ -469,12 +478,14 @@ def test_attributes():
check('member', '__attribute__(()) int f', {1: 'f'})
check('member', '__attribute__((a)) int f', {1: 'f'})
check('member', '__attribute__((a, b)) int f', {1: 'f'})
+ check('member', '__attribute__((optimize(3))) int f', {1: 'f'})
+ check('member', '__attribute__((format(printf, 1, 2))) int f', {1: 'f'})
# style: user-defined id
check('member', 'id_attr int f', {1: 'f'})
# style: user-defined paren
check('member', 'paren_attr() int f', {1: 'f'})
check('member', 'paren_attr(a) int f', {1: 'f'})
- check('member', 'paren_attr("") int f',{1: 'f'})
+ check('member', 'paren_attr("") int f', {1: 'f'})
check('member', 'paren_attr(()[{}][]{}) int f', {1: 'f'})
with pytest.raises(DefinitionError):
parse('member', 'paren_attr(() int f')
@@ -490,17 +501,16 @@ def test_attributes():
parse('member', 'paren_attr({]}) int f')
# position: decl specs
- check('function', 'static inline __attribute__(()) void f()',
- {1: 'f'},
+ check('function', 'static inline __attribute__(()) void f()', {1: 'f'},
output='__attribute__(()) static inline void f()')
- check('function', '[[attr1]] [[attr2]] void f()',
- {1: 'f'},
- output='[[attr1]] [[attr2]] void f()')
+ check('function', '[[attr1]] [[attr2]] void f()', {1: 'f'})
# position: declarator
check('member', 'int *[[attr]] i', {1: 'i'})
check('member', 'int *const [[attr]] volatile i', {1: 'i'},
output='int *[[attr]] volatile const i')
check('member', 'int *[[attr]] *i', {1: 'i'})
+ # position: parameters
+ check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f'})
# issue michaeljones/breathe#500
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',
@@ -514,7 +524,7 @@ def test_attributes():
def filter_warnings(warning, file):
- lines = warning.getvalue().split("\n");
+ lines = warning.getvalue().split("\n")
res = [l for l in lines if "domain-c" in l and "{}.rst".format(file) in l and
"WARNING: document isn't included in any toctree" not in l]
print("Filtered warnings for file '{}':".format(file))
@@ -523,6 +533,25 @@ def filter_warnings(warning, file):
return res
+def extract_role_links(app, filename):
+ t = (app.outdir / filename).read_text()
+ lis = [l for l in t.split('\n') if l.startswith("<li")]
+ entries = []
+ for l in lis:
+ li = ElementTree.fromstring(l)
+ aList = list(li.iter('a'))
+ assert len(aList) == 1
+ a = aList[0]
+ target = a.attrib['href'].lstrip('#')
+ title = a.attrib['title']
+ assert len(a) == 1
+ code = a[0]
+ assert code.tag == 'code'
+ text = ''.join(code.itertext())
+ entries.append((target, title, text))
+ return entries
+
+
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_domain_c(app, status, warning):
app.builder.build_all()
@@ -556,6 +585,34 @@ def test_build_domain_c_semicolon(app, status, warning):
assert len(ws) == 0
+@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
+def test_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")
+ assert len(ws) == 0
+ entries = extract_role_links(app, "function_param_target.html")
+ assert entries == [
+ ('c.f', 'i', 'i'),
+ ('c.f', 'f.i', 'f.i'),
+ ]
+
+
+@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
+def test_build_ns_lookup(app, warning):
+ app.builder.build_all()
+ ws = filter_warnings(warning, "ns_lookup")
+ 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():
+ if name == queryName:
+ return (docname, anchor, objectType)
+ return (queryName, "not", "found")
+
+
def test_cfunction(app):
text = (".. c:function:: PyObject* "
"PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
@@ -563,8 +620,7 @@ def test_cfunction(app):
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
- domain = app.env.get_domain('c')
- entry = domain.objects.get('PyType_GenericAlloc')
+ entry = _get_obj(app, 'PyType_GenericAlloc')
assert entry == ('index', 'c.PyType_GenericAlloc', 'function')
@@ -574,8 +630,7 @@ def test_cmember(app):
assert_node(doctree[1], addnodes.desc, desctype="member",
domain="c", objtype="member", noindex=False)
- domain = app.env.get_domain('c')
- entry = domain.objects.get('PyTypeObject.tp_bases')
+ entry = _get_obj(app, 'PyTypeObject.tp_bases')
assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member')
@@ -585,6 +640,64 @@ def test_cvar(app):
assert_node(doctree[1], addnodes.desc, desctype="var",
domain="c", objtype="var", noindex=False)
- domain = app.env.get_domain('c')
- entry = domain.objects.get('PyClass_Type')
- assert entry == ('index', 'c.PyClass_Type', 'var')
+ 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):
+ origSource = """\
+.. c:member:: int _member
+.. c:var:: int _var
+.. c:function:: void _function()
+.. c:macro:: _macro
+.. c:struct:: _struct
+.. c:union:: _union
+.. c:enum:: _enum
+
+ .. c:enumerator:: _enumerator
+
+.. c:type:: _type
+.. c:function:: void _functionParam(int param)
+""" # noqa
+ inv_file = tempdir / 'inventory'
+ inv_file.write_bytes(b'''\
+# Sphinx inventory version 2
+# Project: C Intersphinx Test
+# Version:
+# The remainder of this file is compressed using zlib.
+''' + zlib.compress(b'''\
+_enum c:enum 1 index.html#c.$ -
+_enum._enumerator c:enumerator 1 index.html#c.$ -
+_enumerator c:enumerator 1 index.html#c._enum.$ -
+_function c:function 1 index.html#c.$ -
+_functionParam c:function 1 index.html#c.$ -
+_functionParam.param c:functionParam 1 index.html#c._functionParam -
+_macro c:macro 1 index.html#c.$ -
+_member c:member 1 index.html#c.$ -
+_struct c:struct 1 index.html#c.$ -
+_type c:type 1 index.html#c.$ -
+_union c:union 1 index.html#c.$ -
+_var c:member 1 index.html#c.$ -
+''')) # noqa
+ app.config.intersphinx_mapping = {
+ 'https://localhost/intersphinx/c/': inv_file,
+ }
+ app.config.intersphinx_cache_limit = 0
+ # load the inventory and check if it's done correctly
+ normalize_intersphinx_mapping(app, app.config)
+ load_mappings(app)
+
+ app.builder.build_all()
+ ws = filter_warnings(warning, "index")
+ assert len(ws) == 0