コード例 #1
0
ファイル: configs.py プロジェクト: tower120/exhale
 def test_no_exclusion(self):
     """Verify empty list means no exlusions from full API."""
     full_names, orphan_names = self.checkAllFilesIncluded()
     root = get_exhale_root(self)
     total = self.total(root)
     assert len(full_names) == total
     assert len(orphan_names) == 0
コード例 #2
0
ファイル: cpp_fortran_mixed.py プロジェクト: svenevs/exhale
 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"}
     )
コード例 #3
0
ファイル: cpp_fortran_mixed.py プロジェクト: svenevs/exhale
 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"}
     )
コード例 #4
0
 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"
     })
コード例 #5
0
 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"
     })
コード例 #6
0
ファイル: cpp_pimpl.py プロジェクト: tower120/exhale
    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)
コード例 #7
0
ファイル: cpp_pimpl.py プロジェクト: svenevs/exhale
    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
        )
コード例 #8
0
ファイル: configs.py プロジェクト: tower120/exhale
    def _validate_diff_for_kinds(self, diff, *kinds):
        """Check if ``diff`` items got orphaned."""
        # Initial check: verify expected lengths.
        full_names, orphan_names = self.checkAllFilesIncluded()
        root = get_exhale_root(self)
        total = self.total(root)
        assert len(full_names) == total - diff
        assert len(orphan_names) == diff

        # Verify the right things actually got excluded.
        for node in root.all_nodes:
            if node.kind in kinds:
                assert node.file_name in orphan_names
            else:
                assert node.file_name not in orphan_names
コード例 #9
0
    def validate_full_api_listing(self):
        """
        Validate that every node shows up in the full api listing.

        .. todo:: Maybe this should be a default check instead?
        """
        exhale_root = get_exhale_root(self)
        with open(exhale_root.unabridged_api_file) as api_root:
            api_root_contents = api_root.read()

        for node in exhale_root.all_nodes:
            self.assertTrue(
                node.file_name in api_root_contents,
                "{file_name} not included in {unabridged_api}!".format(
                    file_name=node.file_name,
                    unabridged_api=exhale_root.unabridged_api_file))
コード例 #10
0
ファイル: cpp_pimpl.py プロジェクト: svenevs/exhale
    def validate_full_api_listing(self):
        """
        Validate that every node shows up in the full api listing.

        .. todo:: Maybe this should be a default check instead?
        """
        exhale_root = get_exhale_root(self)
        with open(exhale_root.unabridged_api_file) as api_root:
            api_root_contents = api_root.read()

        for node in exhale_root.all_nodes:
            self.assertTrue(
                node.file_name in api_root_contents,
                "{file_name} not included in {unabridged_api}!".format(
                    file_name=node.file_name,
                    unabridged_api=exhale_root.unabridged_api_file
                )
            )
コード例 #11
0
ファイル: cpp_pimpl.py プロジェクト: svenevs/exhale
    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
        )
コード例 #12
0
ファイル: cpp_pimpl.py プロジェクト: tower120/exhale
    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)
