Skip to content

Commit 3307b02

Browse files
committed
ENH: Create unit tests for each python example pipeline (BlueQuartzSoftware#793)
Signed-off-by: Michael Jackson <[email protected]>
1 parent 9aa1373 commit 3307b02

25 files changed

+1036
-335
lines changed

.github/workflows/linux.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,23 @@ jobs:
3636
uses: actions/checkout@v3
3737
with:
3838
submodules: true
39+
- uses: actions/checkout@v4
40+
- name: Set up Python
41+
uses: actions/setup-python@v4
42+
with:
43+
python-version: '3.10'
44+
- name: Install Python dependencies
45+
run: |
46+
python -m pip install --upgrade pip
47+
pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
3948
- name: Add C++ Problem Matcher
4049
uses: ammaraskar/[email protected]
41-
- name: Install Dependencies
50+
- name: Install Dependencies - 2
4251
run: |
4352
sudo apt-get -y install ninja-build
4453
- name: Install Sphinx
4554
run: |
46-
sudo pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
55+
sudo pip3 install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
4756
- name: Setup NuGet Credentials
4857
shell: bash
4958
run: |

.github/workflows/macos.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,19 @@ jobs:
2424
- name: Checkout
2525
uses: actions/checkout@v3
2626
with:
27-
submodules: true
27+
submodules: true
28+
- uses: actions/checkout@v4
29+
- name: Set up Python
30+
uses: actions/setup-python@v4
31+
with:
32+
python-version: '3.10'
33+
- name: Install Python dependencies
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install sphinx myst-parser sphinx-markdown-tables sphinx_rtd_theme numpy
2837
- name: Add C++ Problem Matcher
2938
uses: ammaraskar/[email protected]
30-
- name: Install Dependencies
39+
- name: Install Dependencies - 2
3140
run: |
3241
brew install ninja
3342
- name: Install Sphinx

CMakePresets.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
"type": "BOOL",
3434
"value": "ON"
3535
},
36+
"SIMPLNX_ENABLE_PYTHON_TESTS": {
37+
"type": "BOOL",
38+
"value": "ON"
39+
},
3640
"SIMPLNX_EMBED_PYTHON": {
3741
"type": "BOOL",
3842
"value": "OFF"
@@ -121,6 +125,14 @@
121125
"VCPKG_HOST_TRIPLET": {
122126
"type": "STRING",
123127
"value": "x64-osx-v11"
128+
},
129+
"Python3_EXECUTABLE": {
130+
"type": "PATH",
131+
"value": "/Users/runner/hostedtoolcache/Python/3.10.13/x64/bin/python3.10"
132+
},
133+
"SIMPLNX_PY_DISABLE_HIDDEN_VISIBILITY": {
134+
"type": "BOOL",
135+
"value": "ON"
124136
}
125137
}
126138
},
@@ -138,6 +150,14 @@
138150
"VCPKG_HOST_TRIPLET": {
139151
"type": "STRING",
140152
"value": "arm64-osx-dynamic"
153+
},
154+
"Python3_EXECUTABLE": {
155+
"type": "PATH",
156+
"value": "/Users/runner/hostedtoolcache/Python/3.10.13/x64/bin/python3.10"
157+
},
158+
"SIMPLNX_PY_DISABLE_HIDDEN_VISIBILITY": {
159+
"type": "BOOL",
160+
"value": "ON"
141161
}
142162
}
143163
},
@@ -155,6 +175,10 @@
155175
"VCPKG_HOST_TRIPLET": {
156176
"type": "STRING",
157177
"value": "x64-linux-dynamic"
178+
},
179+
"Python3_EXECUTABLE": {
180+
"type": "PATH",
181+
"value": "/opt/hostedtoolcache/Python/3.10.13/x64/bin/python3.10"
158182
}
159183
}
160184
},
@@ -278,6 +302,10 @@
278302
"SIMPLNX_PY_DISABLE_HIDDEN_VISIBILITY": {
279303
"type": "BOOL",
280304
"value": "ON"
305+
},
306+
"Python3_EXECUTABLE": {
307+
"type": "PATH",
308+
"value": "/opt/local/anaconda3/envs/nx-build/bin/python"
281309
}
282310
}
283311
},

cmake/Utility.cmake

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,86 @@ function(cmpBuildDateRevisionString)
460460
endif()
461461

