Skip to content

Commit

Permalink
Create CTest unit tests to run the Python example files.
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson committed Nov 29, 2023
1 parent a06a6d5 commit 81297ca
Show file tree
Hide file tree
Showing 33 changed files with 599 additions and 89 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
sudo apt-get -y install ninja-build
- name: Install Sphinx
run: |
sudo pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme
sudo pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
- name: Setup NuGet Credentials
shell: bash
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
brew install ninja
- name: Install Sphinx
run: |
sudo pip3 install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme
sudo pip3 install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
- name: Setup NuGet Credentials
shell: bash
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
-source "https://nuget.pkg.github.com/BlueQuartzSoftware/index.json"
- name: Install Sphinx
run: |
pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme
pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
- name: Configure
run: |
cmake --preset ci-windows-${{matrix.toolset}} ${{github.workspace}} -T ${{matrix.toolset}}
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ option(COMPLEX_ENABLE_INSTALL "Enables COMPLEX install rules" ON)
file(TO_CMAKE_PATH "${CMAKE_COMMAND}" CMAKE_COMMAND_NORM)

project(complex
VERSION 1.2.0
VERSION 1.2.1
DESCRIPTION "SIMPL Redesign"
HOMEPAGE_URL "https://github.com/BlueQuartzSoftware/complex"
LANGUAGES CXX
Expand Down
4 changes: 4 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
"type": "BOOL",
"value": "ON"
},
"COMPLEX_ENABLE_PYTHON_TESTS": {
"type": "BOOL",
"value": "ON"
},
"COMPLEX_EMBED_PYTHON": {
"type": "BOOL",
"value": "OFF"
Expand Down
83 changes: 83 additions & 0 deletions cmake/Utility.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,86 @@ function(cmpBuildDateRevisionString)
endif()

endfunction()


#-------------------------------------------------------------------------------
# @Brief function AddPythonTest
# @ NAME
# @ FILE
# @ PYTHONPATH
#-------------------------------------------------------------------------------
function(AddPythonTest)
set(options )
set(oneValueArgs NAME FILE)
set(multiValueArgs PYTHONPATH)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if(COMPLEX_BUILD_PYTHON)
if(WIN32)
add_test(NAME ${ARGS_NAME}
COMMAND ${complex_SOURCE_DIR}/wrapping/python/testing/anaconda_test.bat
)

set_property(TEST ${ARGS_NAME}
PROPERTY
ENVIRONMENT
"PYTHON_TEST_FILE=${ARGS_FILE}"
)
else()
add_test(NAME ${ARGS_NAME}
COMMAND ${complex_SOURCE_DIR}/wrapping/python/testing/anaconda_test.sh
)

set_property(TEST ${ARGS_NAME}
PROPERTY
ENVIRONMENT
"PYTHON_TEST_FILE=${ARGS_FILE}"
)
endif()
else()
add_test(NAME ${ARGS_NAME}
COMMAND ${PYTHON_EXECUTABLE} ${ARGS_FILE}
)
endif()

if(WIN32)
string(REPLACE ";" "\\;" ARGS_PYTHONPATH "${ARGS_PYTHONPATH}")
else()
string(REPLACE ";" ":" ARGS_PYTHONPATH "${ARGS_PYTHONPATH}")
endif()

set_property(TEST ${ARGS_NAME}
APPEND
PROPERTY
ENVIRONMENT
"PYTHONPATH=${ARGS_PYTHONPATH}"
"${complex_PYTHON_TEST_ENV}"
)
endfunction()

#-------------------------------------------------------------------------------
# @Brief function CreatePythonTests
# @ PREFIX
# @ INPUT_DIR
# @ TEST_NAMES
#-------------------------------------------------------------------------------
function(CreatePythonTests)
set(options)
set(oneValueArgs PREFIX INPUT_DIR)
set(multiValueArgs TEST_NAMES)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

set(TESTS_PYTHONPATH
"$<TARGET_FILE_DIR:complex>"
)

foreach(test ${ARGS_TEST_NAMES})
string(REPLACE "/" "_" test_name ${test})
set(PY_TEST_NAME ${ARGS_PREFIX}_${test_name})

AddPythonTest(NAME ${PY_TEST_NAME}
FILE ${ARGS_INPUT_DIR}/${test}.py
PYTHONPATH ${TESTS_PYTHONPATH}
)
endforeach()
endfunction()
2 changes: 1 addition & 1 deletion conda/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% set name = "complex" %}
{% set version = "1.2.0" %}
{% set version = "1.2.1" %}

