diff --git a/Modules/Core/Common/test/CMakeLists.txt b/Modules/Core/Common/test/CMakeLists.txt index 6a587ceca93..d9901e8251a 100644 --- a/Modules/Core/Common/test/CMakeLists.txt +++ b/Modules/Core/Common/test/CMakeLists.txt @@ -1427,6 +1427,7 @@ set( itkByteSwapGTest.cxx itkBridgeCholeskySolveGTest.cxx itkCommandObserverObjectGTest.cxx + itkCommonExceptionsGTest.cxx itkCommonTypeTraitsGTest.cxx itkCompensatedSummationGTest.cxx itkConicShellInteriorExteriorSpatialFunctionGTest.cxx diff --git a/Modules/Core/Common/test/itkCommonExceptionsGTest.cxx b/Modules/Core/Common/test/itkCommonExceptionsGTest.cxx new file mode 100644 index 00000000000..3b29b539d38 --- /dev/null +++ b/Modules/Core/Common/test/itkCommonExceptionsGTest.cxx @@ -0,0 +1,166 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +// First include the header files to be tested: +#include "itkDataObject.h" +#include "itkDeref.h" +#include "itkExceptionObject.h" + +#include "itkImage.h" +#include +#include +#include +#include +#include + + +namespace +{ +template +std::string +PrintToString(const TException & exception) +{ + std::ostringstream stream; + stream << exception; + return stream.str(); +} + + +bool +Contains(const std::string & text, const std::string & part) +{ + return text.find(part) != std::string::npos; +} + + +// Checks an exception type that inherits the constructors of ExceptionObject. +template +void +CheckExceptionWithInheritedConstructors(const std::string & expectedName) +{ + static_assert(std::is_base_of_v); + static_assert(std::is_nothrow_default_constructible_v); + static_assert(std::is_nothrow_copy_constructible_v); + static_assert(std::is_nothrow_copy_assignable_v); + + const TException defaultConstructed{}; + EXPECT_EQ(defaultConstructed.GetNameOfClass(), expectedName); + EXPECT_STREQ(defaultConstructed.GetDescription(), ""); + EXPECT_TRUE(Contains(PrintToString(defaultConstructed), "itk::" + expectedName)); + + const std::string testDescription = "test description"; + const std::string testLocation = "test location"; + const TException exception(__FILE__, __LINE__, testDescription, testLocation); + EXPECT_EQ(exception.GetNameOfClass(), expectedName); + EXPECT_EQ(exception.GetDescription(), testDescription); + EXPECT_EQ(exception.GetLocation(), testLocation); + EXPECT_TRUE(Contains(exception.what(), testDescription)); + + const std::string printed = PrintToString(exception); + EXPECT_TRUE(Contains(printed, "itk::" + expectedName)); + EXPECT_TRUE(Contains(printed, "Description: " + testDescription)); + + const TException copy(exception); + EXPECT_EQ(copy, exception); + + TException assigned; + assigned = exception; + EXPECT_EQ(assigned, exception); + + const itk::ExceptionObject & base = exception; + EXPECT_EQ(base.GetNameOfClass(), expectedName); +} + + +// Checks a DataObjectError type, both without and with a data object attached. +template +void +CheckDataObjectError(const std::string & expectedName) +{ + static_assert(std::is_base_of_v); + + TException defaultConstructed; + EXPECT_EQ(defaultConstructed.GetNameOfClass(), expectedName); + EXPECT_EQ(defaultConstructed.GetDataObject(), nullptr); + EXPECT_EQ(std::as_const(defaultConstructed).GetDataObject(), nullptr); + + const std::string printedWithoutDataObject = PrintToString(defaultConstructed); + EXPECT_TRUE(Contains(printedWithoutDataObject, "itk::" + expectedName)); + EXPECT_TRUE(Contains(printedWithoutDataObject, "Data object: (None)")); + + const auto image = itk::Image::New(); + TException exception(__FILE__, __LINE__); + exception.SetDataObject(image); + EXPECT_EQ(exception.GetDataObject(), image.get()); + EXPECT_EQ(std::as_const(exception).GetDataObject(), image.get()); + + const std::string printedWithDataObject = PrintToString(exception); + EXPECT_TRUE(Contains(printedWithDataObject, "itk::" + expectedName)); + EXPECT_TRUE(Contains(printedWithDataObject, "Data object:")); + EXPECT_FALSE(Contains(printedWithDataObject, "(None)")); + EXPECT_TRUE(Contains(printedWithDataObject, "LargestPossibleRegion")); + + const TException copy(exception); + EXPECT_EQ(copy.GetDataObject(), image.get()); + + TException assigned; + assigned = exception; + EXPECT_EQ(assigned.GetDataObject(), image.get()); + + const itk::DataObjectError & base = exception; + EXPECT_EQ(base.GetNameOfClass(), expectedName); + EXPECT_EQ(base.GetDataObject(), image.get()); +} +} // namespace + + +TEST(CommonExceptions, InheritedConstructors) +{ + CheckExceptionWithInheritedConstructors("MemoryAllocationError"); + CheckExceptionWithInheritedConstructors("RangeError"); + CheckExceptionWithInheritedConstructors("InvalidArgumentError"); + CheckExceptionWithInheritedConstructors("IncompatibleOperandsError"); + CheckExceptionWithInheritedConstructors("DerefError"); +} + + +TEST(CommonExceptions, ProcessAborted) +{ + constexpr const char * expectedDescription = "Filter execution was aborted by an external request"; + + const itk::ProcessAborted defaultConstructed; + EXPECT_STREQ(defaultConstructed.GetNameOfClass(), "ProcessAborted"); + EXPECT_STREQ(defaultConstructed.GetDescription(), expectedDescription); + + const unsigned int line = __LINE__; + const itk::ProcessAborted exception(__FILE__, line); + EXPECT_STREQ(exception.GetDescription(), expectedDescription); + EXPECT_STREQ(exception.GetFile(), __FILE__); + EXPECT_EQ(exception.GetLine(), line); + + const std::string printed = PrintToString(exception); + EXPECT_TRUE(Contains(printed, "itk::ProcessAborted")); + EXPECT_TRUE(Contains(printed, expectedDescription)); +} + + +TEST(CommonExceptions, DataObjectErrors) +{ + CheckDataObjectError("DataObjectError"); + CheckDataObjectError("InvalidRequestedRegionError"); +} diff --git a/Modules/IO/ImageBase/test/CMakeLists.txt b/Modules/IO/ImageBase/test/CMakeLists.txt index 239ef17763a..b1de3d9e080 100644 --- a/Modules/IO/ImageBase/test/CMakeLists.txt +++ b/Modules/IO/ImageBase/test/CMakeLists.txt @@ -1047,6 +1047,7 @@ set( itkImageFileReaderGTest1.cxx itkImageIOBaseGTest.cxx itkImageIOFileNameExtensionsGTests.cxx + itkImageIOExceptionsGTest.cxx itkImageSeriesReaderOriginOverrideGTest.cxx itkNumericSeriesFileNamesGTest.cxx itkWriteImageFunctionGTest.cxx diff --git a/Modules/IO/ImageBase/test/itkImageIOExceptionsGTest.cxx b/Modules/IO/ImageBase/test/itkImageIOExceptionsGTest.cxx new file mode 100644 index 00000000000..1a391f67546 --- /dev/null +++ b/Modules/IO/ImageBase/test/itkImageIOExceptionsGTest.cxx @@ -0,0 +1,104 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +// First include the header files to be tested: +#include "itkImageFileReaderException.h" +#include "itkImageFileWriter.h" +#include "itkImageSeriesWriter.h" + +#include +#include +#include +#include +#include + + +namespace +{ +const std::string testMessage = "test message"; +const std::string testLocation = "test location"; + + +bool +Contains(const std::string & text, const std::string & part) +{ + return text.find(part) != std::string::npos; +} + + +// Checks the behavior shared by the IO exceptions, which are constructed from a file, a line, and a message. +template +void +CheckIOException(const std::string & expectedName) +{ + static_assert(std::is_base_of_v); + static_assert(std::is_nothrow_destructible_v); + + const unsigned int line = __LINE__; + const TException withDefaultMessage(__FILE__, line); + EXPECT_EQ(withDefaultMessage.GetNameOfClass(), expectedName); + EXPECT_STREQ(withDefaultMessage.GetDescription(), "Error in IO"); + EXPECT_STREQ(withDefaultMessage.GetFile(), __FILE__); + EXPECT_EQ(withDefaultMessage.GetLine(), line); + + const TException withMessage(__FILE__, __LINE__, testMessage); + EXPECT_EQ(withMessage.GetDescription(), testMessage); + + std::ostringstream stream; + stream << withMessage; + EXPECT_TRUE(Contains(stream.str(), "itk::" + expectedName)); + EXPECT_TRUE(Contains(stream.str(), "Description: " + testMessage)); + + TException copy(withMessage); + EXPECT_EQ(copy, withMessage); + + const TException moved(std::move(copy)); + EXPECT_EQ(moved.GetDescription(), testMessage); + + TException assigned(__FILE__, __LINE__); + assigned = withMessage; + EXPECT_EQ(assigned, withMessage); + + const itk::ExceptionObject & base = withMessage; + EXPECT_EQ(base.GetNameOfClass(), expectedName); +} + + +// Checks the IO exceptions that also accept a location. +template +void +CheckIOExceptionWithLocation(const std::string & expectedName) +{ + CheckIOException(expectedName); + + const TException exception(__FILE__, __LINE__, testMessage, testLocation); + EXPECT_EQ(exception.GetLocation(), testLocation); + + TException moveAssigned(__FILE__, __LINE__); + moveAssigned = TException(exception); + EXPECT_EQ(moveAssigned.GetLocation(), testLocation); +} +} // namespace + + +TEST(ImageIOExceptions, ReaderAndWriterExceptions) +{ + CheckIOExceptionWithLocation("ImageFileReaderException"); + CheckIOExceptionWithLocation("ImageFileWriterException"); + CheckIOException("ImageSeriesWriterException"); +} diff --git a/Modules/IO/MeshBase/itk-module.cmake b/Modules/IO/MeshBase/itk-module.cmake index 89481c470fb..3820a43ba68 100644 --- a/Modules/IO/MeshBase/itk-module.cmake +++ b/Modules/IO/MeshBase/itk-module.cmake @@ -19,5 +19,6 @@ itk_module( ITKVoronoi TEST_DEPENDS ITKTestKernel + ITKGoogleTest DESCRIPTION "${DOCUMENTATION}" ) diff --git a/Modules/IO/MeshBase/test/CMakeLists.txt b/Modules/IO/MeshBase/test/CMakeLists.txt index ab8a5add5ed..789b415276b 100644 --- a/Modules/IO/MeshBase/test/CMakeLists.txt +++ b/Modules/IO/MeshBase/test/CMakeLists.txt @@ -15,3 +15,6 @@ itk_add_test( ITK_REMOVE_TEMPORARY_TEST_FILES ${ITK_TEST_OUTPUT_DIR}/itkMeshFileReaderWriterTest.vtk ) + +set(ITKIOMeshBaseGTests itkMeshIOExceptionsGTest.cxx) +creategoogletestdriver(ITKIOMeshBase "${ITKIOMeshBase-Test_LIBRARIES}" "${ITKIOMeshBaseGTests}") diff --git a/Modules/IO/MeshBase/test/itkMeshIOExceptionsGTest.cxx b/Modules/IO/MeshBase/test/itkMeshIOExceptionsGTest.cxx new file mode 100644 index 00000000000..57e80719445 --- /dev/null +++ b/Modules/IO/MeshBase/test/itkMeshIOExceptionsGTest.cxx @@ -0,0 +1,90 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +// First include the header files to be tested: +#include "itkMeshFileReaderException.h" +#include "itkMeshFileWriterException.h" + +#include +#include +#include +#include +#include + + +namespace +{ +bool +Contains(const std::string & text, const std::string & part) +{ + return text.find(part) != std::string::npos; +} + + +template +void +CheckMeshIOException(const std::string & expectedName) +{ + static_assert(std::is_base_of_v); + static_assert(std::is_nothrow_destructible_v); + + const unsigned int line = __LINE__; + const TException withDefaults(__FILE__, line); + EXPECT_EQ(withDefaults.GetNameOfClass(), expectedName); + EXPECT_STREQ(withDefaults.GetDescription(), "Error in IO"); + EXPECT_STREQ(withDefaults.GetFile(), __FILE__); + EXPECT_EQ(withDefaults.GetLine(), line); + EXPECT_STREQ(withDefaults.GetLocation(), ""); + + const std::string testMessage = "test message"; + const std::string testLocation = "test location"; + const TException exception(__FILE__, __LINE__, testMessage, testLocation); + EXPECT_EQ(exception.GetDescription(), testMessage); + EXPECT_EQ(exception.GetLocation(), testLocation); + + std::ostringstream stream; + stream << exception; + EXPECT_TRUE(Contains(stream.str(), "itk::" + expectedName)); + EXPECT_TRUE(Contains(stream.str(), "Location: \"" + testLocation + '"')); + EXPECT_TRUE(Contains(stream.str(), "Description: " + testMessage)); + + TException copy(exception); + EXPECT_EQ(copy, exception); + + const TException moved(std::move(copy)); + EXPECT_EQ(moved.GetDescription(), testMessage); + + TException assigned(__FILE__, __LINE__); + assigned = exception; + EXPECT_EQ(assigned, exception); + + TException moveAssigned(__FILE__, __LINE__); + moveAssigned = TException(exception); + EXPECT_EQ(moveAssigned.GetLocation(), testLocation); + + const itk::ExceptionObject & base = exception; + EXPECT_EQ(base.GetNameOfClass(), expectedName); +} +} // namespace + + +TEST(MeshIOExceptions, ReaderAndWriterExceptions) +{ + CheckMeshIOException("MeshFileReaderException"); + CheckMeshIOException("MeshFileWriterException"); +} diff --git a/Modules/Numerics/Statistics/test/CMakeLists.txt b/Modules/Numerics/Statistics/test/CMakeLists.txt index 06835a09cc7..ba3fcee4c9a 100644 --- a/Modules/Numerics/Statistics/test/CMakeLists.txt +++ b/Modules/Numerics/Statistics/test/CMakeLists.txt @@ -984,6 +984,7 @@ set( itkMixtureModelComponentBaseGTest.cxx itkNormalVariateGeneratorGTest1.cxx itkRandomVariateGeneratorBaseGTest.cxx + itkSampleToHistogramFilterExceptionsGTest.cxx itkStatisticsTypesGTest.cxx itkTDistributionGTest.cxx itkUniformRandomSpatialNeighborSubsamplerGTest.cxx diff --git a/Modules/Numerics/Statistics/test/itkSampleToHistogramFilterExceptionsGTest.cxx b/Modules/Numerics/Statistics/test/itkSampleToHistogramFilterExceptionsGTest.cxx new file mode 100644 index 00000000000..928824fa2ef --- /dev/null +++ b/Modules/Numerics/Statistics/test/itkSampleToHistogramFilterExceptionsGTest.cxx @@ -0,0 +1,85 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +// First include the header file to be tested: +#include "itkSampleToHistogramFilter.h" + +#include +#include +#include +#include + + +namespace +{ +bool +Contains(const std::string & text, const std::string & part) +{ + return text.find(part) != std::string::npos; +} + + +// Checks an exception declared by itkDeclareExceptionMacro. +template +void +CheckDeclaredException(const std::string & expectedName, const std::string & expectedDefaultMessage) +{ + static_assert(std::is_base_of_v); + static_assert(std::is_base_of_v); + static_assert(std::is_nothrow_default_constructible_v); + + EXPECT_EQ(TException::default_exception_message, expectedDefaultMessage); + + const TException defaultConstructed{}; + EXPECT_EQ(defaultConstructed.GetNameOfClass(), expectedName); + + const TException exception(__FILE__, __LINE__, TException::default_exception_message, ITK_LOCATION); + EXPECT_EQ(exception.GetNameOfClass(), expectedName); + EXPECT_EQ(exception.GetDescription(), expectedDefaultMessage); + + std::ostringstream stream; + stream << exception; + EXPECT_TRUE(Contains(stream.str(), "itk::" + expectedName)); + EXPECT_TRUE(Contains(stream.str(), expectedDefaultMessage)); + + const TException copy(exception); + EXPECT_EQ(copy, exception); + + const TExpectedBaseClass & base = exception; + EXPECT_EQ(base.GetNameOfClass(), expectedName); +} +} // namespace + + +TEST(SampleToHistogramFilterExceptions, DeclaredExceptions) +{ + CheckDeclaredException( + "SampleToHistogramFilterException", "Histogram-related Exception"); + CheckDeclaredException( + "MissingHistogramSizeInput", "Histogram Size input is missing"); + CheckDeclaredException( + "MissingHistogramMarginalScaleInput", "Histogram marginal scale input is missing"); + CheckDeclaredException( + "NullSizeHistogramInputMeasurementVectorSize", "Input sample MeasurementVectorSize is zero"); + CheckDeclaredException( + "MissingHistogramBinMaximumInput", "Histogram Bin Maximum input is missing"); + CheckDeclaredException( + "MissingHistogramBinMinimumInput", "Histogram Bin Minimum input is missing"); + CheckDeclaredException( + "HistogramWrongNumberOfComponents", "Histogram has wrong number of components"); +}