예제 #1
0
파일: c_maths.py 프로젝트: tower120/exhale
 def test_hierarchies(self):
     """Verify the class and file hierarchies."""
     # verify the file hierarchy and file declaration relationships
     file_hierarchy_dict = {
         directory("include"): {
             file("c_maths.h"): {
                 function("int", "cm_add"): parameters("int", "int"),
                 function("int", "cm_sub"): parameters("int", "int")
             }
         }
     }
     compare_file_hierarchy(self, file_hierarchy(file_hierarchy_dict))
     compare_class_hierarchy(self, class_hierarchy({}))
예제 #2
0
 def test_hierarchies(self):
     """Verify the class and file hierarchies."""
     # verify the file hierarchy and file declaration relationships
     file_hierarchy_dict = {
         directory("include"): {
             file("main.h"): {
                 function("int", "add"): signature("int a", "int b"),
                 function("int", "sub"): signature("int a", "int b")
             }
         }
     }
     compare_file_hierarchy(self, file_hierarchy(file_hierarchy_dict))
     compare_class_hierarchy(self, class_hierarchy({}))
예제 #3
0
class CPPWithSpaces(ExhaleTestCase):
    """
    Primary test class for project ``cpp with spaces``.
    """

    test_project = "cpp with spaces"
    """.. testproject:: cpp with spaces"""

    file_hierarchy_dict = {
        directory("include"): {
            directory("with spaces"): {
                file("with spaces.hpp"): {
                    namespace("with_spaces"): {
                        function("int", "value"): parameters()
                    }
                }
            }
        }
    }
    """The file hierarchy for this project."""
    def test_hierarchies(self):
        """
        Verify the class and file hierarchies.

        .. todo::

            Class hierarchy not tested here, need to revisit that part of the testing
            framework **as well as** stop emitting a "Class Hierarchy" on the root api
            page when it is empty.
        """
        compare_file_hierarchy(self, file_hierarchy(self.file_hierarchy_dict))

    @no_cleanup
    def test_build(self):
        """Verify that the sphinx build (with spaces in filenames) succeeds."""
        self.app.build()
예제 #4
0
class CPPFuncOverloads(ExhaleTestCase):
    """
    Primary test class for project ``cpp_func_overloads``.
    """

    test_project = "cpp_func_overloads"
    """.. testproject:: cpp_func_overloads"""

    file_hierarchy_dict = {
        directory("include"): {
            directory("overload"): {
                file("overload.hpp"): {
                    function("int", "blargh"): parameters("int"),
                    namespace("overload"): {
                        # No args
                        function("void", "blargh"): parameters(),
                        # "pure" int overloads
                        function("int", "blargh"): parameters("int"),
                        function("int", "blargh"): parameters("int", "int"),
                        function("int", "blargh"): parameters("int", "int", "int"),
                        # "pure" float overloads
                        function("float", "blargh"): parameters("float"),
                        function("float", "blargh"): parameters("float", "float"),
                        function("float", "blargh"): parameters("float", "float", "float"),
                        # "pure" std::string overloads
                        function("std::string", "blargh"): parameters("const std::string&"),
                        function("std::string", "blargh"): parameters(
                            "const std::string&", "const std::string&"
                        ),
                        function("std::string", "blargh"): parameters(
                            "const std::string&", "const std::string&", "const std::string&"
                        ),
                        # absurd mixtures
                        function("std::size_t", "blargh"): parameters("std::size_t", "const std::string&"),
                        function("std::size_t", "blargh"): parameters(
                            "std::size_t", "const float&", "double", "const std::string&"
                        ),
                        # vector overloads
                        function("void", "blargh"): parameters("std::vector<std::string>&"),
                        function("void", "blargh"): parameters("std::vector<std::vector<int>>&"),
                        # pointer style (spaces matter...)
                        function("void", "blargh"): parameters(
                            "const float *", "const float *", "float *", "std::size_t"
                        ),
                        # templates
                        function("C::type", "blargh", template=["class C"]): parameters("typename C::type"),
                        # SFINAE is really pretty yeah?
                        function(
                            "std::enable_if<std::is_convertible<typename C::type, T>::value, T>::type",
                            "blargh",
                            template=["class C", "typename T"]
                        ): parameters("typename C::type"),
                        function(
                            "std::enable_if<!std::is_convertible<typename C::type, T>::value, T>::type",
                            "blargh",
                            template=["class C", "typename T"]
                        ): parameters("typename C::type")
                    }
                }
            }
        }
    }

    @no_cleanup
    def test_builds(self):
        """Test deliberately kept to serve as a perpetual reminder this is still broken."""
        self.app.build()

    def test_file_hierarchy(self):
        """Verify the file hierarchy."""
        compare_file_hierarchy(self, file_hierarchy(self.file_hierarchy_dict))
