From 76d75eaf1d963a76491c3434465e2eaa298fa07f Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Thu, 29 Oct 2026 16:22:21 -0400 Subject: [PATCH] BUG: Restrict file extensions of two file reader filters DREAM3D-NX offers a filter for every dropped file or folder when the input file parameter of the filter accepts all extensions. These two filters set that option, so they showed for each drop. * RegularizeZSpacingFilter accepts only .txt, .dat and .csv files. * ReadOnScaleTableFileFilter accepts only .flxtbl files. * Add tests for the supported extensions and for the -3003 preflight error that an unsupported extension causes. * Update the filter documentation and the Regularize Z Spacing V&V notes. Converted legacy pipelines that use a different extension now fail preflight and must use a renamed file. Signed-off-by: Michael Jackson --- .../docs/ReadOnScaleTableFileFilter.md | 2 +- .../docs/RegularizeZSpacingFilter.md | 2 +- .../Filters/ReadOnScaleTableFileFilter.cpp | 4 +- .../Filters/RegularizeZSpacingFilter.cpp | 7 ++-- .../test/ReadOnScaleTableFileTest.cpp | 24 +++++++---- .../test/RegularizeZSpacingTest.cpp | 42 +++++++++++++++++++ .../vv/RegularizeZSpacingFilter.md | 2 +- 7 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/Plugins/SimplnxCore/docs/ReadOnScaleTableFileFilter.md b/src/Plugins/SimplnxCore/docs/ReadOnScaleTableFileFilter.md index 1461343b68..43c71b05b5 100644 --- a/src/Plugins/SimplnxCore/docs/ReadOnScaleTableFileFilter.md +++ b/src/Plugins/SimplnxCore/docs/ReadOnScaleTableFileFilter.md @@ -6,7 +6,7 @@ IO (Input) ## Description -This filter reads an OnScale/PZFLEX table file and creates a **Rectilinear Grid Geometry**. The suggested file extension is `.flxtbl`, but the filter accepts any file extension. OnScale table files describe grid coordinates, material names, and the material index for each grid **Cell**. +This filter reads an OnScale/PZFLEX table file and creates a **Rectilinear Grid Geometry**. The file must have the `.flxtbl` extension. OnScale table files describe grid coordinates, material names, and the material index for each grid **Cell**. The reader processes these sections: diff --git a/src/Plugins/SimplnxCore/docs/RegularizeZSpacingFilter.md b/src/Plugins/SimplnxCore/docs/RegularizeZSpacingFilter.md index 4596e1deab..1283e0d719 100644 --- a/src/Plugins/SimplnxCore/docs/RegularizeZSpacingFilter.md +++ b/src/Plugins/SimplnxCore/docs/RegularizeZSpacingFilter.md @@ -27,7 +27,7 @@ The input file is a simple text file with one floating point value per line (or 9.5 12.0 -The values must be monotonically non-decreasing, and the final value (the total Z extent) must be greater than zero. The `.txt` extension is a file-dialog hint only; any readable text file is accepted. +The values must be monotonically non-decreasing, and the final value (the total Z extent) must be greater than zero. The file must have a `.txt`, `.dat`, or `.csv` extension. ### Output Geometry diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadOnScaleTableFileFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadOnScaleTableFileFilter.cpp index 380ac8bf7e..65e3096d70 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadOnScaleTableFileFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadOnScaleTableFileFilter.cpp @@ -79,8 +79,8 @@ Parameters ReadOnScaleTableFileFilter::parameters() const Parameters params; params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); - params.insert(std::make_unique(k_InputFile_Key, "Input File", "The OnScale table file to import. Any file extension is accepted.", fs::path(""), - FileSystemPathParameter::ExtensionsType{".flxtbl"}, FileSystemPathParameter::PathType::InputFile, true)); + params.insert(std::make_unique(k_InputFile_Key, "Input File", "The OnScale table file to import. The file must have the .flxtbl extension.", fs::path(""), + FileSystemPathParameter::ExtensionsType{".flxtbl"}, FileSystemPathParameter::PathType::InputFile)); params.insert(std::make_unique(k_Origin_Key, "Fallback Origin", "The origin for each axis that is absent from the file.", VectorFloat32Parameter::ValueType{0.0F, 0.0F, 0.0F}, VectorFloat32Parameter::NamesType{"X", "Y", "Z"})); params.insert(std::make_unique(k_Spacing_Key, "Fallback Spacing", "The spacing for each axis that is absent from the file.", diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/RegularizeZSpacingFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/RegularizeZSpacingFilter.cpp index 52b41792cb..94b897c38d 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/RegularizeZSpacingFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/RegularizeZSpacingFilter.cpp @@ -79,12 +79,11 @@ Parameters RegularizeZSpacingFilter::parameters() const Parameters params; params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); - // acceptAllExtensions = true: ".txt" is only a file-dialog hint. The legacy filter treated its - // "*.txt" string as a GUI filter and executed on any readable file, so converted pipelines may - // reference .dat/.csv/extensionless files and must not fail extension validation here. + // These extensions are the file types the Z-positions reader accepts. Keep acceptAllExtensions off + // so DREAM3D-NX only offers this filter for those extensions on file drop. params.insert(std::make_unique(k_InputFile_Key, "Current Z Positions File", "Path to a whitespace-delimited text file that contains the current Z position of each slice boundary (ZPoints + 1 float values).", - fs::path(""), FileSystemPathParameter::ExtensionsType{".txt"}, FileSystemPathParameter::PathType::InputFile, true)); + fs::path(""), FileSystemPathParameter::ExtensionsType{".txt", ".dat", ".csv"}, FileSystemPathParameter::PathType::InputFile)); params.insert(std::make_unique(k_NewZRes_Key, "New Z Spacing", "The new, regular spacing to use along the Z axis. Must be greater than 0.", 1.0F)); params.insertLinkableParameter( diff --git a/src/Plugins/SimplnxCore/test/ReadOnScaleTableFileTest.cpp b/src/Plugins/SimplnxCore/test/ReadOnScaleTableFileTest.cpp index 0803004c81..6d1ae38182 100644 --- a/src/Plugins/SimplnxCore/test/ReadOnScaleTableFileTest.cpp +++ b/src/Plugins/SimplnxCore/test/ReadOnScaleTableFileTest.cpp @@ -459,12 +459,12 @@ TEST_CASE("SimplnxCore::ReadOnScaleTableFileFilter: Invalid Preflight Input", "[ TEST_CASE("SimplnxCore::ReadOnScaleTableFileFilter: Additional Valid Inputs", "[SimplnxCore][ReadOnScaleTableFileFilter]") { UnitTest::LoadPlugins(); - const std::string testName = GENERATE("CRLF Line Endings", "Empty Names", "Nonstandard Extension"); + const std::string testName = GENERATE("CRLF Line Endings", "Empty Names"); CAPTURE(testName); FixtureOptions options; options.BoundsCounts = {4, 3, 2}; - fs::path inputFile = fs::path(unit_test::k_BinaryTestOutputDir.view()) / "ReadOnScaleTableFile" / fmt::format("{}.flxtbl", testName); + const fs::path inputFile = fs::path(unit_test::k_BinaryTestOutputDir.view()) / "ReadOnScaleTableFile" / fmt::format("{}.flxtbl", testName); if(testName == "CRLF Line Endings") { options.LineEnding = "\r\n"; @@ -473,15 +473,25 @@ TEST_CASE("SimplnxCore::ReadOnScaleTableFileFilter: Additional Valid Inputs", "[ { options.Names.clear(); } - else - { - inputFile.replace_extension(".table"); - } - const GeneratedFixture fixture = WriteFixture(inputFile, options); RequireValidImport(inputFile, fixture, {3, 2, 1}); } +TEST_CASE("SimplnxCore::ReadOnScaleTableFileFilter: Unsupported Extension", "[SimplnxCore][ReadOnScaleTableFileFilter]") +{ + UnitTest::LoadPlugins(); + FixtureOptions options; + options.BoundsCounts = {4, 3, 2}; + const fs::path inputFile = fs::path(unit_test::k_BinaryTestOutputDir.view()) / "ReadOnScaleTableFile" / "UnsupportedExtension.table"; + WriteFixture(inputFile, options); + + DataStructure dataStructure; + const ReadOnScaleTableFileFilter filter; + auto preflightResult = filter.preflight(dataStructure, CreateArguments(inputFile)); + SIMPLNX_RESULT_REQUIRE_INVALID(preflightResult.outputActions); + REQUIRE(preflightResult.outputActions.errors().front().code == -3003); +} + TEST_CASE("SimplnxCore::ReadOnScaleTableFileFilter: Malformed File", "[SimplnxCore][ReadOnScaleTableFileFilter]") { UnitTest::LoadPlugins(); diff --git a/src/Plugins/SimplnxCore/test/RegularizeZSpacingTest.cpp b/src/Plugins/SimplnxCore/test/RegularizeZSpacingTest.cpp index 1ba7e65db4..77bb9263e3 100644 --- a/src/Plugins/SimplnxCore/test/RegularizeZSpacingTest.cpp +++ b/src/Plugins/SimplnxCore/test/RegularizeZSpacingTest.cpp @@ -163,6 +163,48 @@ TEST_CASE("SimplnxCore::RegularizeZSpacingFilter: Valid Execution (New Geometry) REQUIRE_NOTHROW(dataStructure.getDataRefAs(k_InputGeometryPath)); } +TEST_CASE("SimplnxCore::RegularizeZSpacingFilter: Supported Extensions", "[SimplnxCore][RegularizeZSpacingFilter]") +{ + UnitTest::LoadPlugins(); + const std::string extension = GENERATE(".dat", ".csv"); + CAPTURE(extension); + DataStructure dataStructure = createTestDataStructure(); + const fs::path zBoundsFile = writeZBoundsFile("RegularizeZSpacing_supported" + extension, {0.0F, 1.0F, 3.0F, 6.0F, 10.0F}); + + RegularizeZSpacingFilter filter; + Arguments args; + args.insertOrAssign(RegularizeZSpacingFilter::k_SelectedImageGeometryPath_Key, std::make_any(k_InputGeometryPath)); + args.insertOrAssign(RegularizeZSpacingFilter::k_InputFile_Key, std::make_any(zBoundsFile)); + args.insertOrAssign(RegularizeZSpacingFilter::k_NewZRes_Key, std::make_any(2.0F)); + args.insertOrAssign(RegularizeZSpacingFilter::k_RemoveOriginalGeometry_Key, std::make_any(false)); + args.insertOrAssign(RegularizeZSpacingFilter::k_CreatedImageGeometry_Key, std::make_any(k_OutputGeometryPath)); + + auto preflightResult = filter.preflight(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions); + auto executeResult = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result); + UnitTest::CheckArraysInheritTupleDims(dataStructure); +} + +TEST_CASE("SimplnxCore::RegularizeZSpacingFilter: Unsupported Extension", "[SimplnxCore][RegularizeZSpacingFilter]") +{ + UnitTest::LoadPlugins(); + DataStructure dataStructure = createTestDataStructure(); + const fs::path zBoundsFile = writeZBoundsFile("RegularizeZSpacing_unsupported.zpos", {0.0F, 1.0F, 3.0F, 6.0F, 10.0F}); + + RegularizeZSpacingFilter filter; + Arguments args; + args.insertOrAssign(RegularizeZSpacingFilter::k_SelectedImageGeometryPath_Key, std::make_any(k_InputGeometryPath)); + args.insertOrAssign(RegularizeZSpacingFilter::k_InputFile_Key, std::make_any(zBoundsFile)); + args.insertOrAssign(RegularizeZSpacingFilter::k_NewZRes_Key, std::make_any(2.0F)); + args.insertOrAssign(RegularizeZSpacingFilter::k_RemoveOriginalGeometry_Key, std::make_any(false)); + args.insertOrAssign(RegularizeZSpacingFilter::k_CreatedImageGeometry_Key, std::make_any(k_OutputGeometryPath)); + + auto preflightResult = filter.preflight(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(preflightResult.outputActions); + REQUIRE(preflightResult.outputActions.errors().front().code == -3003); +} + TEST_CASE("SimplnxCore::RegularizeZSpacingFilter: Valid Execution (Spacing Exceeds Extent)", "[SimplnxCore][RegularizeZSpacingFilter]") { DataStructure dataStructure = createTestDataStructure(); diff --git a/src/Plugins/SimplnxCore/vv/RegularizeZSpacingFilter.md b/src/Plugins/SimplnxCore/vv/RegularizeZSpacingFilter.md index 7358371069..bf5918df38 100644 --- a/src/Plugins/SimplnxCore/vv/RegularizeZSpacingFilter.md +++ b/src/Plugins/SimplnxCore/vv/RegularizeZSpacingFilter.md @@ -36,7 +36,7 @@ 1. **Data copy**: legacy per-tuple `memcpy` via a rebuilt `newindicies` table → SIMPLNX bulk `AbstractDataStore::copyFrom` of one contiguous plane-slab per destination plane. Same bytes moved; more efficient and out-of-core friendly. 2. **Output mode**: legacy replaces the cell AttributeMatrix in place → SIMPLNX uses the modern new-geometry pattern (`CreateImageGeometryAction` + rename + deferred delete) with a "Perform In Place" toggle (default true). In-place mode is behaviorally equivalent to legacy. -3. **Preflight validation added**: positive `new_z_spacing`; input file has ≥ `ZPoints + 1` parseable values; values monotonically non-decreasing; positive total Z extent; only DataArray members in the cell AM. These reject bad input that legacy silently consumed (short files reused the last-read value; non-monotonic files produced a garbage mapping; zero extent produced a clamped 1-plane output) but do not alter valid-input output. The `.txt` extension is a dialog hint only (`acceptAllExtensions`), so converted legacy pipelines referencing `.dat`/`.csv`/extensionless files still validate. +3. **Preflight validation added**: positive `new_z_spacing`; input file has ≥ `ZPoints + 1` parseable values; values monotonically non-decreasing; positive total Z extent; only DataArray members in the cell AM. These reject bad input that legacy silently consumed (short files reused the last-read value; non-monotonic files produced a garbage mapping; zero extent produced a clamped 1-plane output) but do not alter valid-input output. The input file must have a `.txt`, `.dat`, or `.csv` extension. Converted legacy pipelines that reference other extensions now fail preflight with error `-3003`; files without an extension fail with error `-3002`. Rename these files and update the paths in the pipelines. 4. **Parallelization**: per-array parallel task runner (one task per cell array, distinct stores) vs legacy serial loop. No output effect. 5. **Cell-AM binding**: legacy took an explicit AttributeMatrix path; SIMPLNX takes the geometry and uses its assigned cell AttributeMatrix. `FromSIMPLJson` keeps only the DataContainer name from the legacy `CellAttributeMatrixPath` (the AM name is discarded). For the overwhelmingly common single-cell-AM case this is identity; corner-case consequences for converted pipelines are documented in the deviations file's scope note. Related accept/reject divergences: a geometry with no cell AM now errors (`-5560`, impossible to express in legacy), and a stale AM name that legacy rejected converts to the geometry's actual cell AM and proceeds.