コード例 #13
0
ファイル: hierarchies.py プロジェクト: svenevs/exhale
def compare_file_hierarchy(test, test_root):
    """
    Compare the parsed and expected file hierarchy for the specified test.

    This method should only be called in a ``test_*`` method implemented in a
    |ExhaleTestCase| member function.

    **Parameters**
        ``test`` (|ExhaleTestCase|)
            The test instance.  This test will have its ``assert*`` methods called
            in this method.  The :class:`exhale.graph.ExhaleRoot` instance for the test
            project is acquired through this parameter.

        ``test_root`` (|file_hierarchy|)
            The class hierarchy to compare the parsed root with.

    **Raises**
        :class:`python:ValueError`
            When ``test`` is not an |ExhaleTestCase|, or ``test_root`` is not a
            |file_hierarchy|.

    .. |file_hierarchy| replace:: :class:`file_hierarchy <testing.hierarchies.file_hierarchy>`
    """
    # Some simple sanity checks
    if not isinstance(test, ExhaleTestCase):
        raise ValueError(
            "'test' parameter was not an instance of 'testing.base.ExhaleTestCase'."
        )
    if not isinstance(test_root, file_hierarchy):
        raise ValueError("test_root parameter must be an instance of `file_hierarchy`.")

    # Run some preliminary tests
    exhale_root = get_exhale_root(test)
    test.assertEqual(len(test_root.dirs), len(exhale_root.dirs))
    test.assertEqual(len(test_root.files), len(exhale_root.files))
    for test_obj in test_root.top_level:
        exhale_obj = None
        if test_obj.kind == "dir":
            for d in exhale_root.dirs:
                if d.name == test_obj.name:  # TODO: duplicate directory names (nested)?
                    exhale_obj = d
                    break
        elif test_obj.kind == "file":
            for f in exhale_root.files:
                if f.name == test_obj.name:  # TODO: duplicate file names (nested)?
                    exhale_obj = f
                    break

        if exhale_obj is None:
            raise RuntimeError("Did not find match for [{0}] {1}".format(
                test_obj.kind, test_obj.name
            ))
        _compare_children("file", test, test_obj, exhale_obj)

    # Functions needs to be checked explicitly (overloaded function names are same...)
    test.assertEqual(len(test_root.functions), len(exhale_root.functions))

    def find_overloads(root):
        # keys: string function names
        # values: list of nodes (length 2 or larger indicates overload)
        overloads = {}
        for func in root.functions:
            if func.name not in overloads:
                overloads[func.name] = [func]
            else:
                overloads[func.name].append(func)

        return overloads

    test_overloads   = find_overloads(test_root)
    exhale_overloads = find_overloads(exhale_root)

    # Create explicit sets to be able to use in error message.
    test_overloads_keys   = set(test_overloads.keys())
    exhale_overloads_keys = set(exhale_overloads.keys())

    # enumerate items in set on their own lines
    def set_error_string(s):
        if not s:
            return "{ /* empty */ }"
        ret = "{\n"
        for item in s:
            ret += "  {item}\n".format(item=item)
        ret += "}"
        return ret

    test.assertEqual(
        test_overloads_keys,
        exhale_overloads_keys,
        # Error messages for sets are quite nice locally, but on CI they are not as
        # helpful.  Probably a python 2 vs 3 thing?  The below information is enough to
        # figure out where the problem is.
        textwrap.dedent('''\
            Functions grouped by overload name not equivalent!

            ==> e (expected, as enumerated by test):
            {expected}

            ==> d (discovered by exhale):
            {discovered}

            ==> Intersection [ e & d ]:
            {intersection}

            ==> Difference [ e - d ]:
            {difference_e_min_d}

            ==> Difference [ d - e ]:
            {difference_d_min_e}

            ==> Symmetric Difference [ e ^ d ]:
            {symmetric_difference}
        ''').format(
            expected=set_error_string(test_overloads_keys),
            discovered=set_error_string(exhale_overloads_keys),
            intersection=set_error_string(test_overloads_keys & exhale_overloads_keys),
            difference_e_min_d=set_error_string(test_overloads_keys - exhale_overloads_keys),
            difference_d_min_e=set_error_string(exhale_overloads_keys - test_overloads_keys),
            symmetric_difference=set_error_string(test_overloads_keys ^ exhale_overloads_keys)
        )
    )

    for key in test_overloads:
        # Surface-level test: must be the same length.
        test.assertEqual(
            len(test_overloads[key]),
            len(exhale_overloads[key]),
            "Function overload group [{group}]:\nTest:\n{test_ids}\n\nExhale:\n{exhale_ids}\n".format(
                group=key,
                test_ids="".join(
                    "\n - {0}".format(f.full_signature()) for f in test_overloads[key]
                ),
                exhale_ids="".join(
                    "\n - {0}".format(f.full_signature()) for f in exhale_overloads[key]
                )
            )
        )

        # Validate the return type, name, and signatures.
        test_functions = set(f.full_signature() for f in test_overloads[key])
        exhale_functions = set(f.full_signature() for f in exhale_overloads[key])
        # The error message when not equal is _beautiful_ <3
        test.assertEqual(test_functions, exhale_functions)
コード例 #14
0
ファイル: cpp_pimpl.py プロジェクト: svenevs/exhale
    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
        }
コード例 #15
0
ファイル: cpp_pimpl.py プロジェクト: tower120/exhale
    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
        }