462462
endfunction()
463+
464+
465+
#-------------------------------------------------------------------------------
466+
# @Brief function AddPythonTest
467+
# @ NAME
468+
# @ FILE
469+
# @ PYTHONPATH
470+
#-------------------------------------------------------------------------------
471+
function(AddPythonTest)
472+
set(options )
473+
set(oneValueArgs NAME FILE)
474+
set(multiValueArgs PYTHONPATH)
475+
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
476+
message(STATUS "ARGS_FILE:${ARGS_FILE}")
477+
if(COMPLEX_BUILD_PYTHON)
478+
if(WIN32)
479+
add_test(NAME ${ARGS_NAME}
480+
COMMAND ${complex_SOURCE_DIR}/wrapping/python/testing/anaconda_test.bat
481+
)
482+
483+
set_property(TEST ${ARGS_NAME}
484+
PROPERTY
485+
ENVIRONMENT
486+
"PYTHON_TEST_FILE=${ARGS_FILE}"
487+
"Python3_EXECUTABLE=${Python3_EXECUTABLE}"
488+
)
489+
else()
490+
add_test(NAME ${ARGS_NAME}
491+
COMMAND ${complex_SOURCE_DIR}/wrapping/python/testing/anaconda_test.sh
492+
)
493+
set_property(TEST ${ARGS_NAME}
494+
PROPERTY
495+
ENVIRONMENT
496+
"PYTHON_TEST_FILE=${ARGS_FILE}"
497+
"Python3_EXECUTABLE=${Python3_EXECUTABLE}"
498+
)
499+
endif()
500+
else()
501+
add_test(NAME ${ARGS_NAME}
502+
COMMAND ${Python3_EXECUTABLE} ${ARGS_FILE}
503+
)
504+
endif()
505+
506+
if(WIN32)
507+
string(REPLACE ";" "\\;" ARGS_PYTHONPATH "${ARGS_PYTHONPATH}")
508+
else()
509+
string(REPLACE ";" ":" ARGS_PYTHONPATH "${ARGS_PYTHONPATH}")
510+
endif()
511+
512+
set_property(TEST ${ARGS_NAME}
513+
APPEND
514+
PROPERTY
515+
ENVIRONMENT
516+
"PYTHONPATH=${ARGS_PYTHONPATH}"
517+
)
518+
endfunction()
519+
520+
#-------------------------------------------------------------------------------
521+
# @Brief function CreatePythonTests
522+
# @ PREFIX
523+
# @ INPUT_DIR
524+
# @ TEST_NAMES
525+
#-------------------------------------------------------------------------------
526+
function(CreatePythonTests)
527+
set(options)
528+
set(oneValueArgs PREFIX INPUT_DIR)
529+
set(multiValueArgs TEST_NAMES)
530+
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
531+
532+
set(TESTS_PYTHONPATH
533+
"$<TARGET_FILE_DIR:complex>"
534+
)
535+
536+
foreach(test ${ARGS_TEST_NAMES})
537+
string(REPLACE "/" "_" test_name ${test})
538+
set(PY_TEST_NAME ${ARGS_PREFIX}_${test_name})
539+
540+
AddPythonTest(NAME ${PY_TEST_NAME}
541+
FILE ${ARGS_INPUT_DIR}/${test}.py
542+
PYTHONPATH ${TESTS_PYTHONPATH}
543+
)
544+
endforeach()
545+
endfunction()

src/Plugins/SimplnxCore/wrapping/python/simplnxpy.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,11 @@ PYBIND11_MODULE(simplnx, mod)
544544
std::vector<DataPath> outputPaths;
545545
for(const auto* object : self.getTopLevelData())
546546
{
547-
auto topLevelPath = DataPath::FromString(object->getDataPaths()[0].getTargetName()).value();
548-
outputPaths.push_back(topLevelPath);
547+
if(object != nullptr)
548+
{
549+
auto topLevelPath = DataPath::FromString(object->getDataPaths()[0].getTargetName()).value();
550+
outputPaths.push_back(topLevelPath);
551+
}
549552
}
550553
return outputPaths;
551554
}

