Testing Hierarchies Module
Various helper classes and functions for validating the class and file hierarchies.
Every derived class of testing.base.ExhaleTestCase
should have at least one
test case validating the class and file hierarchies. The class and file hierarchies
do not need to be validated in the same method. In both cases, the recipe is:
Create a hierarchy that enumerates all of the documented code in the test project. This will either be an instance of
hierarchies.class_hierarchy
, or ofhierarchies.file_hierarchy
.Call the comparison function for the created hierarchy:
compare_class_hierarchy()
orcompare_file_hierarchy()
.
Contents
Representing Hierarchies
![digraph G {
rankdir="LR";
ranksep=0.88;
edge [dir=none];
subgraph cluster_exhale {
style=filled;
color="#a2ddf9";
label="Exhale Type";
fontsize=22;
node [style=filled, shape=rectangle, fontname="Courier"];
e_fake_root_1 [style=none, shape=none, label=""];
exhale_root [label="exhale.graph.ExhaleRoot"];
e_fake_root_2 [style=none, shape=none, label=""];
}
subgraph cluster_testing {
style=filled;
color="#f7f389";
label="Testing Types";
fontsize=22;
node [
style=filled,
shape=rectangle,
fontname="Courier",
width=2,
fixedsize=true
];
fake_root_1 [style=none, shape=none, label=""];
hierarchy_root [label="hierarchies.root"];
fake_root_2 [style=none, shape=none, label=""];
file_hierarchy [label="file_hierarchy"];
fake_hierarchy [style=none, shape=none, label=""];
class_hierarchy [label="class_hierarchy"];
hierarchy_root:e -> file_hierarchy:w;
hierarchy_root -> fake_hierarchy [style=none, color=none];
hierarchy_root:e -> class_hierarchy:w;
{rank=same; fake_root_1; hierarchy_root; fake_root_2}
{rank=same; class_hierarchy; fake_hierarchy; file_hierarchy}
}
exhale_root -> hierarchy_root [style=dashed, label="represents"];
}](../_images/graphviz-3a8ba26554a35d8e547182160225e59fff66b285.png)
During normal execution, exhale.graph.ExhaleRoot
essentially represents
index.xml
coming from Doxygen. The index.xml
enumerates far more than what
the testing.hierarchies.root
class represents, the primary goal for the class
and file hierarchies is to validate generated (from Exhale) and expected (from the test)
output with respect to
- Name Scope Resolutions (
testing.hierarchies.class_hierarchy
) Particularly, parent-child relationships constructed in
exhale.graph.ExhaleRoot
. See Class Hierarchies.- Parsed Directory Structure (
testing.hierarchies.file_hierarchy
) Directory and file structure, and which file defines which documented construct. See File Hierarchies.
When creating a hierarchy to test, the author creates a json-like dictionary encoding the expected relationships. Generally, every helper class maps to a nested dictionary value, with some exceptions:
![digraph G {
rankdir="LR";
ranksep=0.88;
edge [dir=none];
subgraph cluster_exhale {
style=filled;
color="#a2ddf9";
label="Exhale Type";
fontsize=22;
node [style=filled, shape=rectangle, fontname="Courier"];
subgraph cluster_hacksize {
label=" ";// soooo hacky...
fontsize=18;
fake_node_1 [style=none, shape=none, label=""];
fake_node_2 [style=none, shape=none, label=""];
fake_node_3 [style=none, shape=none, label=""];
fake_node_4 [style=none, shape=none, label=""];
exhale_node [label="exhale.graph.ExhaleNode"];
fake_node_5 [style=none, shape=none, label=""];
fake_node_6 [style=none, shape=none, label=""];
fake_node_7 [style=none, shape=none, label=""];
fake_node_8 [style=none, shape=none, label=""];
fake_node_9 [style=none, shape=none, label=""];
}
}
subgraph cluster_testing {
style=filled;
color="#f7f389";
label="Testing Types";
fontsize=22;
node [
style=filled,
shape=rectangle,
fontname="Courier",
width=2,
fixedsize=true
];
hierarchy_node [label="hierarchies.node"];
subgraph cluster_keys {
style=filled;
color=cadetblue;
label="Key Type";
fontsize=18;
clike;
define;
directory;
enum;
file;
function;
namespace;
typedef;
union;
variable;
}
subgraph cluster_values {
style=filled;
color=mediumaquamarine;
label="Value Type";
fontsize=18;
v_clike [label="dict"];
v_define [label="????"];
v_directory [label="dict"];
v_enum [label="????"];
v_file [label="dict"];
v_function [label="parameters"];
v_namespace [label="dict"];
v_typedef [label="????"];
v_union [label="dict"];
v_variable [label="????"];
}
hierarchy_node -> clike:w;
hierarchy_node -> define:w;
hierarchy_node -> directory:w;
hierarchy_node -> enum:w;
hierarchy_node -> file:w;
hierarchy_node -> function:w;
hierarchy_node -> namespace:w;
hierarchy_node -> typedef:w;
hierarchy_node -> union:w;
hierarchy_node -> variable:w;
clike -> v_clike [dir=forward];
define -> v_define [dir=forward];
directory -> v_directory [dir=forward];
enum -> v_enum [dir=forward];
file -> v_file [dir=forward];
function -> v_function [dir=forward];
namespace -> v_namespace [dir=forward];
typedef -> v_typedef [dir=forward];
union -> v_union [dir=forward];
variable -> v_variable [dir=forward];
}
exhale_node -> hierarchy_node [dir=none];
fake_node_1 -> clike [style=none, color=none];
}](../_images/graphviz-5d794c29f7474f50be0feb788d9cd7c394e1e434.png)
Todo
????
values indicate items not currently implemented or used in the testing
framework.
- class testing.hierarchies.root(hierarchy_type, hierarchy)[source]
Represent a class or file hierarchy to simulate an
exhale.graph.ExhaleRoot
.- Parameters
hierarchy_type
(str
)May be either
"class"
or"file"
, indicating which type of hierarchy is being represented.hierarchy
(dict
)The hierarchy dictionary, see reference documentation for
class_hierarchy
and / orfile_hierarchy
for examples.
- Raises
ValueError
If
hierarchy_type
is neither"class"
nor"file"
, or the specifiedhierarchy
not a dictionary or malformed.
- toConsole()[source]
Dump the hierarchy to the console.
Calls
node.toConsole
for eachhierarchies.node
inself.top_level
.
Class Hierarchies
- class testing.hierarchies.class_hierarchy(hierarchy)[source]
Represent a name scope hierarchy.
The class hierarchy represents things that in C++ would equate to using a
::
to gain access to. This includes:Classes and structs (
hierarchies.clike
).Enums (
hierarchies.enum
).Namespaces (
hierarchies.namespace
).Unions (
hierarchies.union
).
Consider the following C++ code:
// in file: include/main.h #pragma once namespace detail { struct SomeStruct { /* ... */ }; } struct SomeStruct { struct View { /* ... */ }; };
Then the testing code may look like:
from testing.base import ExhaleTestCase from testing.hierarchies import class_hierarchy, \ clike, \ compare_class_hierarchy, \ namespace class SomeTest(ExhaleTestCase): test_project = "..." # specify the correct name... def test_class_hierarchy(self): class_hierarchy_dict = { clike("struct", "SomeStruct"): { clike("struct", "View"): {} }, namespace("detail"): { clike("struct", "SomeStruct"): {} } } compare_class_hierarchy(self, class_hierarchy(class_hierarchy_dict))
- Parameters
hierarchy
(dict
)The hierarchy associated with the name scopes for the test project.
- testing.hierarchies.compare_class_hierarchy(test, test_root)[source]
Compare the parsed and expected class hierarchy for the specified test.
This method should only be called in a
test_*
method implemented in aExhaleTestCase
member function.- Parameters
test
(ExhaleTestCase
)The test instance. This test will have its
assert*
methods called in this method. Theexhale.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
ValueError
When
test
is not anExhaleTestCase
, ortest_root
is not aclass_hierarchy
.
File Hierarchies
- class testing.hierarchies.file_hierarchy(hierarchy)[source]
Represent a parsed directory structure, including which file defines which compound.
Note
The test case file hierarchies encompass more than just what should show up in the generated Exhale file hierarchy (which only includes directories and files).
Specifically, the test file hierarchy is expected to encode every documented object in the project. This means there will be duplicated constructs between the class and file hierarchy tests.
This is done in order to help check that the file a construct was defined in is correctly parsed / generated by Exhale.
Working with the same example code as above:
// in file: include/main.h #pragma once namespace detail { struct SomeStruct { /* ... */ }; } struct SomeStruct { struct View { /* ... */ }; };
The testing code is essentially the same dictionary, only we need to include directory and file information:
from testing.base import ExhaleTestCase from testing.hierarchies import clike, \ compare_file_hierarchy, \ directory, \ file \ file_hierarchy \ namespace class SomeTest(ExhaleTestCase): test_project = "..." # specify the correct name... def test_file_hierarchy(self): file_hierarchy_dict = { directory("include"): { file("main.h"): { clike("struct", "SomeStruct"): { clike("struct", "View"): {} }, namespace("detail"): { clike("struct", "SomeStruct"): {} } } } } compare_file_hierarchy(self, file_hierarchy(file_hierarchy_dict))
- Parameters
hierarchy
(dict
)The hierarchy associated with the name scopes for the test project.
- testing.hierarchies.compare_file_hierarchy(test, test_root)[source]
Compare the parsed and expected file hierarchy for the specified test.
This method should only be called in a
test_*
method implemented in aExhaleTestCase
member function.- Parameters
test
(ExhaleTestCase
)The test instance. This test will have its
assert*
methods called in this method. Theexhale.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
ValueError
When
test
is not anExhaleTestCase
, ortest_root
is not afile_hierarchy
.
Utility Classes
- class testing.hierarchies.node(name, kind)[source]
Testing hierarchy parent class for pass-through construction of
exhale.graph.ExhaleNode
.Upon construction, the parent class’s
refid
parameter is set as the empty string in the testing framework. This item is the Doxygen hash value, which is not available until after Doxygen has been executed.- Parameters
name
(str
)The name of the documented compound.
kind
(str
)Assumed to be one of the types in
exhale.utils.AVAILABLE_KINDS
.
- __repr__()[source]
Return
ExhaleNode.__repr__
, possibly manufacturingself.template_params
.A dirty hack to piggy-back off of ExhaleNode’s
__repr__
, the same value is returned but for anything with a template we need to coerce theExhaleNode.template_params
.When parsing from doxygen you get:
((refid, typed), declared_name, defined_name)
But in the testing framework we’re just using lists of strings. Hallucinate everything as the typeid as far as
ExhaleNode
is concerned.Todo
On a day that is not this day, make
ExhaleNode.template_params
use aclass Tparam
and reuse that here and in the testing framework. And just rename it totemplate
, unifying withkind=function
, and have the testing framework use the same variable name. But that’s a lot of effort right now and I just want good debugging prints.
Classes and Structs
Directories
Enums
Files
Functions
- class testing.hierarchies.function(return_type, name, template=None)[source]
Represent a (partial)
function
.Note
This key must always map to a value of
hierarchies.parameters
.function("int", "add"): parameters("int", "int")
represents the function declaration
int add(int a, int b);
Note that parameter names (
a
andb
in this example) are not to be included, only parameter types.- Parameters
- setParameters(parameters)[source]
Set the parameters of this function.
Since this class is to map to a value of type
hierarchies.parameters
, when the dictionary is being parsed this method will be called.
- class testing.hierarchies.parameters(*args)[source]
Represent a
hierarchies.function
parameters.- Parameters
*args
(Parameter Pack)Arbitrary list, assumed to be all strings. For example,
int add(int a, int b);
is represented by creating
function("int", "add"): parameters("int", "int")
whereas
void serialize(Serializer &s, const std::string &name, int id);
is represented by creating
function("void", "serialize"): parameters( "Serializer&", "const std::string&", "int" )
Only parameter types are to be included, not declared names (
int, int
notint a, int b
).
- Attributes