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