src/simplnx/Utilities/DataGroupUtilities.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ std::optional<std::vector<DataPath>> GetAllChildDataPaths(const DataStructure& d
186186
{
187187
bool ignore = false;
188188
DataPath childPath = parentGroup.createChildPath(childName);
189-
const DataObject* dataObject = dataStructure.getData(childPath);
190189
for(const auto& ignoredPath : ignoredDataPaths)
191190
{
192191
if(childPath == ignoredPath)
@@ -195,7 +194,8 @@ std::optional<std::vector<DataPath>> GetAllChildDataPaths(const DataStructure& d
195194
break;
196195
}
197196
}
198-
if(!ignore && (dataObjectType == DataObject::Type::DataObject || dataObject->getDataObjectType() == dataObjectType))
197+
const DataObject* dataObject = dataStructure.getData(childPath);
198+
if(dataObject != nullptr && !ignore && (dataObjectType == DataObject::Type::DataObject || dataObject->getDataObjectType() == dataObjectType))
199199
{
200200
childDataObjects.push_back(childPath);
201201
}
@@ -211,7 +211,7 @@ std::optional<std::vector<DataPath>> GetAllChildDataPaths(const DataStructure& d
211211
{
212212
std::vector<DataPath> childDataObjects;
213213
const DataObject* dataObject1 = dataStructure.getData(parent);
214-
if(dataObject1->getDataObjectType() == DataObject::Type::DataArray || dataObject1->getDataObjectType() == DataObject::Type::DynamicListArray ||
214+
if(dataObject1 == nullptr || dataObject1->getDataObjectType() == DataObject::Type::DataArray || dataObject1->getDataObjectType() == DataObject::Type::DynamicListArray ||
215215
dataObject1->getDataObjectType() == DataObject::Type::NeighborList || dataObject1->getDataObjectType() == DataObject::Type::ScalarData ||
216216
dataObject1->getDataObjectType() == DataObject::Type::StringArray)
217217
{

wrapping/python/CMakeLists.txt

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ function(simplnx_create_pyi)
109109
set(PYI_TARGET_NAME ${ARGS_PYTHON_MODULE_NAME}CreateStubFile)
110110

111111
add_custom_target(${PYI_TARGET_NAME} ALL
112-
COMMAND ${MYPY_STUBGEN_EXE} -m ${ARGS_PYTHON_MODULE_NAME} -o $<TARGET_FILE_DIR:simplnx>
113-
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/fix_pyi_file.py --input_file "$<TARGET_FILE_DIR:simplnx>/${ARGS_PYTHON_MODULE_NAME}.pyi"
112+
COMMAND "${MYPY_STUBGEN_EXE}" -m "${ARGS_PYTHON_MODULE_NAME}" -o "$<TARGET_FILE_DIR:simplnx>"
113+
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/fix_pyi_file.py" --input_file "$<TARGET_FILE_DIR:simplnx>/${ARGS_PYTHON_MODULE_NAME}.pyi"
114114
COMMENT "${ARGS_PYTHON_MODULE_NAME}: Generating .pyi files"
115115
WORKING_DIRECTORY $<TARGET_FILE_DIR:simplnx>
116116
)
@@ -278,3 +278,46 @@ option(SIMPLNX_ENABLE_SPHINX_DOCS "Enables the Sphinx based documentation genera
278278
if(SIMPLNX_ENABLE_SPHINX_DOCS)
279279
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/docs ${simplnx_BINARY_DIR}/sphinx_docs)
280280
endif()
281+
282+
#------------------------------------------------------------------------------
283+
# Create the Python Unit tests
284+
#------------------------------------------------------------------------------
285+
option(COMPLEX_ENABLE_PYTHON_TESTS "Enables python based unit tests" OFF)
286+
if(COMPLEX_ENABLE_PYTHON_TESTS)
287+
288+
file(READ ${complex_SOURCE_DIR}/wrapping/python/complex_test_dirs.in.py COMPLEX_TEST_DIRS_FILE)
289+
290+
string(REPLACE "\${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" $<TARGET_FILE_DIR:complex> COMPLEX_TEST_DIRS_FILE ${COMPLEX_TEST_DIRS_FILE})
291+
string(REPLACE "\${complex_BINARY_DIR}" ${complex_BINARY_DIR} COMPLEX_TEST_DIRS_FILE ${COMPLEX_TEST_DIRS_FILE})
292+
string(REPLACE "\${DREAM3D_DATA_DIR}" ${DREAM3D_DATA_DIR} COMPLEX_TEST_DIRS_FILE ${COMPLEX_TEST_DIRS_FILE})
293+
string(REPLACE "\${complex_SOURCE_DIR}" ${complex_SOURCE_DIR} COMPLEX_TEST_DIRS_FILE ${COMPLEX_TEST_DIRS_FILE})
294+
295+
file(GENERATE
296+
OUTPUT "$<TARGET_FILE_DIR:complex>/complex_test_dirs.py"
297+
CONTENT ${COMPLEX_TEST_DIRS_FILE}
298+
)
299+
300+
set(PYTHON_TEST_INPUT_DIR "${complex_SOURCE_DIR}/wrapping/python/examples")
301+
302+
set(COMPLEX_PYTHON_TESTS
303+
"angle_conversion"
304+
"basic_arrays"
305+
"basic_ebsd_ipf"
306+
"basic_numpy"
307+
"create_ensemble_info"
308+
"generated_file_list"
309+
"geometry_examples"
310+
"import_d3d" # Dependent on 'basic_ebsd_ipf' running first
311+
"import_hdf5" # Dependent on 'basic_ebsd_ipf' running first
312+
"output_file"
313+
"pipeline"
314+
"read_csv_file"
315+
# "read_esprit_data"
316+
)
317+
318+
CreatePythonTests(PREFIX "PY_COMPLEX"
319+
INPUT_DIR ${PYTHON_TEST_INPUT_DIR}
320+
TEST_NAMES ${COMPLEX_PYTHON_TESTS}
321+
)
322+
323+
endif()

wrapping/python/CxPybind/CxPybind/CxPybind.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,18 @@ auto BindFilter(py::handle scope, const Internals& internals)
397397

398398
std::string executeSig = MakePythonSignature<FilterT>("execute", internals);
399399
std::string executeDocString = fmt::format("{}\n\nExecutes the filter\n", executeSig);
400-
400+
filter.def_static("human_name", [&internals]() {
401+
FilterT filter;
402+
return filter.humanName();
403+
});
404+
filter.def_static("name", [&internals]() {
405+
FilterT filter;
406+
return filter.name();
407+
});
408+
filter.def_static("uuid", [&internals]() {
409+
FilterT filter;
410+
return filter.uuid();
411+
});
401412
filter.def_static(
402413
"execute",
403414
[&internals](DataStructure& dataStructure, const py::kwargs& kwargs) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
''' module to get build specific directories
2+
This file is generated during the cmake configure phase. This file does *NOT*
3+
however use the standard `cmake_configure_file()` command but instead uses
4+
a custom generation cmake code. If you add new `${}` style variables, you will
5+
need to update those custom CMake codes.
6+
7+
'''
8+
9+
10+
import complex as cx
11+
12+
def check_filter_result(filter: cx.IFilter, result: cx.IFilter.ExecuteResult):
13+
if len(result.warnings) != 0:
14+
print(f'{filter.name()} :: Warnings: {result.warnings}')
15+
16+
has_errors = len(result.errors) != 0
17+
if has_errors:
18+
print(f'{filter.name()} :: Errors: {result.errors}')
19+
raise RuntimeError(result)
20+
21+
print(f"{filter.name()} :: No errors running the filter")
22+
23+
24+
def GetBuildDirectory():
25+
return '${CMAKE_LIBRARY_OUTPUT_DIRECTORY}'
26+
27+
def GetTestDirectory():
28+
return '${complex_BINARY_DIR}/Testing'
29+
30+
def GetTestTempDirectory():
31+
return '${complex_BINARY_DIR}/Testing/Temporary'
32+
33+
def GetDataDirectory():
34+
return '${DREAM3D_DATA_DIR}'
35+
36+
def GetComplexPythonSourceDir():
37+
return '${complex_SOURCE_DIR}/wrapping/python'
38+
39+
def GetComplexSourceDir():
40+
return '${complex_SOURCE_DIR}'
41+
42+
def print_all_paths():
43+
print(f'#### Important Filesystem Paths ####')
44+
print(f' GetBuildDirectory: {GetBuildDirectory()}')
45+
print(f' GetTestDirectory: {GetTestDirectory()}')
46+
print(f' GetTestTempDirectory: {GetTestTempDirectory()}')
47+
print(f' GetDataDirectory: {GetDataDirectory()}')
48+
print(f' GetComplexPythonSourceDir: {GetComplexPythonSourceDir()}')
49+
print(f' GetComplexSourceDir: {GetComplexSourceDir()}')
50+
print('#######################################')

0 commit comments

Comments
 (0)