コード例 #16
0
def compare_file_hierarchy(test, test_root):
    """
    Compare the parsed and expected file hierarchy for the specified test.

    This method should only be called in a ``test_*`` method implemented in a
    |ExhaleTestCase| member function.

    **Parameters**
        ``test`` (|ExhaleTestCase|)
            The test instance.  This test will have its ``assert*`` methods called
            in this method.  The :class:`exhale.graph.ExhaleRoot` instance for the test
            project is acquired through this parameter.

        ``test_root`` (|file_hierarchy|)
            The class hierarchy to compare the parsed root with.

    **Raises**
        :class:`python:ValueError`
            When ``test`` is not an |ExhaleTestCase|, or ``test_root`` is not a
            |file_hierarchy|.

    .. |file_hierarchy| replace:: :class:`file_hierarchy <testing.hierarchies.file_hierarchy>`
    """
    # Some simple sanity checks
    if not isinstance(test, ExhaleTestCase):
        raise ValueError(
            "'test' parameter was not an instance of 'testing.base.ExhaleTestCase'."
        )
    if not isinstance(test_root, file_hierarchy):
        raise ValueError(
            "test_root parameter must be an instance of `file_hierarchy`.")

    # Run some preliminary tests
    exhale_root = get_exhale_root(test)
    test.assertEqual(len(test_root.dirs), len(exhale_root.dirs))
    test.assertEqual(len(test_root.files), len(exhale_root.files))
    for test_obj in test_root.top_level:
        exhale_obj = None
        if test_obj.kind == "dir":
            for d in exhale_root.dirs:
                if d.name == test_obj.name:  # TODO: duplicate directory names (nested)?
                    exhale_obj = d
                    break
        elif test_obj.kind == "file":
            for f in exhale_root.files:
                if f.name == test_obj.name:  # TODO: duplicate file names (nested)?
                    exhale_obj = f
                    break

        if exhale_obj is None:
            raise RuntimeError("Did not find match for [{0}] {1}".format(
                test_obj.kind, test_obj.name))
        _compare_children("file", test, test_obj, exhale_obj)

    # Functions needs to be checked explicitly (overloaded function names are same...)
    test.assertEqual(len(test_root.functions), len(exhale_root.functions))

    def find_overloads(root):
        # keys: string function names
        # values: list of nodes (length 2 or larger indicates overload)
        overloads = {}
        for func in root.functions:
            if func.name not in overloads:
                overloads[func.name] = [func]
            else:
                overloads[func.name].append(func)

        return overloads

    test_overloads = find_overloads(test_root)
    exhale_overloads = find_overloads(exhale_root)

    # Create explicit sets to be able to use in error message.
    test_overloads_keys = set(test_overloads.keys())
    exhale_overloads_keys = set(exhale_overloads.keys())

    # enumerate items in set on their own lines
    def set_error_string(s):
        if not s:
            return "{ /* empty */ }"
        ret = "{\n"
        for item in s:
            ret += "  {item}\n".format(item=item)
        ret += "}"
        return ret

    test.assertEqual(
        test_overloads_keys,
        exhale_overloads_keys,
        # Error messages for sets are quite nice locally, but on CI they are not as
        # helpful.  Probably a python 2 vs 3 thing?  The below information is enough to
        # figure out where the problem is.
        textwrap.dedent('''\
            Functions grouped by overload name not equivalent!

            ==> e (expected, as enumerated by test):
            {expected}

            ==> d (discovered by exhale):
            {discovered}

            ==> Intersection [ e & d ]:
            {intersection}

            ==> Difference [ e - d ]:
            {difference_e_min_d}

            ==> Difference [ d - e ]:
            {difference_d_min_e}

            ==> Symmetric Difference [ e ^ d ]:
            {symmetric_difference}
        ''').format(
            expected=set_error_string(test_overloads_keys),
            discovered=set_error_string(exhale_overloads_keys),
            intersection=set_error_string(test_overloads_keys
                                          & exhale_overloads_keys),
            difference_e_min_d=set_error_string(test_overloads_keys -
                                                exhale_overloads_keys),
            difference_d_min_e=set_error_string(exhale_overloads_keys -
                                                test_overloads_keys),
            symmetric_difference=set_error_string(test_overloads_keys
                                                  ^ exhale_overloads_keys)))

    for key in test_overloads:
        # Surface-level test: must be the same length.
        test.assertEqual(
            len(test_overloads[key]), len(exhale_overloads[key]),
            "Function overload group [{group}]:\nTest:\n{test_ids}\n\nExhale:\n{exhale_ids}\n"
            .format(group=key,
                    test_ids="".join("\n - {0}".format(f.full_signature())
                                     for f in test_overloads[key]),
                    exhale_ids="".join("\n - {0}".format(f.full_signature())
                                       for f in exhale_overloads[key])))

        # Validate the return type, name, and signatures.
        test_functions = set(f.full_signature() for f in test_overloads[key])
        exhale_functions = set(f.full_signature()
                               for f in exhale_overloads[key])
        # The error message when not equal is _beautiful_ <3
        test.assertEqual(test_functions, exhale_functions)