예제 #5
0
class CPPPimpl(ExhaleTestCase):
    """
    Primary test class for project ``cpp_pimpl``.
    """

    test_project = "cpp_pimpl"
    """.. testproject:: cpp_pimpl"""

    file_hierarchy_dict = {
        directory("include"): {
            directory("pimpl"): {
                file("planet.hpp"): {
                    namespace("pimpl"): {
                        clike("class", "Planet"): {}
                    }
                },
                file("earth.hpp"): {
                    namespace("pimpl"): {
                        clike("class", "Earth"): {},
                        clike("class", "EarthImpl"): {},
                        clike("class", "Earth_v2"): {},
                        namespace("detail"): {
                            clike("class", "EarthImpl"): {}
                        }
                    }
                },
                file("jupiter.hpp"): {
                    namespace("pimpl"): {
                        clike("class", "Jupiter"): {},
                        clike("class", "JupiterImpl"): {},
                        clike("class", "Jupiter_v2"): {},
                        namespace("detail"): {
                            clike("class", "JupiterImpl"): {}
                        }
                    }
                }
            }
        }
    }
    """The file hierarchy for this test."""

    class_hierarchy_dict = {
        namespace("pimpl"): {
            clike("class", "Planet"): {},
            clike("class", "Earth"): {},
            clike("class", "EarthImpl"): {},
            clike("class", "Earth_v2"): {},
            clike("class", "Jupiter"): {},
            clike("class", "JupiterImpl"): {},
            clike("class", "Jupiter_v2"): {},
            namespace("detail"): {
                clike("class", "EarthImpl"): {},
                clike("class", "JupiterImpl"): {}
            }
        }
    }
    """The class hierarchy for this test."""
    def impl_link_names(self):
        """Link names for ``pimpl::{Earth,Jupiter}Impl``."""
        links = self.link_name_format_dict()
        return {
            "- :ref:`{pimpl_EarthImpl}`".format(**links),
            "- :ref:`{pimpl_JupiterImpl}`".format(**links)
        }

    def detail_impl_link_names(self):
        """Link names for ``pimpl::detail::{Earth,Jupiter}Impl``."""
        links = self.link_name_format_dict()
        return {
            "- :ref:`{pimpl_detail_EarthImpl}`".format(**links),
            "- :ref:`{pimpl_detail_JupiterImpl}`".format(**links),
        }

    def earth_hpp_link_names(self):
        """Link names for ``earth.hpp``."""
        links = self.link_name_format_dict()
        return {
            "- :ref:`{pimpl_Earth}`".format(**links),
            "- :ref:`{pimpl_Earth_v2}`".format(**links),
            "- :ref:`{pimpl_EarthImpl}`".format(**links),
            "- :ref:`{pimpl_detail_EarthImpl}`".format(**links)
        }

    def jupiter_hpp_link_names(self):
        """Link names for ``jupiter.hpp``."""
        links = self.link_name_format_dict()
        return {
            "- :ref:`{pimpl_Jupiter}`".format(**links),
            "- :ref:`{pimpl_Jupiter_v2}`".format(**links),
            "- :ref:`{pimpl_JupiterImpl}`".format(**links),
            "- :ref:`{pimpl_detail_JupiterImpl}`".format(**links)
        }

    def nspace_pimpl_link_names(self):
        """Link names for ``namespace pimpl``."""
        links = self.link_name_format_dict()
        return {
            "- :ref:`{pimpl_detail}`".format(**links),
            "- :ref:`{pimpl_Earth}`".format(**links),
            "- :ref:`{pimpl_Earth_v2}`".format(**links),
            "- :ref:`{pimpl_EarthImpl}`".format(**links),
            "- :ref:`{pimpl_Jupiter}`".format(**links),
            "- :ref:`{pimpl_Jupiter_v2}`".format(**links),
            "- :ref:`{pimpl_JupiterImpl}`".format(**links),
            "- :ref:`{pimpl_Planet}`".format(**links)
        }

    def nspace_pimpl_detail_link_names(self):
        """Link names for ``namespace pimpl::detail``."""
        links = self.link_name_format_dict()
        return {
            "- :ref:`{pimpl_detail_EarthImpl}`".format(**links),
            "- :ref:`{pimpl_detail_JupiterImpl}`".format(**links)
        }

    def link_name_format_dict(self):
        """
        Return a dictionary suitable for using with :any:`python:str.format`.

        Since Doxygen will behave differently on different platforms with respect to
        what the specific ``refid`` of a given node is, which affect the generated
        ``link_name`` for a given :class:`~exhale.graph.ExhaleNode`, they must be
        searched for after exhale runs for the given test function.

        **Return** (:class:`python:dict`)
            A dictionary with string keys and string values is returned, the key-value
            pairs are ``{name}``: ``{compound.link_name}``:

            +--------------------------------+--------------------------------------+
            | Key                            | C++ Compound ``link_name``           |
            +================================+======================================+
            | ``"pimpl"``                    | ``namespace pimpl``                  |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_detail"``             | ``namespace detail::pimpl``          |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_detail_EarthImpl"``   | ``class pimpl::detail::EarthImpl``   |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_detail_JupiterImpl"`` | ``class pimpl::detail::JupiterImpl`` |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_Earth"``              | ``class pimpl::Earth``               |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_Earth_v2"``           | ``class pimpl::Earth_v2``            |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_EarthImpl"``          | ``class pimpl::EarthImpl``           |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_Jupiter"``            | ``class pimpl::Jupiter``             |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_Jupiter_v2"``         | ``class pimpl::Jupiter_v2``          |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_JupiterImpl"``        | ``class pimpl::JupiterImpl``         |
            +--------------------------------+--------------------------------------+
            | ``"pimpl_Planet"``             | ``class pimpl::Planet``              |
            +--------------------------------+--------------------------------------+
        """
        pimpl = None
        pimpl_detail = None
        pimpl_detail_EarthImpl = None
        pimpl_detail_JupiterImpl = None
        pimpl_Earth = None
        pimpl_Earth_v2 = None
        pimpl_EarthImpl = None
        pimpl_Jupiter = None
        pimpl_Jupiter_v2 = None
        pimpl_JupiterImpl = None
        pimpl_Planet = None

        exhale_root = get_exhale_root(self)
        for node in exhale_root.all_nodes:
            if node.kind == "namespace":
                if node.name == "pimpl":
                    pimpl = node
                elif node.name == "pimpl::detail":
                    pimpl_detail = node
            elif node.kind == "class":
                if node.name == "pimpl::Earth":
                    pimpl_Earth = node
                elif node.name == "pimpl::Earth_v2":
                    pimpl_Earth_v2 = node
                elif node.name == "pimpl::EarthImpl":
                    pimpl_EarthImpl = node
                elif node.name == "pimpl::detail::EarthImpl":
                    pimpl_detail_EarthImpl = node
                elif node.name == "pimpl::Jupiter":
                    pimpl_Jupiter = node
                elif node.name == "pimpl::Jupiter_v2":
                    pimpl_Jupiter_v2 = node
                elif node.name == "pimpl::JupiterImpl":
                    pimpl_JupiterImpl = node
                elif node.name == "pimpl::detail::JupiterImpl":
                    pimpl_detail_JupiterImpl = node
                elif node.name == "pimpl::Planet":
                    pimpl_Planet = node

        # NOTE: test will crash if one is not found, which we want.
        return {
            "pimpl": pimpl.link_name,
            "pimpl_detail": pimpl_detail.link_name,
            "pimpl_detail_EarthImpl": pimpl_detail_EarthImpl.link_name,
            "pimpl_detail_JupiterImpl": pimpl_detail_JupiterImpl.link_name,
            "pimpl_Earth": pimpl_Earth.link_name,
            "pimpl_Earth_v2": pimpl_Earth_v2.link_name,
            "pimpl_EarthImpl": pimpl_EarthImpl.link_name,
            "pimpl_Jupiter": pimpl_Jupiter.link_name,
            "pimpl_Jupiter_v2": pimpl_Jupiter_v2.link_name,
            "pimpl_JupiterImpl": pimpl_JupiterImpl.link_name,
            "pimpl_Planet": pimpl_Planet.link_name
        }

    def expected_class_hierarchy(self, exclusions):
        """
        Return expected rst class hiearchy 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"

    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")
        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))

    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)

    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)

    def test_hierarchies(self):
        """Verify the class and file hierarchies."""
        compare_file_hierarchy(self, file_hierarchy(self.file_hierarchy_dict))
        compare_class_hierarchy(self,
                                class_hierarchy(self.class_hierarchy_dict))

    # TODO: once config objects are in this override is not necessary, but currently
    #       the tests in tests/configs.py populate a listingExclude....
    @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()

    @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()

    @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()

    @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()

    @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()
예제 #6
0
def make_file_hierarchy_dict():
    """
    Return the :class:`python:dict` representing the file hierarchy.

    If :data:`RUN_ABSURD_TEST` is ``True``, :data:`ABSURD_DIRECTORY_PATH` will be
    incorporated in the returned dictionary.
    """
    absurdly_long_names_hpp_contents = {
        define("MAKE_IT_BIG"): {},
        clike("class", make_it_big("class")): {},
        clike("struct", make_it_big("struct")): {},
        function("std::string", make_it_big("function")): parameters(),
        enum(make_it_big("enum")): {},  # TODO: values("first", "second", "third"),
        namespace(make_it_big("namespace")): {
            variable("int", "value"): {}
        },
        define(make_it_big("define").upper()): {},
        variable("int", make_it_big("variable")): {},
        typedef(make_it_big("typedef"), "float"): {},
        union(make_it_big("union")): {}
    }

    if RUN_ABSURD_TEST:
        absurd_directory_structure = {
            directory("structure"): {
                directory("that"): {
                    directory("is"): {
                        directory("longer"): {
                            directory("than"): {
                                directory("two"): {
                                    directory("hundred"): {
                                        directory("and"): {
                                            directory("fifty"): {
                                                directory("five"): {
                                                    directory("characters"): {
                                                        directory("long"): {
                                                            directory("which"): {
                                                                directory("is"): {
                                                                    directory("an"): {
                                                                        directory("absolutely"): {
                                                                            directory("and"): {
                                                                                directory("completely"): {
                                                                                    directory("ridiculous"): {
                                                                                        directory("thing"): {
                                                                                            directory("to"): {
                                                                                                directory("do"): {                                                                                                                                                                     # noqa: E501
                                                                                                    directory("and"): {                                                                                                                                                                # noqa: E501
                                                                                                        directory("if"): {                                                                                                                                                             # noqa: E501
                                                                                                            directory("you"): {                                                                                                                                                        # noqa: E501
                                                                                                                directory("did"): {                                                                                                                                                    # noqa: E501
                                                                                                                    directory("this"): {                                                                                                                                               # noqa: E501
                                                                                                                        directory("in"): {                                                                                                                                             # noqa: E501
                                                                                                                            directory("the"): {                                                                                                                                        # noqa: E501
                                                                                                                                directory("real"): {                                                                                                                                   # noqa: E501
                                                                                                                                    directory("world"): {                                                                                                                              # noqa: E501
                                                                                                                                        directory("you"): {                                                                                                                            # noqa: E501
                                                                                                                                            directory("put"): {                                                                                                                        # noqa: E501
                                                                                                                                                directory("yourself"): {                                                                                                               # noqa: E501
                                                                                                                                                    directory("comfortably"): {                                                                                                        # noqa: E501
                                                                                                                                                        directory("in"): {                                                                                                             # noqa: E501
                                                                                                                                                            directory("a"): {                                                                                                          # noqa: E501
                                                                                                                                                                directory("position"): {                                                                                               # noqa: E501
                                                                                                                                                                    directory("to"): {                                                                                                 # noqa: E501
                                                                                                                                                                        directory("be"): {                                                                                             # noqa: E501
                                                                                                                                                                            directory("downsized"): {                                                                                  # noqa: E501
                                                                                                                                                                                directory("and"): {                                                                                    # noqa: E501
                                                                                                                                                                                    directory("outta"): {                                                                              # noqa: E501
                                                                                                                                                                                        directory("here"): {                                                                           # noqa: E501
                                                                                                                                                                                            directory("as"): {                                                                         # noqa: E501
                                                                                                                                                                                                directory("soul"): {                                                                   # noqa: E501
                                                                                                                                                                                                    directory("position"): {                                                           # noqa: E501
                                                                                                                                                                                                        directory("would"): {                                                          # noqa: E501
                                                                                                                                                                                                            directory("explain"): {                                                    # noqa: E501
                                                                                                                                                                                                                directory("to"): {                                                     # noqa: E501
                                                                                                                                                                                                                    directory("you"): {                                                # noqa: E501
                                                                                                                                                                                                                        file("a_file.hpp"): {                                          # noqa: E501
                                                                                                                                                                                                                            function("std::string", "extremely_nested"): parameters()  # noqa: E501
                                                                                                                                                                                                                        }                                                              # noqa: E501
                                                                                                                                                                                                                    }                                                                  # noqa: E501
                                                                                                                                                                                                                }                                                                      # noqa: E501
                                                                                                                                                                                                            }                                                                          # noqa: E501
                                                                                                                                                                                                        }                                                                              # noqa: E501
                                                                                                                                                                                                    }                                                                                  # noqa: E501
                                                                                                                                                                                                }                                                                                      # noqa: E501
                                                                                                                                                                                            }                                                                                          # noqa: E501
                                                                                                                                                                                        }                                                                                              # noqa: E501
                                                                                                                                                                                    }                                                                                                  # noqa: E501
                                                                                                                                                                                }                                                                                                      # noqa: E501
                                                                                                                                                                            }                                                                                                          # noqa: E501
                                                                                                                                                                        }                                                                                                              # noqa: E501
                                                                                                                                                                    }                                                                                                                  # noqa: E501
                                                                                                                                                                }                                                                                                                      # noqa: E501
                                                                                                                                                            }                                                                                                                          # noqa: E501
                                                                                                                                                        }                                                                                                                              # noqa: E501
                                                                                                                                                    }                                                                                                                                  # noqa: E501
                                                                                                                                                }                                                                                                                                      # noqa: E501
                                                                                                                                            }                                                                                                                                          # noqa: E501
                                                                                                                                        }                                                                                                                                              # noqa: E501
                                                                                                                                    }                                                                                                                                                  # noqa: E501
                                                                                                                                }                                                                                                                                                      # noqa: E501
                                                                                                                            }                                                                                                                                                          # noqa: E501
                                                                                                                        }                                                                                                                                                              # noqa: E501
                                                                                                                    }                                                                                                                                                                  # noqa: E501
                                                                                                                }                                                                                                                                                                      # noqa: E501
                                                                                                            }
                                                                                                        }
                                                                                                    }
                                                                                                }
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return {
            directory("include"): {
                file("absurdly_long_names.hpp"): absurdly_long_names_hpp_contents,
                directory("directory"): absurd_directory_structure
            }
        }
    else:
        return {
            directory("include"): {
                file("absurdly_long_names.hpp"): absurdly_long_names_hpp_contents
            }
        }
예제 #7
0
class CPPNesting(ExhaleTestCase):
    """
    Primary test class for project ``cpp_nesting``.
    """

    test_project = "cpp_nesting"
    """.. testproject:: cpp_nesting"""

    file_hierarchy_dict = {
        directory("include"): {
            file("top_level.hpp"): {
                clike("struct", "top_level"): {}
            },
            directory("nested"): {
                directory("one"): {
                    file("one.hpp"): {
                        namespace("nested"): {
                            clike("struct", "one"): {
                                clike("struct", "params"): {
                                    union("four_bytes"): {}
                                }
                            }
                        }
                    },
                },
                directory("two"): {
                    file("two.hpp"): {
                        namespace("nested"): {
                            clike("struct", "two"): {
                                clike("struct", "params"): {
                                    union("four_bytes"): {}
                                }
                            },
                            union("four_bytes"): {}
                        }
                    }
                },
                directory("dual_nested"): {
                    directory("one"): {
                        file("one.hpp"): {
                            namespace("nested"): {
                                namespace("dual_nested"): {
                                    clike("struct", "one"): {
                                        clike("struct", "params"): {
                                            union("four_bytes"): {}
                                        }
                                    }
                                }
                            }
                        }
                    },
                    directory("two"): {
                        file("two.hpp"): {
                            namespace("nested"): {
                                namespace("dual_nested"): {
                                    clike("struct", "two"): {
                                        clike("struct", "params"): {
                                            union("four_bytes"): {}
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    class_hierarchy_dict = {
        clike("struct", "top_level"): {},
        namespace("nested"): {
            clike("struct", "one"): {
                clike("struct", "params"): {
                    union("four_bytes"): {}
                }
            },
            clike("struct", "two"): {
                clike("struct", "params"): {
                    union("four_bytes"): {}
                }
            },
            union("four_bytes"): {},
            namespace("dual_nested"): {
                clike("struct", "one"): {
                    clike("struct", "params"): {
                        union("four_bytes"): {}
                    }
                },
                clike("struct", "two"): {
                    clike("struct", "params"): {
                        union("four_bytes"): {}
                    }
                }
            }
        }
    }

    def test_hierarchies(self):
        """Verify the class and file hierarchies."""
        compare_file_hierarchy(self, file_hierarchy(self.file_hierarchy_dict))
        compare_class_hierarchy(self,
                                class_hierarchy(self.class_hierarchy_dict))

    @confoverrides(exhale_args={"doxygenStripFromPath": "../include"})
    def test_hierarchies_stripped(self):
        """
        Verify the class and file hierarchies with ``doxygenStripFromPath=../include``.

        .. todo:: this test is not supported yet
        """
        return  # TODO: Exhale should remove the include/ directory
        # dirty hack to pop off the first include/ directory without needing to know
        # the actual object that is the first and only key
        for key in self.file_hierarchy_dict:
            no_include = self.file_hierarchy_dict[key]
            break
        compare_file_hierarchy(self, file_hierarchy(no_include))
        compare_class_hierarchy(self,
                                class_hierarchy(self.class_hierarchy_dict))
예제 #8
0
class CPPFortranMixed(ExhaleTestCase):
    """
    Primary test class for project ``cpp_fortran_mixed``.

    This test class exists to test explicit language lexer mappings via
    :data:`~exhale.configs.lexerMapping`.  Note that the C++ code and namespace
    ``convert`` is definitively chosen to be different than the name of the fortran
    module name (``conversions``).  If they are the same, some interesting things
    happen on the Doxygen side, because Doxygen will combine the namespaces.

    .. todo::

        These tests are incomplete (``from __future__ import config_objects``).  The
        module level storage creates conflicts between the test cases when specifying
        the regular expressions :/

        Currently there is only one test for a custom lexer mapping, and it only
        actually works because the name is alphabetically sorted after the default
        test (meaning pytest will run it after).

    .. todo::

        The full function validation does not support templates (yet).  Templates at
        large in the testing framework need to be reworked, currently it is all boiler
        plate code with no actual validation...

    """

    test_project = "cpp_fortran_mixed"
    """.. testproject:: cpp_fortran_mixed"""

    file_hierarchy_dict = {
        directory("include"): {
            directory("convert"): {
                file("convert.hpp"): {
                    namespace("convert"): {
                        function("T", "to_degrees", template=["typename T"]):
                        parameters("T"),
                        function("T", "to_radians", template=["typename T"]):
                        parameters("T")
                    }
                }
            }
        },
        directory("src"): {
            file("conversions.f90"): {
                namespace("conversions"): {
                    variable("real(c_float)", "pi_s"): {},
                    variable("real(c_double)", "pi_d"): {},
                    variable("real(c_float)", "s_180"): {},
                    variable("real(c_double)", "d_180"): {},
                    # NOTE: function parameters in fortran are a little weird.
                    # 1. <type> has 'function', e.g. 'real(c_float) function'
                    # 2. Parameters are names, not types?
                    function("real(c_float) function", "degrees_to_radians_s"):
                    parameters("degrees_s"),
                    function("real(c_double) function", "degrees_to_radians_d"):
                    parameters("degrees_d"),
                    function("real(c_float) function", "radians_to_degrees_s"):
                    parameters("radians_s"),
                    function("real(c_double) function", "radians_to_degrees_d"):
                    parameters("radians_d")
                }
            }
        }
    }
    """The file hierarchy for this project."""
    def test_hierarchies(self):
        """
        Validate the class and file hierarchies.

        **Not tested on Windows**, maybe I'll care to fix it one day.
        `But it is not this day`__!

        __ https://youtu.be/EXGUNvIFTQw

        .. todo::

            Too much shared code in hierarchy comparisons, the class hierarchy for this
            project should be **empty**.  Need to enable asserting this fact (same for
            the ``cpp_nesting`` project).
        """
        if platform.system() != "Windows":
            compare_file_hierarchy(self,
                                   file_hierarchy(self.file_hierarchy_dict))
            # compare_class_hierarchy(self, class_hierarchy({}))

    def validate_pygments_lexers(self, exhale_root, node_map):
        """
        Validate nodes have the expected pygments lexer in their program listing file.

        **Parameters**

            ``exhale_root`` (:class:`exhale.graph.ExhaleRoot`)
                The graph root object for this project.

            ``node_map`` (:class:`python:dict`)
                A map of :class:`exhale.graph.ExhaleNode` objects to string values.  The
                keys must be the nodes extracted from the ``exhale_root`` (as opposed
                to a testing type).  Each value should be a pygments lexer, such as
                ``"cpp"`` or ``"fortran"``.

                The generated program listing file will be parsed and an assert
                statement performed for equality to these specified values.
        """
        lexer_regex = re.compile(r"^.. code-block:: (.*)$")

        for file_node in node_map:
            expected_lexer = node_map[file_node]
            program_listing_file_path = os.path.join(
                exhale_root.root_directory, file_node.program_file)

            lexer_was_asserted = False
            with open(program_listing_file_path) as program_listing_file:
                for line in program_listing_file:
                    lexer_match = lexer_regex.match(line)
                    if lexer_match:
                        lexer = lexer_match.groups()[0]
                        self.assertTrue(
                            lexer == expected_lexer,
                            "{0}: expected '{1}' but got '{2}' language lexer."
                            .format(file_node.location, expected_lexer, lexer))
                        lexer_was_asserted = True
                        break

            # Make sure we actually ran a check for this file.
            self.assertTrue(
                lexer_was_asserted,
                "Did not find '.. code-block:: xxxx' in [{0}]".format(
                    program_listing_file_path))

    def get_hpp_and_f90_nodes(self, exhale_root):
        """
        Return the two :class:`~exhale.graph.ExhaleNode` objects for this project.

        **Parameters**

            ``exhale_root`` (:class:`~exhale.graph.ExhaleRoot`)
                The graph root object for this project.

        **Returns**

            :class:`python:tuple`
                A length two tuple of :class:`~exhale.graph.ExhaleNode` objects, ordered
                as ``(convert_hpp, conversions_f90)``.  These represent the files
                ``include/convert/convert.hpp`` and ``src/conversions.f90``,
                respectively.
        """
        convert_hpp = None
        convert_hpp_path = os.path.join("include", "convert", "convert.hpp")
        conversions_f90 = None
        conversions_f90_path = os.path.join("src", "conversions.f90")
        for f in exhale_root.files:
            if f.location == convert_hpp_path:
                convert_hpp = f
                continue
            elif f.location == conversions_f90_path:
                conversions_f90 = f
                continue

        self.assertTrue(
            convert_hpp is not None,
            "Could not find ExhaleNode with file location 'include/convert/convert.hpp'."
        )
        self.assertTrue(
            conversions_f90 is not None,
            "Could not find ExhaleNode with file location 'src/conversions.f90'."
        )

        return (convert_hpp, conversions_f90)

    def test_default_lexers(self):
        """
        Verify ``convert.hpp`` maps to ``"cpp"`` and ``conversions.f90`` to ``"fortran"``.
        """
        exhale_root = get_exhale_root(self)
        convert_hpp, conversions_f90 = self.get_hpp_and_f90_nodes(exhale_root)
        self.validate_pygments_lexers(exhale_root, {
            convert_hpp: "cpp",
            conversions_f90: "fortran"
        })

    @confoverrides(exhale_args={"lexerMapping": {r".*\.f90": "python"}})
    def test_modified_fortran(self):
        """
        Verify regular expression overload of ``*.f90`` files map to ``"python"``.
        """
        exhale_root = get_exhale_root(self)
        convert_hpp, conversions_f90 = self.get_hpp_and_f90_nodes(exhale_root)
        self.validate_pygments_lexers(exhale_root, {
            convert_hpp: "cpp",
            conversions_f90: "python"
        })