package:
name: {{ name|lower }}
Expand Down
2 changes: 1 addition & 1 deletion conda/recipe.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
context:
version: "1.2.0"
version: "1.2.1"
name: complex

package:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ IFilter::PreflightResult CreateDataArray::preflightImpl(const DataStructure& dat
if(!useDims)
{
return MakePreflightErrorResult(
-78602, "The DataArray to be created is not within an AttributeMatrix, so the dimensions cannot be determined implicitly. Check Set Tuple Dimensions to set the dimensions");
-78602, fmt::format("The DataArray to be created '{}'is not within an AttributeMatrix, so the dimensions cannot be determined implicitly. Check Set Tuple Dimensions to set the dimensions",
dataArrayPath.toString()));
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ IFilter::PreflightResult ReadTextDataArrayFilter::preflightImpl(const DataStruct
if(!useDims)
{
return MakePreflightErrorResult(
-77602, "The DataArray to be created is not within an AttributeMatrix, so the dimensions cannot be determined implicitly. Check Set Tuple Dimensions to set the dimensions");
-78602, fmt::format("The DataArray to be created '{}'is not within an AttributeMatrix, so the dimensions cannot be determined implicitly. Check Set Tuple Dimensions to set the dimensions",
arrayPath.toString()));
}
else
{
Expand Down
33 changes: 32 additions & 1 deletion src/Plugins/ComplexCore/wrapping/python/complexpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "ComplexCore/ComplexCoreFilterBinding.hpp"

#include <ComplexCore/ComplexCorePlugin.hpp>

#include <complex/DataStructure/AttributeMatrix.hpp>
#include <complex/DataStructure/DataArray.hpp>
#include <complex/DataStructure/DataGroup.hpp>
Expand Down Expand Up @@ -80,6 +79,7 @@
#include <complex/Pipeline/AbstractPipelineNode.hpp>
#include <complex/Pipeline/Pipeline.hpp>
#include <complex/Pipeline/PipelineFilter.hpp>
#include <complex/Utilities/DataGroupUtilities.hpp>

#include <fmt/ranges.h>

Expand Down Expand Up @@ -510,6 +510,37 @@ PYBIND11_MODULE(complex, mod)
dataStructure.def_property_readonly("size", &DataStructure::getSize);
dataStructure.def("__len__", &DataStructure::getSize);
dataStructure.def("remove", py::overload_cast<const DataPath&>(&DataStructure::removeData));
dataStructure.def("hierarchy_to_str", [](DataStructure& self) {
std::stringstream ss;
self.exportHierarchyAsText(ss);
return ss.str();
});
dataStructure.def("hierarchy_to_graphviz", [](DataStructure& self) {
std::stringstream ss;
self.exportHierarchyAsGraphViz(ss);
return ss.str();
});
dataStructure.def("get_children", [](DataStructure& self, complex::DataPath& parentPath) {
if(parentPath.empty())
{
std::vector<DataPath> outputPaths;
for(const auto* object : self.getTopLevelData())
{
auto topLevelPath = DataPath::FromString(object->getDataPaths()[0].getTargetName()).value();
outputPaths.push_back(topLevelPath);
}
return outputPaths;
}
else
{
auto result = complex::GetAllChildDataPaths(self, parentPath);
if(result)
{
return result.value();
}
return std::vector<DataPath>{};
}
});

py::class_<BaseGroup, DataObject, std::shared_ptr<BaseGroup>> baseGroup(mod, "BaseGroup");
baseGroup.def("contains", py::overload_cast<const std::string&>(&BaseGroup::contains, py::const_));
Expand Down
73 changes: 39 additions & 34 deletions src/complex/DataStructure/DataStructure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,72 +870,77 @@ void DataStructure::resetIds(DataObject::IdType startingId)
m_RootGroup.updateIds(updatedIds);
}

void DataStructure::exportHeirarchyAsGraphViz(std::ostream& outputStream) const
void DataStructure::exportHierarchyAsGraphViz(std::ostream& outputStream) const
{
// initialize dot file
outputStream << "digraph DataGraph {\n"
<< "\tlabelloc =\"t\"\n"
<< "\trankdir=LR;\n\n";

<< "\trankdir=LR;\n"
<< "\tlabel=\"DataStructure Hierarchy\"\n"
<< "\tlabelloc=\"t\"\n"
<< "\tfontcolor=\"#FFFFFA\"\n"
<< "\tfontsize=12\n"
<< "\tgraph [splines=true bgcolor=\"#242627\"]\n"
<< "\tnode [shape=record style=\"filled\" fillcolor=\"#1D7ECD\" fontsize=12 fontcolor=\"#FFFFFA\"]\n"
<< "\tedge [dir=front arrowtail=empty style=\"\" color=\"#FFFFFA\"]\n\n";
// set base case
for(const auto* object : getTopLevelData())
{
auto topLevelPath = DataPath::FromString(object->getDataPaths()[0].getTargetName()).value();
auto optionalDataPaths = GetAllChildDataPaths(*this, topLevelPath);
outputStream << "\n/* Top level DataObject: " << topLevelPath.getTargetName() << " */\n\"" << topLevelPath.getTargetName() << "\";\n";

if(!optionalDataPaths.has_value() || optionalDataPaths.value().size() == 0)
if(optionalDataPaths.has_value() && !optionalDataPaths.value().empty())
{
outputStream << "\"" << topLevelPath.getTargetName() << "\";\n\n";
// Begin recursion
recurseHierarchyToGraphViz(outputStream, optionalDataPaths.value(), topLevelPath.getTargetName());
}

// Begin recursion
recurseHeirarchyToGraphViz(outputStream, optionalDataPaths.value(), topLevelPath.getTargetName());
}

// close dot file
outputStream << "}" << std::endl; // for readability
}

void DataStructure::recurseHeirarchyToGraphViz(std::ostream& outputStream, const std::vector<DataPath> paths, const std::string& parent) const
void DataStructure::exportHierarchyAsText(std::ostream& outputStream) const
{
for(const auto& path : paths)
// set base case
for(const auto* object : getTopLevelData())
{
// Output parent node, child node, and edge connecting them in .dot format
outputStream << "\"" << parent << "\" -> \"" << path.getTargetName() << "\"\n";
auto topLevelPath = DataPath::FromString(object->getDataPaths()[0].getTargetName()).value();
outputStream << k_Delimiter << topLevelPath.getTargetName() << "\n";
auto optionalDataPaths = GetAllChildDataPaths(*this, topLevelPath);

// pull child paths or skip to next iteration
auto optionalChildPaths = GetAllChildDataPaths(*this, path);
if(!optionalChildPaths.has_value() || optionalChildPaths.value().size() == 0)
if(optionalDataPaths.has_value() && !optionalDataPaths.value().empty())
{
continue;
// Begin recursion
recurseHierarchyToText(outputStream, optionalDataPaths.value(), "");
}

// recurse
recurseHeirarchyToGraphViz(outputStream, optionalChildPaths.value(), path.getTargetName());
}
outputStream << "\n"; // for readability
outputStream << std::endl; // for readability
}

void DataStructure::exportHeirarchyAsText(std::ostream& outputStream) const
void DataStructure::recurseHierarchyToGraphViz(std::ostream& outputStream, const std::vector<DataPath> paths, const std::string& parent) const
{
// set base case
for(const auto* object : getTopLevelData())
for(const auto& path : paths)
{
auto topLevelPath = DataPath::FromString(object->getDataPaths()[0].getTargetName()).value();
auto optionalDataPaths = GetAllChildDataPaths(*this, topLevelPath);

outputStream << k_Delimiter << topLevelPath.getTargetName() << "\n";
auto* dataObjectPtr = getData(path);
// Output parent node, child node, and edge connecting them in .dot format
outputStream << "\"" << parent << "\" -> \"" << path.getTargetName() << "\"\n";

if(optionalDataPaths.has_value() || optionalDataPaths.value().size() != 0)
// pull child paths or skip to next iteration
auto optionalChildPaths = GetAllChildDataPaths(*this, path);
if(!optionalChildPaths.has_value() || optionalChildPaths.value().empty())
{
// Begin recursion
recurseHeirarchyToText(outputStream, optionalDataPaths.value(), "");
continue;
}

// recurse
recurseHierarchyToGraphViz(outputStream, optionalChildPaths.value(), path.getTargetName());
}
outputStream << std::endl; // for readability
// outputStream << "\n"; // for readability
}

void DataStructure::recurseHeirarchyToText(std::ostream& outputStream, const std::vector<DataPath> paths, std::string indent) const
void DataStructure::recurseHierarchyToText(std::ostream& outputStream, const std::vector<DataPath> paths, std::string indent) const
{
indent += " ";

Expand All @@ -946,13 +951,13 @@ void DataStructure::recurseHeirarchyToText(std::ostream& outputStream, const std

// pull child paths or skip to next iteration
auto optionalChildPaths = GetAllChildDataPaths(*this, path);
if(!optionalChildPaths.has_value() || optionalChildPaths.value().size() == 0)
if(!optionalChildPaths.has_value() || optionalChildPaths.value().empty())
{
continue;
}

// recurse
recurseHeirarchyToText(outputStream, optionalChildPaths.value(), indent);
recurseHierarchyToText(outputStream, optionalChildPaths.value(), indent);
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/complex/DataStructure/DataStructure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,14 +665,14 @@ class COMPLEX_EXPORT DataStructure
* @param outputStream the child class of ostream to output dot syntax to
* @return
*/
void exportHeirarchyAsGraphViz(std::ostream& outputStream) const;
void exportHierarchyAsGraphViz(std::ostream& outputStream) const;

/**
* @brief Outputs data graph in console readable format
* @param outputStream the child class of ostream to output to
* @return
*/
void exportHeirarchyAsText(std::ostream& outputStream) const;
void exportHierarchyAsText(std::ostream& outputStream) const;

/**
* @brief Copy assignment operator. The copied DataStructure's observers are not retained.
Expand Down Expand Up @@ -800,7 +800,7 @@ class COMPLEX_EXPORT DataStructure
* @param parent name of the calling parent to output
* @return
*/
void recurseHeirarchyToGraphViz(std::ostream& outputStream, const std::vector<DataPath> paths, const std::string& parent) const;
void recurseHierarchyToGraphViz(std::ostream& outputStream, const std::vector<DataPath> paths, const std::string& parent) const;

/**
* @brief The recursive function to parse graph and dump names to and output stream in
Expand All @@ -810,7 +810,7 @@ class COMPLEX_EXPORT DataStructure
* @param indent the indent for the heirarchy
* @return
*/
void recurseHeirarchyToText(std::ostream& outputStream, const std::vector<DataPath> paths, std::string indent) const;
void recurseHierarchyToText(std::ostream& outputStream, const std::vector<DataPath> paths, std::string indent) const;

/**
* @brief Notifies observers to the provided message.
Expand Down
Loading

0 comments on commit 81297ca

Please sign in to comment.