コード例 #17
0
def compare_class_hierarchy(test, test_root):
    """
    Compare the parsed and expected class hierarchy for the specified test.

    This method should only be called in a ``test_*`` method implemented in a
    |ExhaleTestCase| member function.

    **Parameters**
        ``test`` (|ExhaleTestCase|)
            The test instance.  This test will have its ``assert*`` methods called
            in this method.  The :class:`exhale.graph.ExhaleRoot` instance for the test
            project is acquired through this parameter.

        ``test_root`` (|class_hierarchy|)
            The class hierarchy to compare the parsed root with.

    **Raises**
        :class:`python:ValueError`
            When ``test`` is not an |ExhaleTestCase|, or ``test_root`` is not a
            |class_hierarchy|.

    .. |ExhaleTestCase| replace:: :class:`ExhaleTestCase <testing.base.ExhaleTestCase>`
    .. |class_hierarchy| replace:: :class:`class_hierarchy <testing.hierarchies.class_hierarchy>`
    """
    # Some simple sanity checks
    if not isinstance(test, ExhaleTestCase):
        raise ValueError(
            "'test' parameter was not an instance of 'testing.base.ExhaleTestCase'."
        )
    if not isinstance(test_root, class_hierarchy):
        raise ValueError(
            "test_root parameter must be an instance of `class_hierarchy`.")

    # Run some preliminary tests
    exhale_root = get_exhale_root(test)
    test.assertEqual(len(test_root.class_like), len(exhale_root.class_like))
    test.assertEqual(len(test_root.enums), len(exhale_root.enums))
    test.assertEqual(len(test_root.namespaces), len(exhale_root.namespaces))
    test.assertEqual(len(test_root.unions), len(exhale_root.unions))

    for test_obj in test_root.top_level:
        exhale_obj = None
        if test_obj.kind in ["class", "struct"]:
            for cl in exhale_root.class_like:
                if cl.name == test_obj.name and cl.kind == test_obj.kind:
                    exhale_obj = cl
                    break
        elif test_obj.kind == "enum":
            for e in exhale_root.enums:
                if e.name == test_obj.name:
                    exhale_obj = e
                    break
        elif test_obj.kind == "namespace":
            for n in exhale_root.namespaces:
                if n.name == test_obj.name:
                    exhale_obj = n
                    break
        elif test_obj.kind == "union":
            for u in exhale_root.unions:
                if u.name == test_obj.name:
                    exhale_obj = u
                    break

        if exhale_obj is None:
            test.assertTrue(False,
                            msg="Did not find match for [{0}] {1}".format(
                                test_obj.kind, test_obj.name))

        _compare_children("class", test, test_obj, exhale_obj)
コード例 #18
0
ファイル: hierarchies.py プロジェクト: svenevs/exhale
def compare_class_hierarchy(test, test_root):
    """
    Compare the parsed and expected class hierarchy for the specified test.

    This method should only be called in a ``test_*`` method implemented in a
    |ExhaleTestCase| member function.

    **Parameters**
        ``test`` (|ExhaleTestCase|)
            The test instance.  This test will have its ``assert*`` methods called
            in this method.  The :class:`exhale.graph.ExhaleRoot` instance for the test
            project is acquired through this parameter.

        ``test_root`` (|class_hierarchy|)
            The class hierarchy to compare the parsed root with.

    **Raises**
        :class:`python:ValueError`
            When ``test`` is not an |ExhaleTestCase|, or ``test_root`` is not a
            |class_hierarchy|.

    .. |ExhaleTestCase| replace:: :class:`ExhaleTestCase <testing.base.ExhaleTestCase>`
    .. |class_hierarchy| replace:: :class:`class_hierarchy <testing.hierarchies.class_hierarchy>`
    """
    # Some simple sanity checks
    if not isinstance(test, ExhaleTestCase):
        raise ValueError(
            "'test' parameter was not an instance of 'testing.base.ExhaleTestCase'."
        )
    if not isinstance(test_root, class_hierarchy):
        raise ValueError("test_root parameter must be an instance of `class_hierarchy`.")

    # Run some preliminary tests
    exhale_root = get_exhale_root(test)
    test.assertEqual(len(test_root.class_like), len(exhale_root.class_like))
    test.assertEqual(len(test_root.enums), len(exhale_root.enums))
    test.assertEqual(len(test_root.namespaces), len(exhale_root.namespaces))
    test.assertEqual(len(test_root.unions), len(exhale_root.unions))

    for test_obj in test_root.top_level:
        exhale_obj = None
        if test_obj.kind in ["class", "struct"]:
            for cl in exhale_root.class_like:
                if cl.name == test_obj.name and cl.kind == test_obj.kind:
                    exhale_obj = cl
                    break
        elif test_obj.kind == "enum":
            for e in exhale_root.enums:
                if e.name == test_obj.name:
                    exhale_obj = e
                    break
        elif test_obj.kind == "namespace":
            for n in exhale_root.namespaces:
                if n.name == test_obj.name:
                    exhale_obj = n
                    break
        elif test_obj.kind == "union":
            for u in exhale_root.unions:
                if u.name == test_obj.name:
                    exhale_obj = u
                    break

        if exhale_obj is None:
            test.assertTrue(
                False,
                msg="Did not find match for [{0}] {1}".format(test_obj.kind, test_obj.name)
            )

        _compare_children("class", test, test_obj, exhale_obj)