diff --git a/docker/build/assets.Dockerfile b/docker/build/assets.Dockerfile index 8f14d77342..7a5c71765c 100644 --- a/docker/build/assets.Dockerfile +++ b/docker/build/assets.Dockerfile @@ -271,9 +271,7 @@ RUN if [ ! -x "$(command -v nvidia-smi)" ] || [ -z "$(nvidia-smi | egrep -o "CUD # Removing gcc packages remove the CUDA toolkit since it depends on them source /cuda-quantum/scripts/configure_build.sh install-cudart; \ fi && cd /cuda-quantum && \ - # FIXME: Tensor unit tests for runtime errors throw a different exception. - # Issue: https://github.com/NVIDIA/cuda-quantum/issues/2321 - excludes+=" --exclude-regex ctest-nvqpp|ctest-targettests|Tensor.*Error" && \ + excludes+=" --exclude-regex ctest-nvqpp|ctest-targettests" && \ ctest --output-on-failure --test-dir build $excludes ENV PATH="${PATH}:/usr/local/cuda/bin" diff --git a/unittests/utils/Tensor.cpp b/unittests/utils/Tensor.cpp index 1493cc8709..530af7da4b 100644 --- a/unittests/utils/Tensor.cpp +++ b/unittests/utils/Tensor.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/utils/tensor.h" +#include "exceptions.h" #include "gtest/gtest.h" TEST(Tensor, initialization) { @@ -27,8 +28,8 @@ TEST(Tensor, initialization) { TEST(Tensor, initializationError) { { - EXPECT_THROW(cudaq::matrix_2 m1({1., 2., 3., 4., 5.}, {2, 3}), - std::runtime_error); + EXPECT_THROW_MIXED_STDLIB(cudaq::matrix_2 m1({1., 2., 3., 4., 5.}, {2, 3}), + std::runtime_error); } } @@ -61,22 +62,22 @@ TEST(Tensor, accessError) { { cudaq::matrix_2 m0; - EXPECT_THROW((m0[{0}]), std::runtime_error); - EXPECT_THROW((m0[{0, 1}]), std::runtime_error); - EXPECT_THROW((m0[{0, 0, 0}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m0[{0}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m0[{0, 1}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m0[{0, 0, 0}]), std::runtime_error); } { cudaq::matrix_2 m1({1., 0., 0., 1.}); - EXPECT_THROW((m1[{0, 2}]), std::runtime_error); - EXPECT_THROW((m1[{0, 0, 0}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m1[{0, 2}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m1[{0, 0, 0}]), std::runtime_error); } { cudaq::matrix_2 m1({1., 2., 3., 4., 5., 6.}, {2, 3}); - EXPECT_THROW((m1[{0, 3}]), std::runtime_error); - EXPECT_THROW((m1[{2, 1}]), std::runtime_error); - EXPECT_THROW((m1[{0, 2, 3}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m1[{0, 3}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m1[{2, 1}]), std::runtime_error); + EXPECT_THROW_MIXED_STDLIB((m1[{0, 2, 3}]), std::runtime_error); } } @@ -100,7 +101,7 @@ TEST(Tensor, productError) { { cudaq::matrix_2 m2({2., 1., 3., 4.}); cudaq::matrix_2 m3({1., 2., 3., 4., 5., 6.}, {3, 2}); - EXPECT_THROW(m2 * m3, std::runtime_error); + EXPECT_THROW_MIXED_STDLIB(m2 * m3, std::runtime_error); } } @@ -117,7 +118,7 @@ TEST(Tensor, additionError) { { cudaq::matrix_2 m5({2., 1., 3., 4.}); cudaq::matrix_2 m6({1., 2., 3., 4., 5., 6.}, {3, 2}); - EXPECT_THROW(m5 + m6, std::runtime_error); + EXPECT_THROW_MIXED_STDLIB(m5 + m6, std::runtime_error); } } @@ -133,7 +134,7 @@ TEST(Tensor, subtractionError) { { cudaq::matrix_2 m8({2., 1., 3., 4.}); cudaq::matrix_2 m9({1., 2., 3., 4., 5., 6.}, {3, 2}); - EXPECT_THROW(m8 - m9, std::runtime_error); + EXPECT_THROW_MIXED_STDLIB(m8 - m9, std::runtime_error); } } diff --git a/unittests/utils/exceptions.h b/unittests/utils/exceptions.h new file mode 100644 index 0000000000..0cb891a5d5 --- /dev/null +++ b/unittests/utils/exceptions.h @@ -0,0 +1,59 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +/// Additional test API that can catch and verify exceptions in code compiled +/// with `libstdc++` that were thrown in code compiled with `libc++`. +#include "gtest/gtest.h" +#define GTEST_TEST_THROW_MIXED_STDLIB(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const &) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + std::string exType = __cxxabiv1::__cxa_current_exception_type()->name(); \ + auto demangledPtr = __cxxabiv1::__cxa_demangle(exType.c_str(), nullptr, \ + nullptr, nullptr); \ + if (demangledPtr) { \ + std::string demangledName(demangledPtr); \ + if (demangledName == #expected_exception) { \ + gtest_caught_expected = true; \ + } else { \ + gtest_msg.value = \ + "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type: " + \ + demangledName; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else { \ + gtest_msg.value = \ + "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual (cannot demangle): it throws a different type: " + \ + exType; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#define EXPECT_THROW_MIXED_STDLIB(statement, expected_exception) \ + GTEST_TEST_THROW_MIXED_STDLIB(statement, expected_exception, \ + GTEST_NONFATAL_FAILURE_)