From: Vladimír Vondruš Date: Sat, 10 Feb 2018 16:11:46 +0000 (+0100) Subject: doxygen: support rendering namespace members in file scope. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=5ab694a34d653a0cd4b89782f73ac17ce502568c;p=blog.git doxygen: support rendering namespace members in file scope. --- diff --git a/doc/doxygen.rst b/doc/doxygen.rst index 105a7502..4cd7a096 100644 --- a/doc/doxygen.rst +++ b/doc/doxygen.rst @@ -69,6 +69,10 @@ with the stock output to avoid broken links once you switch. It may work reasonably well with older versions, but I can't guarantee that. Upgrade to the latest version to have the best experience. + Some features depend on patches that are not yet integrated in Doxygen, in + that case the documentation mentions which revision to use or which patch + you need to apply. + Everything you need apart from Doxygen itself is a Python script and a bunch of template files. You can get that by cloning :gh:`the m.css GitHub repository ` and looking into the ``doxygen/`` directory: @@ -504,6 +508,23 @@ the ``@section`` command instead. Table of contents for pages is generated only if they specify ``@tableofcontents`` in their documentation block. +`Namespace members in file scope`_ +---------------------------------- + +Doxygen by default doesn't render namespace members for file documentation in +its XML output. To match the behavior of stock HTML output, enable the +:ini:`XML_NAMESPACE_MEMBERS_IN_FILE_SCOPE` option: + +.. code:: ini + + # Requires a patch to Doxygen 1.8.14, see below + XML_NAMESPACE_MEMBERS_IN_FILE_SCOPE = YES + +.. note-warning:: Doxygen patches + + In order to use the :ini:`XML_NAMESPACE_MEMBERS_IN_FILE_SCOPE` option, you + need Doxygen with :gh:`doxygen/doxygen#653` applied. + `Code highlighting`_ -------------------- @@ -1190,6 +1211,8 @@ has the following properties: =============================== =============================================== Property Description =============================== =============================================== +:py:`enum.base_url` Base URL of file containing detailed + description [3]_ :py:`enum.id` Identifier hash [3]_ :py:`enum.type` Enum type or empty if implicitly typed [6]_ :py:`enum.is_strong` If the enum is strong @@ -1212,6 +1235,8 @@ Every item of :py:`enum.values` has the following properties: =========================== =================================================== Property Description =========================== =================================================== +:py:`value.base_url` Base URL of file containing detailed description + [3]_ :py:`value.id` Identifier hash [3]_ :py:`value.name` Value name [4]_ :py:`value.initializer` Value initializer. Can be empty. [1]_ @@ -1230,6 +1255,8 @@ item has the following properties: =========================== =================================================== Property Description =========================== =================================================== +:py:`typedef.base_url` Base URL of file containing detailed description + [3]_ :py:`typedef.id` Identifier hash [3]_ :py:`typedef.is_using` Whether it is a :cpp:`typedef` or an :cpp:`using` :py:`typedef.type` Typedef type, or what all goes before the name for @@ -1263,6 +1290,8 @@ every item has the following properties: =============================== =============================================== Property Description =============================== =============================================== +:py:`func.base_url` Base URL of file containing detailed + description [3]_ :py:`func.id` Identifier hash [3]_ :py:`func.type` Function return type [6]_ :py:`func.name` Function name [4]_ @@ -1345,6 +1374,8 @@ every item has the following properties: =========================== =================================================== Property Description =========================== =================================================== +:py:`var.base_url` Base URL of file containing detailed description + [3]_ :py:`var.id` Identifier hash [3]_ :py:`var.type` Variable type [6]_ :py:`var.name` Variable name [4]_ @@ -1521,15 +1552,19 @@ all directories are before all files. .. [2] :py:`i.description` is HTML code with the full description, containing paragraphs, notes, code blocks, images etc. Can be empty in case just the brief description is present. -.. [3] :py:`i.id` is a hash used to link to the member on the page, usually - appearing after ``#`` in page URL +.. [3] :py:`i.base_url`, joined using ``#`` with :py:`i.id` form a unique URL + for given symbol. If the :py:`i.base_url` is not the same as + :py:`compound.url`, it means given symbol is just referenced from given + compound and its detailed documentation resides elsewhere. .. [4] :py:`i.name` is just the member name, not qualified. Prepend :py:`compound.prefix_wbr` to it to get the fully qualified name. .. [5] :py:`compound.has_*_details` and :py:`i.has_details` are :py:`True` if there is detailed description, function/template/macro parameter documentation or enum value listing that makes it worth to render the full description block. If :py:`False`, the member should be included only in - the brief listing on top of the page to avoid unnecessary repetition. + the brief listing on top of the page to avoid unnecessary repetition. If + :py:`i.base_url` is not the same as :py:`compound.url`, its + :py:`i.has_details` is always set to :py:`False`. .. [6] :py:`i.type` and :py:`param.default` is rendered as HTML and usually contains links to related documentation .. [7] :py:`i.is_deprecated` is set to :py:`True` if detailed docs of given diff --git a/doxygen/dox2html5.py b/doxygen/dox2html5.py index 5096a7a5..e2f58b77 100755 --- a/doxygen/dox2html5.py +++ b/doxygen/dox2html5.py @@ -1314,7 +1314,7 @@ def parse_enum(state: State, element: ET.Element): assert element.tag == 'memberdef' and element.attrib['kind'] == 'enum' enum = Empty() - enum.id = extract_id_hash(state, element) + enum.base_url, enum.id = parse_id(state, element) enum.type = parse_type(state, element.find('type')) enum.name = element.find('name').text if enum.name.startswith('@'): enum.name = '(anonymous)' @@ -1330,7 +1330,9 @@ def parse_enum(state: State, element: ET.Element): enumvalue: ET.Element for enumvalue in element.findall('enumvalue'): value = Empty() - value.id = extract_id_hash(state, enumvalue) + # The base_url might be different, but should be the same as enum.base_url + value_base_url, value.id = parse_id(state, enumvalue) + assert value_base_url == enum.base_url value.name = enumvalue.find('name').text # There can be an implicit initializer for enum value value.initializer = html.escape(enumvalue.findtext('initializer', '')) @@ -1339,10 +1341,10 @@ def parse_enum(state: State, element: ET.Element): value.description, value_search_keywords, value.is_deprecated = parse_enum_value_desc(state, enumvalue) if value.description: enum.has_value_details = True - if not state.doxyfile['M_SEARCH_DISABLED']: + if enum.base_url == state.current_url and not state.doxyfile['M_SEARCH_DISABLED']: result = Empty() result.flags = ResultFlag.ENUM_VALUE|(ResultFlag.DEPRECATED if value.is_deprecated else ResultFlag(0)) - result.url = state.current_url + '#' + value.id + result.url = enum.base_url + '#' + value.id result.prefix = state.current_prefix + [enum.name] result.name = value.name result.keywords = value_search_keywords @@ -1351,12 +1353,12 @@ def parse_enum(state: State, element: ET.Element): state.search += [result] enum.values += [value] - enum.has_details = enum.description or enum.has_value_details + enum.has_details = enum.base_url == state.current_url and (enum.description or enum.has_value_details) if enum.brief or enum.has_details or enum.has_value_details: - if not state.doxyfile['M_SEARCH_DISABLED']: + if enum.base_url == state.current_url and not state.doxyfile['M_SEARCH_DISABLED']: result = Empty() result.flags = ResultFlag.ENUM|(ResultFlag.DEPRECATED if enum.is_deprecated else ResultFlag(0)) - result.url = state.current_url + '#' + enum.id + result.url = enum.base_url + '#' + enum.id result.prefix = state.current_prefix result.name = enum.name result.keywords = search_keywords @@ -1405,7 +1407,7 @@ def parse_typedef(state: State, element: ET.Element): assert element.tag == 'memberdef' and element.attrib['kind'] == 'typedef' typedef = Empty() - typedef.id = extract_id_hash(state, element) + typedef.base_url, typedef.id = parse_id(state, element) typedef.is_using = element.findtext('definition', '').startswith('using') typedef.type = parse_type(state, element.find('type')) typedef.args = parse_type(state, element.find('argsstring')) @@ -1415,12 +1417,13 @@ def parse_typedef(state: State, element: ET.Element): typedef.is_protected = element.attrib['prot'] == 'protected' typedef.has_template_details, typedef.templates = parse_template_params(state, element.find('templateparamlist'), templates) - typedef.has_details = typedef.description or typedef.has_template_details + typedef.has_details = typedef.base_url == state.current_url and (typedef.description or typedef.has_template_details) if typedef.brief or typedef.has_details: - if not state.doxyfile['M_SEARCH_DISABLED']: + # Avoid duplicates in search + if typedef.base_url == state.current_url and not state.doxyfile['M_SEARCH_DISABLED']: result = Empty() result.flags = ResultFlag.TYPEDEF|(ResultFlag.DEPRECATED if typedef.is_deprecated else ResultFlag(0)) - result.url = state.current_url + '#' + typedef.id + result.url = typedef.base_url + '#' + typedef.id result.prefix = state.current_prefix result.name = typedef.name result.keywords = search_keywords @@ -1432,7 +1435,7 @@ def parse_func(state: State, element: ET.Element): assert element.tag == 'memberdef' and element.attrib['kind'] == 'function' func = Empty() - func.id = extract_id_hash(state, element) + func.base_url, func.id = parse_id(state, element) func.type = parse_type(state, element.find('type')) func.name = fix_type_spacing(html.escape(element.find('name').text)) func.brief = parse_desc(state, element.find('briefdescription')) @@ -1519,12 +1522,13 @@ def parse_func(state: State, element: ET.Element): # Some param description got unused if params: logging.warning("{}: function parameter description doesn't match parameter names: {}".format(state.current, repr(params))) - func.has_details = func.description or func.has_template_details or func.has_param_details or func.return_value or func.return_values + func.has_details = func.base_url == state.current_url and (func.description or func.has_template_details or func.has_param_details or func.return_value or func.return_values) if func.brief or func.has_details: - if not state.doxyfile['M_SEARCH_DISABLED']: + # Avoid duplicates in search + if func.base_url == state.current_url and not state.doxyfile['M_SEARCH_DISABLED']: result = Empty() result.flags = ResultFlag.FUNC|(ResultFlag.DEPRECATED if func.is_deprecated else ResultFlag(0))|(ResultFlag.DELETED if func.is_deleted else ResultFlag(0)) - result.url = state.current_url + '#' + func.id + result.url = func.base_url + '#' + func.id result.prefix = state.current_prefix result.name = func.name result.keywords = search_keywords @@ -1538,7 +1542,7 @@ def parse_var(state: State, element: ET.Element): assert element.tag == 'memberdef' and element.attrib['kind'] == 'variable' var = Empty() - var.id = extract_id_hash(state, element) + var.base_url, var.id = parse_id(state, element) var.type = parse_type(state, element.find('type')) if var.type.startswith('constexpr'): var.type = var.type[10:] @@ -1552,12 +1556,13 @@ def parse_var(state: State, element: ET.Element): var.brief = parse_desc(state, element.find('briefdescription')) var.description, search_keywords, var.is_deprecated = parse_var_desc(state, element) - var.has_details = not not var.description + var.has_details = var.base_url == state.current_url and var.description if var.brief or var.has_details: - if not state.doxyfile['M_SEARCH_DISABLED']: + # Avoid duplicates in search + if var.base_url == state.current_url and not state.doxyfile['M_SEARCH_DISABLED']: result = Empty() result.flags = ResultFlag.VAR|(ResultFlag.DEPRECATED if var.is_deprecated else ResultFlag(0)) - result.url = state.current_url + '#' + var.id + result.url = var.base_url + '#' + var.id result.prefix = state.current_prefix result.name = var.name result.keywords = search_keywords diff --git a/doxygen/templates/entry-enum.html b/doxygen/templates/entry-enum.html index 61a078eb..60ee6a46 100644 --- a/doxygen/templates/entry-enum.html +++ b/doxygen/templates/entry-enum.html @@ -1,5 +1,5 @@
{% set j = joiner(',\n ') %} - enum {% if enum.is_strong %}class {% endif %}{{ enum.name }}{% if enum.type %}: {{ enum.type }}{% endif %} { {% for value in enum.values %}{{ j() }}{{ value.name }}{% if value.initializer %} {{ value.initializer }}{% endif %}{% if value.is_deprecated %} deprecated{% endif %}{% endfor %} }{% if enum.is_deprecated %} deprecated{% endif %}{% if mark_nonpublic and enum.is_protected %} protected{% endif %} + enum {% if enum.is_strong %}class {% endif %}{{ enum.name }}{% if enum.type %}: {{ enum.type }}{% endif %} { {% for value in enum.values %}{{ j() }}{{ value.name }}{% if value.initializer %} {{ value.initializer }}{% endif %}{% if value.is_deprecated %} deprecated{% endif %}{% endfor %} }{% if enum.is_deprecated %} deprecated{% endif %}{% if mark_nonpublic and enum.is_protected %} protected{% endif %}
{{ enum.brief }}
diff --git a/doxygen/templates/entry-func.html b/doxygen/templates/entry-func.html index a5fc72d9..bb9b6805 100644 --- a/doxygen/templates/entry-func.html +++ b/doxygen/templates/entry-func.html @@ -4,6 +4,6 @@
template<{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}>
{% endif %} {% set j = joiner(',\n ') %} - {{ func.prefix }}{% if func.type == 'void' %}void {% elif func.type %}auto {% endif %}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.type and func.type != 'void' %} -> {{ func.type }}{% endif %}{% if func.is_deprecated %} deprecated{% endif %}{% if not func.type or mark_nonpublic %}{% if func.is_protected %} protected{% elif func.is_private %} private{% endif %}{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_explicit %} explicit {% endif %}{% if func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_constexpr %} constexpr{% endif %}{% if func.is_noexcept %} noexcept{% endif %} + {{ func.prefix }}{% if func.type == 'void' %}void {% elif func.type %}auto {% endif %}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.type and func.type != 'void' %} -> {{ func.type }}{% endif %}{% if func.is_deprecated %} deprecated{% endif %}{% if not func.type or mark_nonpublic %}{% if func.is_protected %} protected{% elif func.is_private %} private{% endif %}{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_explicit %} explicit {% endif %}{% if func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_constexpr %} constexpr{% endif %}{% if func.is_noexcept %} noexcept{% endif %}
{{ func.brief }}
diff --git a/doxygen/templates/entry-typedef.html b/doxygen/templates/entry-typedef.html index f370c1ab..cbcb4b57 100644 --- a/doxygen/templates/entry-typedef.html +++ b/doxygen/templates/entry-typedef.html @@ -3,7 +3,7 @@ {% set j = joiner(', ') %}
template<{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}>
{% endif %} - using {{ typedef.name }} = {{ typedef.type }}{{ typedef.args }}{% if typedef.is_deprecated %} deprecated{% endif %}{% if mark_nonpublic and typedef.is_protected %} protected{% endif %} + using {{ typedef.name }} = {{ typedef.type }}{{ typedef.args }}{% if typedef.is_deprecated %} deprecated{% endif %}{% if mark_nonpublic and typedef.is_protected %} protected{% endif %} {# This empty line needs to be there otherwise it's eaten #} diff --git a/doxygen/templates/entry-var.html b/doxygen/templates/entry-var.html index 50fb95ef..820cab85 100644 --- a/doxygen/templates/entry-var.html +++ b/doxygen/templates/entry-var.html @@ -1,2 +1,2 @@ -
{% if var.is_static %}static {% endif %}{{ var.type }} {{ var.name }}{% if var.is_deprecated %} deprecated{% endif %}{% if mark_nonpublic and var.is_protected %} protected{% endif %}{% if var.is_constexpr %} constexpr{% endif %}
+
{% if var.is_static %}static {% endif %}{{ var.type }} {{ var.name }}{% if var.is_deprecated %} deprecated{% endif %}{% if mark_nonpublic and var.is_protected %} protected{% endif %}{% if var.is_constexpr %} constexpr{% endif %}
{{ var.brief }}
diff --git a/doxygen/test/compound_namespace_members_in_file_scope/Doxyfile b/doxygen/test/compound_namespace_members_in_file_scope/Doxyfile new file mode 100644 index 00000000..8663c0b3 --- /dev/null +++ b/doxygen/test/compound_namespace_members_in_file_scope/Doxyfile @@ -0,0 +1,13 @@ +INPUT = File.h +QUIET = YES +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +XML_NAMESPACE_MEMBERS_IN_FILE_SCOPE = YES + +M_PAGE_FINE_PRINT = +M_THEME_COLOR = +M_LINKS_NAVBAR1 = +M_LINKS_NAVBAR2 = +M_SEARCH_DISABLED = YES diff --git a/doxygen/test/compound_namespace_members_in_file_scope/File.h b/doxygen/test/compound_namespace_members_in_file_scope/File.h new file mode 100644 index 00000000..94fcbec8 --- /dev/null +++ b/doxygen/test/compound_namespace_members_in_file_scope/File.h @@ -0,0 +1,104 @@ +/** @file + * @brief A file + */ + +/** @brief A namespace */ +namespace Namespace { + +/** +@brief A function + +Detailed function docs. +*/ +void foo(); + +/** @brief Function with just a brief */ +void fooBrief(); + +/** +@brief An enum + +Detailed enum docs. +*/ +enum Enum { + Value /**< A value */ +}; + +/** @brief Enum with just a brief */ +enum EnumBrief {}; + +/** +@brief A typedef + +Detailed typedef docs. +*/ +typedef int Typedef; + +/** @brief Typedef with just a brief */ +typedef int TypedefBrief; + +/** +@brief A variable + +Detailed variable docs. +*/ +constexpr int Variable = 5; + +/** @brief Variable with just a brief */ +constexpr int VariableBrief = 5; + +/** +@brief A macro + +This appears only in the file docs and fully expanded. +*/ +#define A_MACRO + +} + +/* For the undocumented namespace, everything should appear in file docs */ +namespace UndocumentedNamespace { + +/** +@brief A function + +Detailed function docs. +*/ +void foo(); + +/** @brief Function with just a brief */ +void fooBrief(); + +/** +@brief An enum + +Detailed enum docs. +*/ +enum Enum { + Value /**< A value */ +}; + +/** @brief Enum with just a brief */ +enum EnumBrief {}; + +/** +@brief A typedef + +Detailed typedef docs. +*/ +typedef int Typedef; + +/** @brief Typedef with just a brief */ +typedef int TypedefBrief; + +/** +@brief A variable + +Detailed variable docs. +*/ +constexpr int Variable = 5; + +/** @brief Variable with just a brief */ +constexpr int VariableBrief = 5; + +} diff --git a/doxygen/test/compound_namespace_members_in_file_scope/File_8h.html b/doxygen/test/compound_namespace_members_in_file_scope/File_8h.html new file mode 100644 index 00000000..ca9663cb --- /dev/null +++ b/doxygen/test/compound_namespace_members_in_file_scope/File_8h.html @@ -0,0 +1,200 @@ + + + + + File.h file | My Project + + + + + +
+
+
+
+
+

+ File.h file +

+

A file.

+
+

Contents

+ +
+
+

Namespaces

+
+
namespace Namespace
+
A namespace.
+
+
+
+

Enums

+
+
+ enum Enum { Value } +
+
An enum.
+
+ enum EnumBrief { } +
+
Enum with just a brief.
+
+ enum Enum { Value } +
+
An enum.
+
+ enum EnumBrief { } +
+
Enum with just a brief.
+
+
+
+

Typedefs

+
+
+ using Typedef = int +
+
A typedef.
+
+ using TypedefBrief = int +
+
Typedef with just a brief.
+
+ using Typedef = int +
+
A typedef.
+
+ using TypedefBrief = int +
+
Typedef with just a brief.
+
+
+
+

Functions

+
+
+ void foo() +
+
A function.
+
+ void fooBrief() +
+
Function with just a brief.
+
+ void foo() +
+
A function.
+
+ void fooBrief() +
+
Function with just a brief.
+
+
+
+

Variables

+
+
int Variable constexpr
+
A variable.
+
int VariableBrief constexpr
+
Variable with just a brief.
+
int Variable constexpr
+
A variable.
+
int VariableBrief constexpr
+
Variable with just a brief.
+
+
+
+

Defines

+
+
+ #define A_MACRO +
+
A macro.
+
+
+
+

Enum documentation

+
+

+ enum Enum +

+

An enum.

+

Detailed enum docs.

+ + + + + + + + +
Enumerators
Value +

A value

+
+
+
+
+

Typedef documentation

+
+

+ typedef int Typedef +

+

A typedef.

+

Detailed typedef docs.

+
+
+
+

Function documentation

+
+

+ void foo() +

+

A function.

+

Detailed function docs.

+
+
+
+

Variable documentation

+
+

+ int Variable constexpr +

+

A variable.

+

Detailed variable docs.

+
+
+
+

Define documentation

+
+

+ #define A_MACRO +

+

A macro.

+

This appears only in the file docs and fully expanded.

+
+
+
+
+
+
+ + diff --git a/doxygen/test/compound_namespace_members_in_file_scope/namespaceNamespace.html b/doxygen/test/compound_namespace_members_in_file_scope/namespaceNamespace.html new file mode 100644 index 00000000..c471ffe1 --- /dev/null +++ b/doxygen/test/compound_namespace_members_in_file_scope/namespaceNamespace.html @@ -0,0 +1,142 @@ + + + + + Namespace namespace | My Project + + + + + +
+
+
+
+
+

Namespace namespace

+

A namespace.

+
+

Contents

+ +
+
+

Enums

+
+
+ enum Enum { Value } +
+
An enum.
+
+ enum EnumBrief { } +
+
Enum with just a brief.
+
+
+
+

Typedefs

+
+
+ using Typedef = int +
+
A typedef.
+
+ using TypedefBrief = int +
+
Typedef with just a brief.
+
+
+
+

Functions

+
+
+ void foo() +
+
A function.
+
+ void fooBrief() +
+
Function with just a brief.
+
+
+
+

Variables

+
+
int Variable constexpr
+
A variable.
+
int VariableBrief constexpr
+
Variable with just a brief.
+
+
+
+

Enum documentation

+
+

+ enum Namespace::Enum +

+

An enum.

+

Detailed enum docs.

+ + + + + + + + +
Enumerators
Value +

A value

+
+
+
+
+

Typedef documentation

+
+

+ typedef int Namespace::Typedef +

+

A typedef.

+

Detailed typedef docs.

+
+
+
+

Function documentation

+
+

+ void Namespace::foo() +

+

A function.

+

Detailed function docs.

+
+
+
+

Variable documentation

+
+

+ int Namespace::Variable constexpr +

+

A variable.

+

Detailed variable docs.

+
+
+
+
+
+
+ + diff --git a/doxygen/test/search/Doxyfile b/doxygen/test/search/Doxyfile index 0e77c509..afec645f 100644 --- a/doxygen/test/search/Doxyfile +++ b/doxygen/test/search/Doxyfile @@ -4,6 +4,10 @@ GENERATE_HTML = NO GENERATE_LATEX = NO GENERATE_XML = YES XML_PROGRAMLISTING = NO + +# Verify that this doesn't cause duplicated items +XML_NAMESPACE_MEMBERS_IN_FILE_SCOPE = YES + EXAMPLE_PATH = . ALIASES = \ "m_keywords{1}=@xmlonly@endxmlonly" \ diff --git a/doxygen/test/test_compound.py b/doxygen/test/test_compound.py index b390cf91..83c91263 100644 --- a/doxygen/test/test_compound.py +++ b/doxygen/test/test_compound.py @@ -25,7 +25,9 @@ import os import unittest -from test import IntegrationTestCase +from distutils.version import LooseVersion + +from test import IntegrationTestCase, doxygen_version class Listing(IntegrationTestCase): def __init__(self, *args, **kwargs): @@ -185,3 +187,21 @@ class Deprecated(IntegrationTestCase): # Base and derived class listing self.assertEqual(*self.actual_expected_contents('structDeprecatedNamespace_1_1BaseDeprecatedClass.html')) self.assertEqual(*self.actual_expected_contents('structDeprecatedNamespace_1_1DeprecatedClass.html')) + +class NamespaceMembersInFileScope(IntegrationTestCase): + def __init__(self, *args, **kwargs): + super().__init__(__file__, 'namespace_members_in_file_scope', *args, **kwargs) + + def test(self): + self.run_dox2html5(wildcard='namespaceNamespace.xml') + + # The namespace should have the detailed docs + self.assertEqual(*self.actual_expected_contents('namespaceNamespace.html')) + + @unittest.skipUnless(LooseVersion(doxygen_version()) > LooseVersion("1.8.14"), + "https://github.com/doxygen/doxygen/pull/653") + def test_file(self): + self.run_dox2html5(wildcard='File_8h.xml') + + # The file should have just links to detailed docs + self.assertEqual(*self.actual_expected_contents('File_8h.html'))