Source code for testing.tests.cpp_pimpl

# -*- coding: utf8 -*-
########################################################################################
# This file is part of exhale.  Copyright (c) 2017-2024, Stephen McDowell.             #
# Full BSD 3-Clause license available here:                                            #
#                                                                                      #
#                https://github.com/svenevs/exhale/blob/master/LICENSE                 #
########################################################################################
"""
Tests for the ``cpp_nesting`` project.
"""

from __future__ import unicode_literals

import os
import re
import textwrap

from testing import get_exhale_root
from testing.base import ExhaleTestCase
from testing.decorators import confoverrides
from testing.hierarchies import                                                        \
    class_hierarchy, compare_class_hierarchy, compare_file_hierarchy, file_hierarchy


[docs] class TestedExclusionTypes(object): """ An "enum" for listing exclusions we care about. Yes, I still need to support python 2. """ NoExclusions = 0 """No exclusions were requested.""" AllImpl = 1 """All ``r".*Impl$"`` should be excluded.""" DetailImpl = 2 """Only ``r".*detail::.*Impl$"`` should be excluded."""
[docs] class CPPPimpl(ExhaleTestCase): """ Primary test class for project ``cpp_pimpl``. """ test_project = "cpp_pimpl" """.. testproject:: cpp_pimpl"""
[docs] def expected_class_hierarchy(self, exclusions): """ Return expected rst class hierarchy listing based on specified ``exclusions``. Helper method for :func:`validate_class_hierarchy`. **Parameters** ``exclusions`` (:class:`TestedExclusionTypes`) The exclusion that is currently being tested. **Return** (:class:`python:str`) The expected rst class listing. If ``exclusions`` is invalid, then the string ``"INTERNAL TESTING ERROR"`` is returned. """ full_class_hierarchy_lines = textwrap.dedent('''\ - :ref:`{pimpl}` - :ref:`{pimpl_detail}` - :ref:`{pimpl_detail_EarthImpl}` - :ref:`{pimpl_detail_JupiterImpl}` - :ref:`{pimpl_Earth}` - :ref:`{pimpl_Earth_v2}` - :ref:`{pimpl_EarthImpl}` - :ref:`{pimpl_Jupiter}` - :ref:`{pimpl_Jupiter_v2}` - :ref:`{pimpl_JupiterImpl}` - :ref:`{pimpl_Planet}` ''').format(**self.link_name_format_dict()).splitlines() if exclusions == TestedExclusionTypes.NoExclusions: return "\n".join(full_class_hierarchy_lines) elif exclusions == TestedExclusionTypes.AllImpl: return "\n".join([ line for line in full_class_hierarchy_lines if "detail" not in line and "impl" not in line.lower() ]) elif exclusions == TestedExclusionTypes.DetailImpl: return "\n".join([ line for line in full_class_hierarchy_lines if "detail" not in line ]) return "INTERNAL TESTING ERROR"
[docs] def validate_class_hierarchy(self, exclusions): """ Validate generated class hierarchy rst list is correct based on ``exclusions``. **Parameters** ``exclusions`` (:class:`TestedExclusionTypes`) The exclusion that is currently being tested. """ class_hierarchy_path = os.path.join( self.getAbsContainmentFolder(), "class_view_hierarchy.rst.include" ) expected_class_hierarchy = self.expected_class_hierarchy(exclusions) with open(class_hierarchy_path) as class_hierarchy: class_hierarchy_contents = class_hierarchy.read() self.assertTrue( expected_class_hierarchy in class_hierarchy_contents, textwrap.dedent('''\ Expected class hierarchy not in generated class hierarchy. Expected class hierarchy: {vsep} {expected} {vsep} Generated class hierarchy: {vsep} {generated} ''').format( vsep=('-' * 44), expected=expected_class_hierarchy, generated=class_hierarchy_contents ) )
[docs] def validate_namespace_listings(self, exclusions): """ Validate generated namespace rst listings are correct based on ``exclusions``. This project contains two namespaces that are tested: ``namespace pimpl`` and ``namespace pimpl::detail``. **Parameters** ``exclusions`` (:class:`TestedExclusionTypes`) The exclusion that is currently being tested. """ # First gather the two namespace nodes for this project so we can read in the # generated file's contents. exhale_root = get_exhale_root(self) # TODO: this may break if you un-do the reparenting stuff # There's only one top-level namespace pimpl = exhale_root.namespaces[0] pimpl_detail = None for child in pimpl.children: if child.kind == "namespace" and child.name == "pimpl::detail": pimpl_detail = child break # Make sure required / forbidden items check out for namespace pimpl pimpl_contents = self.contents_for_node(pimpl) if exclusions in {TestedExclusionTypes.NoExclusions, TestedExclusionTypes.DetailImpl}: pimpl_required = self.nspace_pimpl_link_names() pimpl_forbidden = None elif exclusions == TestedExclusionTypes.AllImpl: pimpl_required = self.nspace_pimpl_link_names() - self.impl_link_names() pimpl_forbidden = self.impl_link_names() self.cross_validate( pimpl_contents, required=pimpl_required, forbidden=pimpl_forbidden ) # Make sure required / forbidden items check out for namespace pimpl::detail pimpl_detail_contents = self.contents_for_node(pimpl_detail) if exclusions == TestedExclusionTypes.NoExclusions: pimpl_detail_required = self.nspace_pimpl_detail_link_names() pimpl_detail_forbidden = None elif exclusions in {TestedExclusionTypes.AllImpl, TestedExclusionTypes.DetailImpl}: pimpl_detail_required = None pimpl_detail_forbidden = self.nspace_pimpl_detail_link_names() self.cross_validate( pimpl_detail_contents, required=pimpl_detail_required, forbidden=pimpl_detail_forbidden )
[docs] def validate_file_listings(self): """ Validate ``{earth,jupiter}.hpp`` link to all items (regardless of ``"listingExclude"``). """ # Gather the exhale nodes for the two files we care about. exhale_root = get_exhale_root(self) earth_hpp = None jupiter_hpp = None for f in exhale_root.files: if "earth.hpp" in f.name: earth_hpp = f elif "jupiter.hpp" in f.name: jupiter_hpp = f # Make sure the "excluded" items are always included on file pages. self.cross_validate( self.contents_for_node(earth_hpp), required=self.earth_hpp_link_names(), forbidden=None ) self.cross_validate( self.contents_for_node(jupiter_hpp), required=self.jupiter_hpp_link_names(), forbidden=None )
[docs] def test_hierarchies(self): """Verify the class and file hierarchies.""" compare_class_hierarchy(self, class_hierarchy(self.class_hierarchy_dict())) compare_file_hierarchy(self, file_hierarchy(self.file_hierarchy_dict()))
# TODO: once config objects are in this override is not necessary, but currently # the tests in tests/configs.py populate a listingExclude....
[docs] @confoverrides(exhale_args={"listingExclude": []}) def test_no_listing_exclusions(self): """Verify empty listing exclude results in no change in listed API.""" self.validate_class_hierarchy(TestedExclusionTypes.NoExclusions) self.validate_namespace_listings(TestedExclusionTypes.NoExclusions) self.validate_file_listings() self.checkAllFilesIncluded()
[docs] @confoverrides(exhale_args={"listingExclude": [r".*Impl$"]}) def test_impl_exclude(self): """Verify ``r".*Impl$"`` excludes ``*Impl`` class names.""" self.validate_class_hierarchy(TestedExclusionTypes.AllImpl) self.validate_namespace_listings(TestedExclusionTypes.AllImpl) self.validate_file_listings() self.checkAllFilesIncluded()
[docs] @confoverrides(exhale_args={"listingExclude": [(r".*impl$", re.IGNORECASE)]}) def test_impl_exclude_ignorecase(self): """ Verify ``r".*impl$`` with :data:`python:re.IGNORECASE` excludes ``*Impl`` items. """ self.validate_class_hierarchy(TestedExclusionTypes.AllImpl) self.validate_namespace_listings(TestedExclusionTypes.AllImpl) self.validate_file_listings() self.checkAllFilesIncluded()
[docs] @confoverrides(exhale_args={"listingExclude": [r".*detail::.*Impl$"]}) def test_detail_impl_exclude(self): """ Verify ``r".*detail::.*Impl$`` excludes ``*detail::*Impl`` items. """ self.validate_class_hierarchy(TestedExclusionTypes.DetailImpl) self.validate_namespace_listings(TestedExclusionTypes.DetailImpl) self.validate_file_listings() self.checkAllFilesIncluded()
[docs] @confoverrides(exhale_args={"listingExclude": [(r".*detail::.*impl$", re.IGNORECASE)]}) def test_detail_impl_exclude_ignorecase(self): """ Verify ``r".*detail::.*impl$`` with |I| excludes ``*detail::*Impl`` items. .. |I| replace:: :data:`python:re.IGNORECASE` """ self.validate_class_hierarchy(TestedExclusionTypes.DetailImpl) self.validate_namespace_listings(TestedExclusionTypes.DetailImpl) self.validate_file_listings() self.checkAllFilesIncluded()