From 15c519ab4cee86fd32ddb783cae40685df8d84e1 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Fri, 24 Jan 2025 12:36:38 -0800 Subject: [PATCH 1/5] Add unit tests for Channel.hpp to improve coverage --- tests/CMakeLists.txt | 1 + tests/testChannel.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 tests/testChannel.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 77b8b9ce..5954fc10 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,7 @@ include(Catch) add_executable(aqnwb_test testBaseIO.cpp + testChannel.cpp testData.cpp testDevice.cpp testEcephys.cpp diff --git a/tests/testChannel.cpp b/tests/testChannel.cpp new file mode 100644 index 00000000..c8434ec7 --- /dev/null +++ b/tests/testChannel.cpp @@ -0,0 +1,189 @@ +#include + +#include "Channel.hpp" +#include "testUtils.hpp" + +TEST_CASE("Test Channel Construction and Basic Operations", "[channel]") +{ + SECTION("Default constructor parameters") + { + Channel ch("test_channel", "test_group", 1, 2, 3); + REQUIRE(ch.getName() == "test_channel"); + REQUIRE(ch.getGroupName() == "test_group"); + REQUIRE(ch.getGroupIndex() == 1); + REQUIRE(ch.getLocalIndex() == 2); + REQUIRE(ch.getGlobalIndex() == 3); + REQUIRE(ch.getConversion() + == Catch::Approx(0.05e-6f).epsilon( + 0.001)); // bitVolts/conversion = 0.05/1e6 + REQUIRE(ch.getSamplingRate() == Catch::Approx(30000.f).epsilon(0.001)); + REQUIRE(ch.getBitVolts() == Catch::Approx(0.05f).epsilon(0.001)); + const auto& pos = ch.getPosition(); + REQUIRE(pos[0] == Catch::Approx(0.f).epsilon(0.001)); + REQUIRE(pos[1] == Catch::Approx(0.f).epsilon(0.001)); + REQUIRE(pos[2] == Catch::Approx(0.f).epsilon(0.001)); + REQUIRE(ch.getComments() == "no comments"); + } + + SECTION("Custom constructor parameters") + { + std::array pos = {1.f, 2.f, 3.f}; + Channel ch("test_channel", + "test_group", + 1, + 2, + 3, + 2e6f, // custom conversion + 44100.f, // custom sampling rate + 0.1f, // custom bitVolts + pos, // custom position + "test comment" // custom comment + ); + + REQUIRE(ch.getName() == "test_channel"); + REQUIRE(ch.getGroupName() == "test_group"); + REQUIRE(ch.getGroupIndex() == 1); + REQUIRE(ch.getLocalIndex() == 2); + REQUIRE(ch.getGlobalIndex() == 3); + REQUIRE(ch.getConversion() == Catch::Approx(0.1f / 2e6f).epsilon(0.001)); + REQUIRE(ch.getSamplingRate() == Catch::Approx(44100.f).epsilon(0.001)); + REQUIRE(ch.getBitVolts() == Catch::Approx(0.1f).epsilon(0.001)); + const auto& actualPos = ch.getPosition(); + REQUIRE(actualPos[0] == Catch::Approx(pos[0]).epsilon(0.001)); + REQUIRE(actualPos[1] == Catch::Approx(pos[1]).epsilon(0.001)); + REQUIRE(actualPos[2] == Catch::Approx(pos[2]).epsilon(0.001)); + REQUIRE(ch.getComments() == "test comment"); + } +} + +TEST_CASE("Test Channel Setters", "[channel]") +{ + Channel ch("test_channel", "test_group", 1, 2, 3); + + SECTION("Set comments") + { + ch.setComments("new comment"); + REQUIRE(ch.getComments() == "new comment"); + } + + SECTION("Set position") + { + std::array newPos = {4.f, 5.f, 6.f}; + ch.setPosition(newPos); + const auto& actualPos = ch.getPosition(); + REQUIRE(actualPos[0] == Catch::Approx(newPos[0]).epsilon(0.001)); + REQUIRE(actualPos[1] == Catch::Approx(newPos[1]).epsilon(0.001)); + REQUIRE(actualPos[2] == Catch::Approx(newPos[2]).epsilon(0.001)); + } + + SECTION("Set name") + { + ch.setName("new_channel"); + REQUIRE(ch.getName() == "new_channel"); + } +} + +TEST_CASE("Test Channel Copy and Move Operations", "[channel]") +{ + std::array pos = {1.f, 2.f, 3.f}; + Channel original("test_channel", + "test_group", + 1, + 2, + 3, + 2e6f, + 44100.f, + 0.1f, + pos, + "test comment"); + + SECTION("Copy constructor") + { + Channel copy(original); + REQUIRE(copy.getName() == original.getName()); + REQUIRE(copy.getGroupName() == original.getGroupName()); + REQUIRE(copy.getGroupIndex() == original.getGroupIndex()); + REQUIRE(copy.getLocalIndex() == original.getLocalIndex()); + REQUIRE(copy.getGlobalIndex() == original.getGlobalIndex()); + REQUIRE(copy.getConversion() + == Catch::Approx(original.getConversion()).epsilon(0.001)); + REQUIRE(copy.getSamplingRate() + == Catch::Approx(original.getSamplingRate()).epsilon(0.001)); + REQUIRE(copy.getBitVolts() + == Catch::Approx(original.getBitVolts()).epsilon(0.001)); + const auto& origPos = original.getPosition(); + const auto& copyPos = copy.getPosition(); + REQUIRE(copyPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); + REQUIRE(copyPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); + REQUIRE(copyPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE(copy.getComments() == original.getComments()); + } + + SECTION("Copy assignment") + { + Channel copy("other", "other_group", 0, 0, 0); + copy = original; + REQUIRE(copy.getName() == original.getName()); + REQUIRE(copy.getGroupName() == original.getGroupName()); + REQUIRE(copy.getGroupIndex() == original.getGroupIndex()); + REQUIRE(copy.getLocalIndex() == original.getLocalIndex()); + REQUIRE(copy.getGlobalIndex() == original.getGlobalIndex()); + REQUIRE(copy.getConversion() + == Catch::Approx(original.getConversion()).epsilon(0.001)); + REQUIRE(copy.getSamplingRate() + == Catch::Approx(original.getSamplingRate()).epsilon(0.001)); + REQUIRE(copy.getBitVolts() + == Catch::Approx(original.getBitVolts()).epsilon(0.001)); + const auto& origPos = original.getPosition(); + const auto& copyPos = copy.getPosition(); + REQUIRE(copyPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); + REQUIRE(copyPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); + REQUIRE(copyPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE(copy.getComments() == original.getComments()); + } + + SECTION("Move constructor") + { + Channel moved(std::move(Channel(original))); + REQUIRE(moved.getName() == original.getName()); + REQUIRE(moved.getGroupName() == original.getGroupName()); + REQUIRE(moved.getGroupIndex() == original.getGroupIndex()); + REQUIRE(moved.getLocalIndex() == original.getLocalIndex()); + REQUIRE(moved.getGlobalIndex() == original.getGlobalIndex()); + REQUIRE(moved.getConversion() + == Catch::Approx(original.getConversion()).epsilon(0.001)); + REQUIRE(moved.getSamplingRate() + == Catch::Approx(original.getSamplingRate()).epsilon(0.001)); + REQUIRE(moved.getBitVolts() + == Catch::Approx(original.getBitVolts()).epsilon(0.001)); + const auto& origPos = original.getPosition(); + const auto& movedPos = moved.getPosition(); + REQUIRE(movedPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); + REQUIRE(movedPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); + REQUIRE(movedPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE(moved.getComments() == original.getComments()); + } + + SECTION("Move assignment") + { + Channel moved("other", "other_group", 0, 0, 0); + moved = std::move(Channel(original)); + REQUIRE(moved.getName() == original.getName()); + REQUIRE(moved.getGroupName() == original.getGroupName()); + REQUIRE(moved.getGroupIndex() == original.getGroupIndex()); + REQUIRE(moved.getLocalIndex() == original.getLocalIndex()); + REQUIRE(moved.getGlobalIndex() == original.getGlobalIndex()); + REQUIRE(moved.getConversion() + == Catch::Approx(original.getConversion()).epsilon(0.001)); + REQUIRE(moved.getSamplingRate() + == Catch::Approx(original.getSamplingRate()).epsilon(0.001)); + REQUIRE(moved.getBitVolts() + == Catch::Approx(original.getBitVolts()).epsilon(0.001)); + const auto& origPos = original.getPosition(); + const auto& movedPos = moved.getPosition(); + REQUIRE(movedPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); + REQUIRE(movedPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); + REQUIRE(movedPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE(moved.getComments() == original.getComments()); + } +} From 9f54ad21d8d459e1f2043795e77a49afc224b7f6 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Fri, 24 Jan 2025 13:00:12 -0800 Subject: [PATCH 2/5] Add unit tests for HDF5RecordingData --- src/io/BaseIO.hpp | 20 ++- tests/CMakeLists.txt | 1 + tests/testHDF5RecordingData.cpp | 221 ++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 tests/testHDF5RecordingData.cpp diff --git a/src/io/BaseIO.hpp b/src/io/BaseIO.hpp index e44d780c..c5fef6d8 100644 --- a/src/io/BaseIO.hpp +++ b/src/io/BaseIO.hpp @@ -564,6 +564,24 @@ class BaseRecordingData const BaseDataType& type, const std::vector& data) = 0; + /** + * @brief Get the number of dimensions in the dataset. + * @return The number of dimensions. + */ + inline SizeType getNumDimensions() const { return nDimensions; } + + /** + * @brief Get the size of the dataset. + * @return Vector containing the size in each dimension. + */ + inline const std::vector& getSize() const { return size; } + + /** + * @brief Get the current position in the dataset. + * @return Vector containing the position in each dimension. + */ + inline const std::vector& getPosition() const { return position; } + protected: /** * @brief The size of the dataset in each dimension. @@ -576,7 +594,7 @@ class BaseRecordingData std::vector position; /** - * @brief The number of dimensions in the data block. + * @brief Number of dimensions in the dataset. */ SizeType nDimensions; }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5954fc10..59ef9843 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(aqnwb_test testChannel.cpp testData.cpp testDevice.cpp + testHDF5RecordingData.cpp testEcephys.cpp testElementIdentifiers.cpp testFile.cpp diff --git a/tests/testHDF5RecordingData.cpp b/tests/testHDF5RecordingData.cpp new file mode 100644 index 00000000..5554f077 --- /dev/null +++ b/tests/testHDF5RecordingData.cpp @@ -0,0 +1,221 @@ +#include + +#include "io/hdf5/HDF5RecordingData.hpp" +#include "testUtils.hpp" + +TEST_CASE("HDF5RecordingData basic operations", "[hdf5recordingdata]") +{ + SECTION("Constructor and initialization") + { + // Create a file and dataset first + std::string path = getTestFilePath("test_HDF5RecordingData.h5"); + std::unique_ptr hdf5io = + std::make_unique(path); + hdf5io->open(); + + // Create a dataset + std::unique_ptr dataset = hdf5io->createArrayDataSet( + BaseDataType::I32, SizeArray {10}, SizeArray {5}, "/testDataset"); + + // Verify the dataset was created with correct dimensions + REQUIRE(dataset->getNumDimensions() == 1); + REQUIRE(dataset->getSize()[0] == 10); + REQUIRE(dataset->getPosition()[0] == 0); + + hdf5io->close(); + } +} + +TEST_CASE("HDF5RecordingData write operations", "[hdf5recordingdata]") +{ + std::string path = getTestFilePath("test_HDF5RecordingData_write.h5"); + std::unique_ptr hdf5io = + std::make_unique(path); + hdf5io->open(); + + SECTION("Write numeric data types") + { + // Test int32 + { + std::vector data = {1, 2, 3, 4, 5}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::I32, SizeArray {5}, SizeArray {5}, "/int32Dataset"); + Status status = dataset->writeDataBlock(std::vector {5}, + std::vector {0}, + BaseDataType::I32, + data.data()); + REQUIRE(status == Status::Success); + } + + // Test float + { + std::vector data = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::F32, SizeArray {5}, SizeArray {5}, "/floatDataset"); + Status status = dataset->writeDataBlock(std::vector {5}, + std::vector {0}, + BaseDataType::F32, + data.data()); + REQUIRE(status == Status::Success); + } + } + + SECTION("Write string data") + { + // Test fixed-length strings + { + std::vector data = {"abc", "def", "ghi"}; + BaseDataType strType(BaseDataType::Type::T_STR, 3); + auto dataset = hdf5io->createArrayDataSet( + strType, SizeArray {3}, SizeArray {3}, "/fixedStrDataset"); + Status status = dataset->writeDataBlock( + std::vector {3}, std::vector {0}, strType, data); + REQUIRE(status == Status::Success); + } + + // Test variable-length strings + { + std::vector data = { + "longer", "strings", "of", "varying", "length"}; + BaseDataType strType(BaseDataType::Type::V_STR, 0); + auto dataset = hdf5io->createArrayDataSet( + strType, SizeArray {5}, SizeArray {5}, "/varStrDataset"); + Status status = dataset->writeDataBlock( + std::vector {5}, std::vector {0}, strType, data); + REQUIRE(status == Status::Success); + } + } + + SECTION("Error cases") + { + // Test dimension mismatch + { + std::vector data = {1, 2, 3, 4, 5}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::I32, SizeArray {5}, SizeArray {5}, "/errorDataset1"); + // Wrong number of dimensions in dataShape + Status status = dataset->writeDataBlock( + std::vector {5, 1}, // 2D shape for 1D dataset + std::vector {0}, + BaseDataType::I32, + data.data()); + REQUIRE(status == Status::Failure); + } + + // Test wrong data type + // TODO: This test is not possible because the data type is not checked + // when writing data. This is because the data type is not stored + // in the dataset object. It is only used when reading data. + // This should be fixed in the future by storing the data type + // in the BaseRecordingData object, which should allow us to + // remove the need to specify the data type when calling + // writeDataBlock and enforce types more strictly on write. + /* + { + std::vector data = {1, 2, 3, 4, 5}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::F32, // Float dataset + SizeArray {5}, + SizeArray {5}, + "/errorDataset2" + ); + // Try to write int32 data to float dataset + Status status = dataset->writeDataBlock( + std::vector{5}, + std::vector{0}, + BaseDataType::I32, // Wrong type + data.data() + ); + REQUIRE(status == Status::Failure); + }*/ + + // Test writing string data with void* interface + { + std::vector data = {"test"}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::V_STR, SizeArray {1}, SizeArray {1}, "/errorDataset3"); + // Try to write string data using void* interface + Status status = dataset->writeDataBlock(std::vector {1}, + std::vector {0}, + BaseDataType::V_STR, + data.data() // This should fail + ); + REQUIRE(status == Status::Failure); + } + + // Test position offset out of bounds + { + std::vector data = {1, 2, 3, 4, 5}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::I32, SizeArray {5}, SizeArray {5}, "/errorDataset4"); + // Try to write at invalid offset + Status status = dataset->writeDataBlock( + std::vector {5}, + std::vector {10}, // Offset too large + BaseDataType::I32, + data.data()); + REQUIRE(status == Status::Failure); + } + } + + hdf5io->close(); +} + +TEST_CASE("HDF5RecordingData multi-dimensional operations", + "[hdf5recordingdata]") +{ + std::string path = getTestFilePath("test_HDF5RecordingData_multidim.h5"); + std::unique_ptr hdf5io = + std::make_unique(path); + hdf5io->open(); + + SECTION("2D operations") + { + std::vector data = {1, 2, 3, 4, 5, 6}; + auto dataset = hdf5io->createArrayDataSet( + BaseDataType::I32, SizeArray {2, 3}, SizeArray {2, 3}, "/2dDataset"); + + // Write full 2D block + Status status = dataset->writeDataBlock(std::vector {2, 3}, + std::vector {0, 0}, + BaseDataType::I32, + data.data()); + REQUIRE(status == Status::Success); + + // Write partial block + std::vector partial = {7, 8}; + status = dataset->writeDataBlock(std::vector {1, 2}, + std::vector {1, 1}, + BaseDataType::I32, + partial.data()); + REQUIRE(status == Status::Success); + } + + SECTION("3D operations") + { + std::vector data(24); // 2x3x4 array + std::iota(data.begin(), data.end(), 1); // Fill with 1,2,3,...,24 + + auto dataset = hdf5io->createArrayDataSet(BaseDataType::I32, + SizeArray {2, 3, 4}, + SizeArray {2, 3, 4}, + "/3dDataset"); + + // Write full 3D block + Status status = dataset->writeDataBlock(std::vector {2, 3, 4}, + std::vector {0, 0, 0}, + BaseDataType::I32, + data.data()); + REQUIRE(status == Status::Success); + + // Write partial block + std::vector partial = {100, 101, 102, 103}; + status = dataset->writeDataBlock(std::vector {1, 1, 4}, + std::vector {1, 1, 0}, + BaseDataType::I32, + partial.data()); + REQUIRE(status == Status::Success); + } + + hdf5io->close(); +} From ac7e3c1f02347836fd8b26ae380ae974083c98c7 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Fri, 24 Jan 2025 13:55:53 -0800 Subject: [PATCH 3/5] Fix failing unit test case --- tests/testHDF5RecordingData.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/testHDF5RecordingData.cpp b/tests/testHDF5RecordingData.cpp index 5554f077..0234f9a2 100644 --- a/tests/testHDF5RecordingData.cpp +++ b/tests/testHDF5RecordingData.cpp @@ -148,13 +148,17 @@ TEST_CASE("HDF5RecordingData write operations", "[hdf5recordingdata]") std::vector data = {1, 2, 3, 4, 5}; auto dataset = hdf5io->createArrayDataSet( BaseDataType::I32, SizeArray {5}, SizeArray {5}, "/errorDataset4"); - // Try to write at invalid offset + // Write at larger offset - should succeed by extending dataset Status status = dataset->writeDataBlock( std::vector {5}, - std::vector {10}, // Offset too large + std::vector {10}, // Dataset will extend to accommodate BaseDataType::I32, data.data()); - REQUIRE(status == Status::Failure); + REQUIRE(status == Status::Success); + + // Verify the dataset was extended + REQUIRE(dataset->getSize()[0] + == 15); // Original offset (10) + data size (5) } } From 30856b28cc4ddeaf458a906dfbde34eba03f3979 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Tue, 4 Feb 2025 19:59:35 -0800 Subject: [PATCH 4/5] Replace vector comparison with RangeEquals --- tests/testChannel.cpp | 39 +++++++++++++++++---------------------- tests/testUtils.hpp | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/tests/testChannel.cpp b/tests/testChannel.cpp index c8434ec7..e0ea1b34 100644 --- a/tests/testChannel.cpp +++ b/tests/testChannel.cpp @@ -1,4 +1,5 @@ #include +#include #include "Channel.hpp" #include "testUtils.hpp" @@ -18,10 +19,10 @@ TEST_CASE("Test Channel Construction and Basic Operations", "[channel]") 0.001)); // bitVolts/conversion = 0.05/1e6 REQUIRE(ch.getSamplingRate() == Catch::Approx(30000.f).epsilon(0.001)); REQUIRE(ch.getBitVolts() == Catch::Approx(0.05f).epsilon(0.001)); - const auto& pos = ch.getPosition(); - REQUIRE(pos[0] == Catch::Approx(0.f).epsilon(0.001)); - REQUIRE(pos[1] == Catch::Approx(0.f).epsilon(0.001)); - REQUIRE(pos[2] == Catch::Approx(0.f).epsilon(0.001)); + std::array pos = {0.f, 0.f, 0.f}; + const auto& actualPos = ch.getPosition(); + REQUIRE_THAT(actualPos, + Catch::Matchers::RangeEquals(pos, approxComparator)); REQUIRE(ch.getComments() == "no comments"); } @@ -49,9 +50,8 @@ TEST_CASE("Test Channel Construction and Basic Operations", "[channel]") REQUIRE(ch.getSamplingRate() == Catch::Approx(44100.f).epsilon(0.001)); REQUIRE(ch.getBitVolts() == Catch::Approx(0.1f).epsilon(0.001)); const auto& actualPos = ch.getPosition(); - REQUIRE(actualPos[0] == Catch::Approx(pos[0]).epsilon(0.001)); - REQUIRE(actualPos[1] == Catch::Approx(pos[1]).epsilon(0.001)); - REQUIRE(actualPos[2] == Catch::Approx(pos[2]).epsilon(0.001)); + REQUIRE_THAT(actualPos, + Catch::Matchers::RangeEquals(pos, approxComparator)); REQUIRE(ch.getComments() == "test comment"); } } @@ -71,9 +71,8 @@ TEST_CASE("Test Channel Setters", "[channel]") std::array newPos = {4.f, 5.f, 6.f}; ch.setPosition(newPos); const auto& actualPos = ch.getPosition(); - REQUIRE(actualPos[0] == Catch::Approx(newPos[0]).epsilon(0.001)); - REQUIRE(actualPos[1] == Catch::Approx(newPos[1]).epsilon(0.001)); - REQUIRE(actualPos[2] == Catch::Approx(newPos[2]).epsilon(0.001)); + REQUIRE_THAT(actualPos, + Catch::Matchers::RangeEquals(newPos, approxComparator)); } SECTION("Set name") @@ -113,9 +112,8 @@ TEST_CASE("Test Channel Copy and Move Operations", "[channel]") == Catch::Approx(original.getBitVolts()).epsilon(0.001)); const auto& origPos = original.getPosition(); const auto& copyPos = copy.getPosition(); - REQUIRE(copyPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); - REQUIRE(copyPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); - REQUIRE(copyPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE_THAT(copyPos, + Catch::Matchers::RangeEquals(origPos, approxComparator)); REQUIRE(copy.getComments() == original.getComments()); } @@ -136,9 +134,8 @@ TEST_CASE("Test Channel Copy and Move Operations", "[channel]") == Catch::Approx(original.getBitVolts()).epsilon(0.001)); const auto& origPos = original.getPosition(); const auto& copyPos = copy.getPosition(); - REQUIRE(copyPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); - REQUIRE(copyPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); - REQUIRE(copyPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE_THAT(copyPos, + Catch::Matchers::RangeEquals(origPos, approxComparator)); REQUIRE(copy.getComments() == original.getComments()); } @@ -158,9 +155,8 @@ TEST_CASE("Test Channel Copy and Move Operations", "[channel]") == Catch::Approx(original.getBitVolts()).epsilon(0.001)); const auto& origPos = original.getPosition(); const auto& movedPos = moved.getPosition(); - REQUIRE(movedPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); - REQUIRE(movedPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); - REQUIRE(movedPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE_THAT(movedPos, + Catch::Matchers::RangeEquals(origPos, approxComparator)); REQUIRE(moved.getComments() == original.getComments()); } @@ -181,9 +177,8 @@ TEST_CASE("Test Channel Copy and Move Operations", "[channel]") == Catch::Approx(original.getBitVolts()).epsilon(0.001)); const auto& origPos = original.getPosition(); const auto& movedPos = moved.getPosition(); - REQUIRE(movedPos[0] == Catch::Approx(origPos[0]).epsilon(0.001)); - REQUIRE(movedPos[1] == Catch::Approx(origPos[1]).epsilon(0.001)); - REQUIRE(movedPos[2] == Catch::Approx(origPos[2]).epsilon(0.001)); + REQUIRE_THAT(movedPos, + Catch::Matchers::RangeEquals(origPos, approxComparator)); REQUIRE(moved.getComments() == original.getComments()); } } diff --git a/tests/testUtils.hpp b/tests/testUtils.hpp index 5feda449..a6954596 100644 --- a/tests/testUtils.hpp +++ b/tests/testUtils.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "Channel.hpp" @@ -15,6 +16,20 @@ using namespace AQNWB; using namespace AQNWB::IO; namespace fs = std::filesystem; +/* + * @brief Approximate comparator for floats used in Catch2 tests. + */ +inline bool approxComparator(float a, float b) +{ + return a == Catch::Approx(b).epsilon(0.001); +} + +/* + * @brief Get the path to a test file in the data directory. + * + * This function creates the data directory if it doesn't exist, removes the + * file if it already exists, and returns the path to the file. + */ inline std::string getTestFilePath(std::string filename) { // create data directory if it doesn't exist From d5f4e5dc0b2dc0079f2b3e69cac2b42db51acd68 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Tue, 4 Feb 2025 20:06:03 -0800 Subject: [PATCH 5/5] Fix build warning --- tests/testChannel.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/testChannel.cpp b/tests/testChannel.cpp index e0ea1b34..5cdf16a9 100644 --- a/tests/testChannel.cpp +++ b/tests/testChannel.cpp @@ -141,7 +141,8 @@ TEST_CASE("Test Channel Copy and Move Operations", "[channel]") SECTION("Move constructor") { - Channel moved(std::move(Channel(original))); + Channel temp(original); + Channel moved(std::move(temp)); REQUIRE(moved.getName() == original.getName()); REQUIRE(moved.getGroupName() == original.getGroupName()); REQUIRE(moved.getGroupIndex() == original.getGroupIndex()); @@ -163,7 +164,8 @@ TEST_CASE("Test Channel Copy and Move Operations", "[channel]") SECTION("Move assignment") { Channel moved("other", "other_group", 0, 0, 0); - moved = std::move(Channel(original)); + Channel temp(original); + moved = std::move(temp); REQUIRE(moved.getName() == original.getName()); REQUIRE(moved.getGroupName() == original.getGroupName()); REQUIRE(moved.getGroupIndex() == original.getGroupIndex());