summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_build_latex.py2
-rw-r--r--tests/test_domain_cpp.py91
-rw-r--r--tests/test_domain_py.py72
-rw-r--r--tests/test_ext_napoleon.py18
-rw-r--r--tests/test_ext_napoleon_docstring.py45
5 files changed, 218 insertions, 10 deletions
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 3728b3da7..8f7a2e85a 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -1519,7 +1519,7 @@ def test_latex_labels(app, status, warning):
def test_latex_figure_in_admonition(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'python.tex').read_text(encoding='utf8')
- assert(r'\begin{figure}[H]' in result)
+ assert r'\begin{figure}[H]' in result
def test_default_latex_documents():
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index ee8da3d39..5e9bc6aa3 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -841,9 +841,9 @@ def test_domain_cpp_ast_templates():
check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar',
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
check('class', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar<id_0, id_1, id_2>',
- {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'})
+ {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar<id_0, id_1, id_2...>',
- {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'})
+ {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
check('class', 'template<> Concept{{U}} {key}A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'})
@@ -876,6 +876,9 @@ def test_domain_cpp_ast_templates():
# defaulted constrained type parameters
check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
+ # pack expansion after non-type template parameter
+ check('type', 'template<int (X::*)(bool)...> {key}A', {2: 'I_DpM1XFibEE1A'}, key='using')
+
def test_domain_cpp_ast_placeholder_types():
check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'})
@@ -901,7 +904,7 @@ def test_domain_cpp_ast_requires_clauses():
'template<typename T> requires R<T> ' +
'template<typename U> requires S<T> ' +
'void A<T>::f() requires B',
- {4: 'I0EIQ1RI1TEEI0EIQaa1SI1TE1BEN1AI1TE1fEvv'})
+ {4: 'I0EIQ1RI1TEEI0EIQaa1SI1TE1BEN1A1fEvv'})
check('function',
'template<template<typename T> requires R<T> typename X> ' +
'void f()',
@@ -1040,6 +1043,38 @@ def test_domain_cpp_ast_xref_parsing():
check('T f()')
+@pytest.mark.parametrize(
+ 'param,is_pack',
+ [('typename', False),
+ ('typename T', False),
+ ('typename...', True),
+ ('typename... T', True),
+ ('int', False),
+ ('int N', False),
+ ('int* N', False),
+ ('int& N', False),
+ ('int&... N', True),
+ ('int*... N', True),
+ ('int...', True),
+ ('int... N', True),
+ ('auto', False),
+ ('auto...', True),
+ ('int X::*', False),
+ ('int X::*...', True),
+ ('int (X::*)(bool)', False),
+ ('int (X::*x)(bool)', False),
+ ('int (X::*)(bool)...', True),
+ ('template<typename> class', False),
+ ('template<typename> class...', True),
+ ])
+def test_domain_cpp_template_parameters_is_pack(param: str, is_pack: bool):
+ def parse_template_parameter(param: str):
+ ast = parse('type', 'template<' + param + '> X')
+ return ast.templatePrefix.templates[0].params[0]
+ ast = parse_template_parameter(param)
+ assert ast.isPack == is_pack
+
+
# def test_print():
# # used for getting all the ids out for checking
# for a in ids:
@@ -1395,3 +1430,53 @@ def test_domain_cpp_parse_mix_decl_duplicate(app, warning):
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] == ""
+
+
+# For some reason, using the default testroot of "root" leads to the contents of
+# `test-root/objects.txt` polluting the symbol table depending on the test
+# execution order. Using a testroot of "config" seems to avoid that problem.
+@pytest.mark.sphinx(testroot='config')
+def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params):
+ args, kwargs = app_params
+
+ text1 = (".. cpp:class:: template <typename T> A\n")
+ text2 = (".. cpp:class:: template <typename T> template <typename U> A<T>::B\n")
+
+ app1 = make_app(*args, **kwargs)
+ restructuredtext.parse(app=app1, text=text1, docname='text1')
+ root1 = app1.env.domaindata['cpp']['root_symbol']
+
+ assert root1.dump(1) == (
+ ' ::\n'
+ ' template<typename T> \n'
+ ' A: {class} template<typename T> A\t(text1)\n'
+ ' T: {templateParam} typename T\t(text1)\n'
+ )
+
+ app2 = make_app(*args, **kwargs)
+ restructuredtext.parse(app=app2, text=text2, docname='text2')
+ root2 = app2.env.domaindata['cpp']['root_symbol']
+
+ assert root2.dump(1) == (
+ ' ::\n'
+ ' template<typename T> \n'
+ ' A\n'
+ ' T\n'
+ ' template<typename U> \n'
+ ' B: {class} template<typename T> template<typename U> A<T>::B\t(text2)\n'
+ ' U: {templateParam} typename U\t(text2)\n'
+ )
+
+ root2.merge_with(root1, ['text1'], app2.env)
+
+ assert root2.dump(1) == (
+ ' ::\n'
+ ' template<typename T> \n'
+ ' A: {class} template<typename T> A\t(text1)\n'
+ ' T: {templateParam} typename T\t(text1)\n'
+ ' template<typename U> \n'
+ ' B: {class} template<typename T> template<typename U> A<T>::B\t(text2)\n'
+ ' U: {templateParam} typename U\t(text2)\n'
+ )
+ warning = app2._warning.getvalue()
+ assert 'Internal C++ domain error during symbol merging' not in warning
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index a9a729c1f..3c71d8337 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -1201,6 +1201,78 @@ def test_info_field_list_var(app):
refdomain="py", reftype="class", reftarget="int", **{"py:class": "Class"})
+def test_info_field_list_napoleon_deliminator_of(app):
+ text = (".. py:module:: example\n"
+ ".. py:class:: Class\n"
+ "\n"
+ " :param list_str_var: example description.\n"
+ " :type list_str_var: list of str\n"
+ " :param tuple_int_var: example description.\n"
+ " :type tuple_int_var: tuple of tuple of int\n"
+ )
+ doctree = restructuredtext.parse(app, text)
+
+ # :param list of str list_str_var:
+ assert_node(doctree[3][1][0][0][1][0][0][0],
+ ([addnodes.literal_strong, "list_str_var"],
+ " (",
+ [pending_xref, addnodes.literal_emphasis, "list"],
+ [addnodes.literal_emphasis, " of "],
+ [pending_xref, addnodes.literal_emphasis, "str"],
+ ")",
+ " -- ",
+ "example description."))
+
+ # :param tuple of tuple of int tuple_int_var:
+ assert_node(doctree[3][1][0][0][1][0][1][0],
+ ([addnodes.literal_strong, "tuple_int_var"],
+ " (",
+ [pending_xref, addnodes.literal_emphasis, "tuple"],
+ [addnodes.literal_emphasis, " of "],
+ [pending_xref, addnodes.literal_emphasis, "tuple"],
+ [addnodes.literal_emphasis, " of "],
+ [pending_xref, addnodes.literal_emphasis, "int"],
+ ")",
+ " -- ",
+ "example description."))
+
+
+def test_info_field_list_napoleon_deliminator_or(app):
+ text = (".. py:module:: example\n"
+ ".. py:class:: Class\n"
+ "\n"
+ " :param bool_str_var: example description.\n"
+ " :type bool_str_var: bool or str\n"
+ " :param str_float_int_var: example description.\n"
+ " :type str_float_int_var: str or float or int\n"
+ )
+ doctree = restructuredtext.parse(app, text)
+
+ # :param bool or str bool_str_var:
+ assert_node(doctree[3][1][0][0][1][0][0][0],
+ ([addnodes.literal_strong, "bool_str_var"],
+ " (",
+ [pending_xref, addnodes.literal_emphasis, "bool"],
+ [addnodes.literal_emphasis, " or "],
+ [pending_xref, addnodes.literal_emphasis, "str"],
+ ")",
+ " -- ",
+ "example description."))
+
+ # :param str or float or int str_float_int_var:
+ assert_node(doctree[3][1][0][0][1][0][1][0],
+ ([addnodes.literal_strong, "str_float_int_var"],
+ " (",
+ [pending_xref, addnodes.literal_emphasis, "str"],
+ [addnodes.literal_emphasis, " or "],
+ [pending_xref, addnodes.literal_emphasis, "float"],
+ [addnodes.literal_emphasis, " or "],
+ [pending_xref, addnodes.literal_emphasis, "int"],
+ ")",
+ " -- ",
+ "example description."))
+
+
def test_type_field(app):
text = (".. py:data:: var1\n"
" :type: .int\n"
diff --git a/tests/test_ext_napoleon.py b/tests/test_ext_napoleon.py
index f4ad04a00..e73c75ffd 100644
--- a/tests/test_ext_napoleon.py
+++ b/tests/test_ext_napoleon.py
@@ -94,8 +94,10 @@ class SetupTest(TestCase):
for name in Config._config_values:
has_config = False
for method_name, args, _kwargs in app.method_calls:
- if(method_name == 'add_config_value' and
- args[0] == name):
+ if (
+ method_name == 'add_config_value' and
+ args[0] == name
+ ):
has_config = True
if not has_config:
self.fail('Config value was not added to app %s' % name)
@@ -104,11 +106,15 @@ class SetupTest(TestCase):
has_skip_member = False
for method_name, args, _kwargs in app.method_calls:
if method_name == 'connect':
- if(args[0] == 'autodoc-process-docstring' and
- args[1] == _process_docstring):
+ if (
+ args[0] == 'autodoc-process-docstring' and
+ args[1] == _process_docstring
+ ):
has_process_docstring = True
- elif(args[0] == 'autodoc-skip-member' and
- args[1] == _skip_member):
+ elif (
+ args[0] == 'autodoc-skip-member' and
+ args[1] == _skip_member
+ ):
has_skip_member = True
if not has_process_docstring:
self.fail('autodoc-process-docstring never connected')
diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py
index 6db897932..c2704ebce 100644
--- a/tests/test_ext_napoleon_docstring.py
+++ b/tests/test_ext_napoleon_docstring.py
@@ -356,6 +356,41 @@ class GoogleDocstringTest(BaseDocstringTest):
:Yields: Extended
description of yielded value
"""
+ ), (
+ """
+ Single line summary
+
+ Args:
+
+ arg1 (list of str): Extended
+ description of arg1.
+ arg2 (tuple of int): Extended
+ description of arg2.
+ arg3 (tuple of list of float): Extended
+ description of arg3.
+ arg4 (int, float, or list of bool): Extended
+ description of arg4.
+ arg5 (list of int, float, or bool): Extended
+ description of arg5.
+ arg6 (list of int or float): Extended
+ description of arg6.
+ """,
+ """
+ Single line summary
+
+ :Parameters: * **arg1** (*list of str*) -- Extended
+ description of arg1.
+ * **arg2** (*tuple of int*) -- Extended
+ description of arg2.
+ * **arg3** (*tuple of list of float*) -- Extended
+ description of arg3.
+ * **arg4** (*int, float, or list of bool*) -- Extended
+ description of arg4.
+ * **arg5** (*list of int, float, or bool*) -- Extended
+ description of arg5.
+ * **arg6** (*list of int or float*) -- Extended
+ description of arg6.
+ """
)]
def test_sphinx_admonitions(self):
@@ -2367,6 +2402,8 @@ definition_after_normal_text : int
"defaultdict",
"int, float, or complex",
"int or float or None, optional",
+ "list of list of int or float, optional",
+ "tuple of list of str, float, or int",
'{"F", "C", "N"}',
"{'F', 'C', 'N'}, default: 'F'",
"{'F', 'C', 'N or C'}, default 'F'",
@@ -2383,6 +2420,8 @@ definition_after_normal_text : int
["defaultdict"],
["int", ", ", "float", ", or ", "complex"],
["int", " or ", "float", " or ", "None", ", ", "optional"],
+ ["list", " of ", "list", " of ", "int", " or ", "float", ", ", "optional"],
+ ["tuple", " of ", "list", " of ", "str", ", ", "float", ", or ", "int"],
["{", '"F"', ", ", '"C"', ", ", '"N"', "}"],
["{", "'F'", ", ", "'C'", ", ", "'N'", "}", ", ", "default", ": ", "'F'"],
["{", "'F'", ", ", "'C'", ", ", "'N or C'", "}", ", ", "default", " ", "'F'"],
@@ -2443,6 +2482,7 @@ definition_after_normal_text : int
"optional",
"str, optional",
"int or float or None, default: None",
+ "list of tuple of str, optional",
"int, default None",
'{"F", "C", "N"}',
"{'F', 'C', 'N'}, default: 'N'",
@@ -2455,6 +2495,7 @@ definition_after_normal_text : int
"*optional*",
":class:`str`, *optional*",
":class:`int` or :class:`float` or :obj:`None`, *default*: :obj:`None`",
+ ":class:`list` of :class:`tuple` of :class:`str`, *optional*",
":class:`int`, *default* :obj:`None`",
'``{"F", "C", "N"}``',
"``{'F', 'C', 'N'}``, *default*: ``'N'``",
@@ -2486,6 +2527,8 @@ definition_after_normal_text : int
a optional mapping
param8 : ... or Ellipsis
ellipsis
+ param9 : tuple of list of int
+ a parameter with tuple of list of int
""")
expected = dedent("""\
:param param1: the data to work on
@@ -2504,6 +2547,8 @@ definition_after_normal_text : int
:type param7: :term:`mapping` of :term:`hashable` to :class:`str`, *optional*
:param param8: ellipsis
:type param8: :obj:`... <Ellipsis>` or :obj:`Ellipsis`
+ :param param9: a parameter with tuple of list of int
+ :type param9: :class:`tuple` of :class:`list` of :class:`int`
""")
translations = {
"dict-like": ":term:`dict-like <mapping>`",