Commit 7eb49fbd authored by David Frank's avatar David Frank Committed by Tobias Lasser

Add helper to print type in tests; Remove duplicate function of random...

Add helper to print type in tests; Remove duplicate function of random generation of eigen vectors; Add test, which should fail sanitizer
parent 977cf168
Pipeline #233863 failed with stages
in 27 seconds
......@@ -226,14 +226,15 @@ cuda-memcheck:
- gcc
- cuda
sanitize-asan-ubsan:
asan-ubsan:
<<: *nightly_job
stage: sanitizer
image: $CUDA_IMAGE
script:
- mkdir -p build
- cd build
- cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DELSA_SANITIZER="Address;Undefined" ..
- rm -f CMakeCache.txt # remove cache, as we sometimes get the wrong cache
- cmake -GNinja -DELSA_BUILD_CUDA_PROJECTORS=OFF -DCMAKE_BUILD_TYPE=Debug -DELSA_SANITIZER="Address;Undefined" ..
- ninja tests
tags:
- linux
......@@ -241,34 +242,6 @@ sanitize-asan-ubsan:
- gcc
- cuda
sanitize-tsan:
<<: *nightly_job
stage: sanitizer
image: $CUDA_IMAGE
script:
- mkdir -p build
- cd build
- CC=clang CXX=clang cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DELSA_SANITIZER="Thread" ..
- ninja tests
tags:
- linux
- elsa
- cuda
sanitize-memsan:
<<: *nightly_job
stage: sanitizer
image: $CUDA_IMAGE
script:
- mkdir -p build
- cd build
- CC=clang CXX=clang cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DELSA_SANITIZER="Memory" ..
- ninja tests
tags:
- linux
- elsa
- cuda
### test coverage ###
......
......@@ -113,8 +113,7 @@ if(ELSA_CUDA_VECTOR)
endif()
endif()
# ------------ setup tools -----------
# ------------ Setup Tools -----------
# ------------
# Includes clang-format, clang-tidy, cmake-format, and sanitizers
......@@ -143,6 +142,7 @@ if(ELSA_TESTING OR ELSA_BENCHMARKS)
add_custom_target(tests
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
USES_TERMINAL
COMMENT "Build and run all the tests.")
if(ELSA_COVERAGE)
message(STATUS "elsa test coverage is enabled")
......
......@@ -13,6 +13,8 @@
#include "IdenticalBlocksDescriptor.h"
#include "testHelpers.h"
#include <type_traits>
using namespace elsa;
using namespace Catch::literals; // to enable 0.0_a approximate floats
......@@ -43,6 +45,8 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Constructing DataContainers", "", (TestHel
{
using data_t = typename TestType::data_t;
INFO("Testing type: " << TypeName_v<const data_t>);
GIVEN("a DataDescriptor")
{
IndexVector_t numCoeff(3);
......@@ -63,8 +67,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Constructing DataContainers", "", (TestHel
WHEN("constructing an initialized DataContainer")
{
Eigen::Matrix<data_t, Eigen::Dynamic, 1> data{desc.getNumberOfCoefficients()};
data.setRandom();
auto data = generateRandomMatrix<data_t>(desc.getNumberOfCoefficients());
DataContainer<data_t> dc(desc, data, TestType::handler_t);
......@@ -87,8 +90,8 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Constructing DataContainers", "", (TestHel
DataDescriptor desc(numCoeff);
DataContainer<data_t> otherDc(desc, TestType::handler_t);
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{otherDc.getSize()};
randVec.setRandom();
auto randVec = generateRandomMatrix<data_t>(otherDc.getSize());
for (index_t i = 0; i < otherDc.getSize(); ++i)
otherDc[i] = randVec(i);
......@@ -165,6 +168,8 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Element-wise access of DataContainers", ""
{
using data_t = typename TestType::data_t;
INFO("Testing type: " << TypeName_v<const data_t>);
GIVEN("a DataContainer")
{
IndexVector_t numCoeff(2);
......@@ -206,19 +211,17 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the reduction operations of DataCo
{
using data_t = typename TestType::data_t;
INFO("Testing type: " << TypeName_v<const data_t>);
GIVEN("a DataContainer")
{
IndexVector_t numCoeff(3);
numCoeff << 11, 73, 45;
DataDescriptor desc(numCoeff);
DataContainer<data_t> dc(desc, TestType::handler_t);
WHEN("putting in some random data")
{
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{dc.getSize()};
randVec.setRandom();
for (index_t i = 0; i < dc.getSize(); ++i)
dc[i] = randVec(i);
auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
THEN("the reductions work as expected")
{
......@@ -227,13 +230,9 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the reduction operations of DataCo
REQUIRE(checkSameNumbers(dc.lInfNorm(), randVec.array().abs().maxCoeff()));
REQUIRE(checkSameNumbers(dc.squaredL2Norm(), randVec.squaredNorm()));
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec2{dc.getSize()};
randVec2.setRandom();
DataContainer<data_t> dc2(desc, TestType::handler_t);
for (index_t i = 0; i < dc2.getSize(); ++i)
dc2[i] = randVec2(i);
auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
REQUIRE(checkSameNumbers(dc.dot(dc2), randVec.dot(randVec2), 10));
REQUIRE(checkSameNumbers(dc.dot(dc2), randVec.dot(randVec2)));
}
}
}
......@@ -251,25 +250,23 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of Dat
{
using data_t = typename TestType::data_t;
INFO("Testing type: " << TypeName_v<const data_t>);
GIVEN("a DataContainer")
{
IndexVector_t numCoeff(2);
numCoeff << 47, 11;
DataDescriptor desc(numCoeff);
DataContainer<data_t> dc(desc, TestType::handler_t);
WHEN("putting in some random data")
{
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{dc.getSize()};
randVec.setRandom();
for (index_t i = 0; i < dc.getSize(); ++i)
dc[i] = randVec(i);
auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
THEN("the element-wise unary operations work as expected")
{
DataContainer dcSquare = square(dc);
for (index_t i = 0; i < dc.getSize(); ++i)
REQUIRE(checkSameNumbers(dcSquare[i], randVec.array().square()[i], 10));
REQUIRE(checkSameNumbers(dcSquare[i], randVec.array().square()[i]));
DataContainer dcSqrt = sqrt(dcSquare);
for (index_t i = 0; i < dc.getSize(); ++i)
REQUIRE(checkSameNumbers(dcSqrt[i], randVec.array().square().sqrt()[i]));
......@@ -284,7 +281,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of Dat
DataContainer dcLog = log(dcSquare);
for (index_t i = 0; i < dc.getSize(); ++i)
REQUIRE(checkSameNumbers(dcLog[i], randVec.array().square().log()[i], 100));
REQUIRE(checkSameNumbers(dcLog[i], randVec.array().square().log()[i]));
}
auto scalar = static_cast<data_t>(923.41f);
......@@ -327,16 +324,8 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of Dat
WHEN("having two containers with random data")
{
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{dc.getSize()};
randVec.setRandom();
for (index_t i = 0; i < dc.getSize(); ++i)
dc[i] = randVec(i);
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec2{dc.getSize()};
randVec2.setRandom();
DataContainer<data_t> dc2(desc, TestType::handler_t);
for (index_t i = 0; i < dc2.getSize(); ++i)
dc2[i] = randVec2[i];
auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
THEN("the element-wise in-place addition works as expected")
{
......@@ -356,7 +345,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of Dat
{
dc *= dc2;
for (index_t i = 0; i < dc.getSize(); ++i)
REQUIRE(checkSameNumbers(dc[i], randVec(i) * randVec2(i), 100));
REQUIRE(checkSameNumbers(dc[i], randVec(i) * randVec2(i)));
}
THEN("the element-wise in-place division works as expected")
......@@ -364,7 +353,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of Dat
dc /= dc2;
for (index_t i = 0; i < dc.getSize(); ++i)
if (dc2[i] != data_t(0))
REQUIRE(checkSameNumbers(dc[i], randVec(i) / randVec2(i), 10));
REQUIRE(checkSameNumbers(dc[i], randVec(i) / randVec2(i)));
}
}
}
......@@ -383,24 +372,16 @@ TEMPLATE_PRODUCT_TEST_CASE(
{
using data_t = typename TestType::data_t;
INFO("Testing type: " << TypeName_v<const data_t>);
GIVEN("some DataContainers")
{
IndexVector_t numCoeff(3);
numCoeff << 52, 7, 29;
DataDescriptor desc(numCoeff);
DataContainer<data_t> dc(desc, TestType::handler_t);
DataContainer<data_t> dc2(desc, TestType::handler_t);
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{dc.getSize()};
randVec.setRandom();
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec2{dc.getSize()};
randVec2.setRandom();
for (index_t i = 0; i < dc.getSize(); ++i) {
dc[i] = randVec(i);
dc2[i] = randVec2(i);
}
auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
THEN("the binary element-wise operations work as expected")
{
......@@ -414,12 +395,12 @@ TEMPLATE_PRODUCT_TEST_CASE(
DataContainer resultMult = dc * dc2;
for (index_t i = 0; i < dc.getSize(); ++i)
REQUIRE(checkSameNumbers(resultMult[i], dc[i] * dc2[i], 100));
REQUIRE(checkSameNumbers(resultMult[i], dc[i] * dc2[i]));
DataContainer resultDiv = dc / dc2;
for (index_t i = 0; i < dc.getSize(); ++i)
if (dc2[i] != data_t(0))
REQUIRE(checkSameNumbers(resultDiv[i], dc[i] / dc2[i], 1000));
REQUIRE(checkSameNumbers(resultDiv[i], dc[i] / dc2[i]));
}
THEN("the operations with a scalar work as expected")
......@@ -474,6 +455,8 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing creation of Maps through DataConta
{
using data_t = typename TestType::data_t;
INFO("Testing type: " << TypeName_v<const data_t>);
GIVEN("a non-blocked container")
{
IndexVector_t numCoeff(3);
......@@ -600,8 +583,7 @@ TEMPLATE_TEST_CASE("Scenario: Testing loading data to GPU and vice versa.", "",
DataContainer<TestType> dcCPU(desc, DataHandlerType::CPU);
DataContainer<TestType> dcGPU(desc, DataHandlerType::GPU);
Eigen::Matrix<TestType, Eigen::Dynamic, 1> randVec{dcCPU.getSize()};
randVec.setRandom();
auto randVec = generateRandomMatrix<TestType>(dcCPU.getSize());
for (index_t i = 0; i < dcCPU.getSize(); ++i) {
dcCPU[i] = randVec(i);
......
......@@ -786,14 +786,14 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of Dat
for (index_t i = 0; i < size; ++i)
if (dh2[i] != data_t(0))
// due to floating point arithmetic less precision
REQUIRE(checkSameNumbers(dh[i], oldDh[i] / dh2[i], 100));
REQUIRE(checkSameNumbers(dh[i], oldDh[i] / dh2[i]));
dh = oldDh;
dh /= dhCPU;
for (index_t i = 0; i < size; ++i)
if (dhCPU[i] != data_t(0))
// due to floating point arithmetic less precision
REQUIRE(checkSameNumbers(dh[i], oldDh[i] / dhCPU[i], 100));
REQUIRE(checkSameNumbers(dh[i], oldDh[i] / dhCPU[i]));
}
THEN("the element-wise binary scalar operations work as expected")
......
This diff is collapsed.
......@@ -4,6 +4,7 @@
#include <complex>
#include <random>
#include "elsaDefines.h"
#include "DataDescriptor.h"
#include <iomanip>
#include <limits>
......@@ -27,23 +28,17 @@ namespace elsa
* fails
*/
template <typename T>
bool checkSameNumbers(T left, T right, int epsilonFactor = 1)
bool checkSameNumbers(T left, T right)
{
using numericalBaseType = elsa::GetFloatingPointType_t<T>;
numericalBaseType eps = std::numeric_limits<numericalBaseType>::epsilon()
* static_cast<numericalBaseType>(epsilonFactor)
* static_cast<numericalBaseType>(100);
if constexpr (std::is_same_v<
T, std::complex<float>> || std::is_same_v<T, std::complex<double>>) {
CHECK(Approx(left.real()).epsilon(eps) == right.real());
CHECK(Approx(left.imag()).epsilon(eps) == right.imag());
return Approx(left.real()).epsilon(eps) == right.real()
&& Approx(left.imag()).epsilon(eps) == right.imag();
CHECK(Approx(left.real()).margin(epsilon) == right.real());
CHECK(Approx(left.imag()).margin(epsilon) == right.imag());
return Approx(left.real()).margin(epsilon) == right.real()
&& Approx(left.imag()).margin(epsilon) == right.imag();
} else {
CHECK(Approx(left).epsilon(eps) == right);
return Approx(left).epsilon(eps) == right;
CHECK(Approx(left).margin(epsilon) == right);
return Approx(left).margin(epsilon) == right;
}
}
......@@ -59,17 +54,27 @@ namespace elsa
* running into overflow issues.
*/
template <typename data_t>
auto generateRandomMatrix(elsa::index_t size)
auto generateRandomMatrix(const index_t size)
{
Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec(size);
if constexpr (std::is_integral_v<data_t>) {
// Define range depending on signed or unsigned type
const auto [rangeBegin, rangeEnd] = []() -> std::tuple<data_t, data_t> {
if constexpr (std::is_signed_v<data_t>) {
return {-100, 100};
} else {
return {1, 100};
}
}();
std::random_device rd;
std::mt19937 eng(rd());
std::uniform_int_distribution<> distr(-100, 100);
std::uniform_int_distribution<data_t> distr(rangeBegin, rangeEnd);
for (elsa::index_t i = 0; i < size; ++i) {
for (index_t i = 0; i < size; ++i) {
data_t num = distr(eng);
// remove zeros as this leads to errors when dividing
if (num == 0)
num = 1;
......@@ -82,6 +87,29 @@ namespace elsa
return randVec;
}
/**
* \brief generate a random eigen vector and a DataContainer with the same data. Specifically
* take index_t into consideration and scale the random eigen vector, to not generate overflows
*
* \tparam data_t Value type of DataContainers
* \param desc First DataContainer
* \param handlerType Second DataContainer
*
* \return a pair of a DataContainer and eigen vector, of same size and the same values
*/
template <typename data_t>
std::tuple<DataContainer<data_t>, Eigen::Matrix<data_t, Eigen::Dynamic, 1>>
generateRandomContainer(const DataDescriptor& desc, DataHandlerType handlerType)
{
auto containerSize = desc.getNumberOfCoefficients();
auto randVec = generateRandomMatrix<data_t>(containerSize);
auto dc = DataContainer<data_t>(desc, randVec, handlerType);
return {dc, randVec};
}
/**
* \brief Compares two DataContainers using their norm. Computes \f$ \sqrt{\| x - y \|_{2}^2}
* \f$ and compares it to \f$ prec * \sqrt{min(\| x \|_{2}^2, \| y \|_{2}^2)} \f$. If the first
......@@ -108,4 +136,74 @@ namespace elsa
data_t rhs = prec * std::sqrt(std::min(x.squaredL2Norm(), y.squaredL2Norm()));
return lhs <= rhs;
}
/**
* \brief Wrapper to remove const, volatile and reference of a type
*/
template <typename T>
using UnqualifiedType_t =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
/**
* \brief Helper to give types a name, this is used to print information during testing
*
* \tparam T type that should be given a name
* \tparam Dummy dummy, to be used to enable or disable specific specializations
*/
template <typename T, typename Dummy = void>
struct TypeName;
/**
* \brief specialization to specify a name for index_t
* \tparam T [const] [volatile] index_t[&] should be accepted
*/
template <typename T>
struct TypeName<T, std::enable_if_t<std::is_same_v<index_t, UnqualifiedType_t<T>>>> {
static constexpr char name[] = "index_t";
};
/**
* \brief specialization to specify a name for float
* \tparam T [const] [volatile] float[&] should be accepted
*/
template <typename T>
struct TypeName<T, std::enable_if_t<std::is_same_v<float, UnqualifiedType_t<T>>>> {
static constexpr char name[] = "float";
};
/**
* \brief specialization to specify a name for double
* \tparam T [const] [volatile] double[&] should be accepted
*/
template <typename T>
struct TypeName<T, std::enable_if_t<std::is_same_v<double, UnqualifiedType_t<T>>>> {
static constexpr char name[] = "double";
};
/**
* \brief specialization to specify a name for complex<float>
* \tparam T [const] [volatile] complex<float>[&] should be accepted
*/
template <typename T>
struct TypeName<T,
std::enable_if_t<std::is_same_v<std::complex<float>, UnqualifiedType_t<T>>>> {
static constexpr char name[] = "complex<float>";
};
/**
* \brief specialization to specify a name for complex<double>
* \tparam T [const] [volatile] complex<double>[&] should be accepted
*/
template <typename T>
struct TypeName<T,
std::enable_if_t<std::is_same_v<std::complex<double>, UnqualifiedType_t<T>>>> {
static constexpr char name[] = "complex<double>";
};
/**
* \brief Quick access to TypeName<UnqualifiedType>::name
* \tparam T a type
*/
template <typename T>
static constexpr auto TypeName_v = TypeName<UnqualifiedType_t<T>>::name;
} // namespace elsa
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment