//===-- error_test.cpp --sssssssss-----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of the ORC runtime. // // Note: // This unit test was adapted from // llvm/unittests/Support/ExtensibleRTTITest.cpp // //===----------------------------------------------------------------------===// #include "error.h" #include "gtest/gtest.h" using namespace __orc_rt; namespace { class CustomError : public RTTIExtends { public: CustomError(int V1) : V1(V1) {} std::string toString() const override { return "CustomError V1 = " + std::to_string(V1); } int getV1() const { return V1; } protected: int V1; }; class CustomSubError : public RTTIExtends { public: CustomSubError(int V1, std::string V2) : RTTIExtends(V1), V2(std::move(V2)) {} std::string toString() const override { return "CustomSubError V1 = " + std::to_string(V1) + ", " + V2; } const std::string &getV2() const { return V2; } protected: std::string V2; }; } // end anonymous namespace // Test that a checked success value doesn't cause any issues. TEST(Error, CheckedSuccess) { Error E = Error::success(); EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'"; } // Check that a consumed success value doesn't cause any issues. TEST(Error, ConsumeSuccess) { consumeError(Error::success()); } TEST(Error, ConsumeError) { Error E = make_error(42); if (E) { consumeError(std::move(E)); } else ADD_FAILURE() << "Error failure value should convert to true"; } // Test that unchecked success values cause an abort. TEST(Error, UncheckedSuccess) { EXPECT_DEATH({ Error E = Error::success(); }, "Error must be checked prior to destruction") << "Unchecked Error Succes value did not cause abort()"; } // Test that a checked but unhandled error causes an abort. TEST(Error, CheckedButUnhandledError) { auto DropUnhandledError = []() { Error E = make_error(42); (void)!E; }; EXPECT_DEATH(DropUnhandledError(), "Error must be checked prior to destruction") << "Unhandled Error failure value did not cause an abort()"; } // Test that error_cast works as expected. TEST(Error, BasicErrorCast) { { // Check casting base error value to base error type. auto E = make_error(42); if (auto CSE = error_cast(E)) { ADD_FAILURE() << "Derived cast incorrectly matched base error"; } else if (auto CE = error_cast(E)) { EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value"; } else ADD_FAILURE() << "Unexpected error value"; } { // Check casting derived error value to base error type. auto E = make_error(42, "foo"); if (auto CE = error_cast(E)) { EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value"; } else ADD_FAILURE() << "Unexpected error value"; } { // Check casting derived error value to derived error type. auto E = make_error(42, "foo"); if (auto CSE = error_cast(E)) { EXPECT_EQ(CSE->getV1(), 42) << "Unexpected wrapped value"; EXPECT_EQ(CSE->getV2(), "foo") << "Unexpected wrapped value"; } else ADD_FAILURE() << "Unexpected error value"; } } // ErrorAsOutParameter tester. static void errAsOutParamHelper(Error &Err) { ErrorAsOutParameter ErrAsOutParam(&Err); // Verify that checked flag is raised - assignment should not crash. Err = Error::success(); // Raise the checked bit manually - caller should still have to test the // error. (void)!!Err; } // Test that ErrorAsOutParameter sets the checked flag on construction. TEST(Error, ErrorAsOutParameterChecked) { Error E = Error::success(); errAsOutParamHelper(E); (void)!!E; } // Test that ErrorAsOutParameter clears the checked flag on destruction. TEST(Error, ErrorAsOutParameterUnchecked) { EXPECT_DEATH( { Error E = Error::success(); errAsOutParamHelper(E); }, "Error must be checked prior to destruction") << "ErrorAsOutParameter did not clear the checked flag on destruction."; } // Check 'Error::isA' method handling. TEST(Error, IsAHandling) { // Check 'isA' handling. Error E = make_error(42); Error F = make_error(42, "foo"); Error G = Error::success(); EXPECT_TRUE(E.isA()); EXPECT_FALSE(E.isA()); EXPECT_TRUE(F.isA()); EXPECT_TRUE(F.isA()); EXPECT_FALSE(G.isA()); consumeError(std::move(E)); consumeError(std::move(F)); consumeError(std::move(G)); } TEST(Error, StringError) { auto E = make_error("foo"); if (auto SE = error_cast(E)) { EXPECT_EQ(SE->toString(), "foo") << "Unexpected StringError value"; } else ADD_FAILURE() << "Expected StringError value"; } // Test Checked Expected in success mode. TEST(Error, CheckedExpectedInSuccessMode) { Expected A = 7; EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'"; // Access is safe in second test, since we checked the error in the first. EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value"; } // Test Expected with reference type. TEST(Error, ExpectedWithReferenceType) { int A = 7; Expected B = A; // 'Check' B. (void)!!B; int &C = *B; EXPECT_EQ(&A, &C) << "Expected failed to propagate reference"; } // Test Unchecked Expected in success mode. // We expect this to blow up the same way Error would. // Test runs in debug mode only. TEST(Error, UncheckedExpectedInSuccessModeDestruction) { EXPECT_DEATH({ Expected A = 7; }, "Expected must be checked before access or destruction.") << "Unchecekd Expected success value did not cause an abort()."; } // Test Unchecked Expected in success mode. // We expect this to blow up the same way Error would. // Test runs in debug mode only. TEST(Error, UncheckedExpectedInSuccessModeAccess) { EXPECT_DEATH( { Expected A = 7; *A; }, "Expected must be checked before access or destruction.") << "Unchecekd Expected success value did not cause an abort()."; } // Test Unchecked Expected in success mode. // We expect this to blow up the same way Error would. // Test runs in debug mode only. TEST(Error, UncheckedExpectedInSuccessModeAssignment) { EXPECT_DEATH( { Expected A = 7; A = 7; }, "Expected must be checked before access or destruction.") << "Unchecekd Expected success value did not cause an abort()."; } // Test Expected in failure mode. TEST(Error, ExpectedInFailureMode) { Expected A = make_error(42); EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'"; Error E = A.takeError(); EXPECT_TRUE(E.isA()) << "Incorrect Expected error value"; consumeError(std::move(E)); } // Check that an Expected instance with an error value doesn't allow access to // operator*. // Test runs in debug mode only. TEST(Error, AccessExpectedInFailureMode) { Expected A = make_error(42); EXPECT_DEATH(*A, "Expected must be checked before access or destruction.") << "Incorrect Expected error value"; consumeError(A.takeError()); } // Check that an Expected instance with an error triggers an abort if // unhandled. // Test runs in debug mode only. TEST(Error, UnhandledExpectedInFailureMode) { EXPECT_DEATH({ Expected A = make_error(42); }, "Expected must be checked before access or destruction.") << "Unchecked Expected failure value did not cause an abort()"; } // Test covariance of Expected. TEST(Error, ExpectedCovariance) { class B {}; class D : public B {}; Expected A1(Expected(nullptr)); // Check A1 by converting to bool before assigning to it. (void)!!A1; A1 = Expected(nullptr); // Check A1 again before destruction. (void)!!A1; Expected> A2(Expected>(nullptr)); // Check A2 by converting to bool before assigning to it. (void)!!A2; A2 = Expected>(nullptr); // Check A2 again before destruction. (void)!!A2; } // Test that the ExitOnError utility works as expected. TEST(Error, CantFailSuccess) { cantFail(Error::success()); int X = cantFail(Expected(42)); EXPECT_EQ(X, 42) << "Expected value modified by cantFail"; int Dummy = 42; int &Y = cantFail(Expected(Dummy)); EXPECT_EQ(&Dummy, &Y) << "Reference mangled by cantFail"; } // Test that cantFail results in a crash if you pass it a failure value. TEST(Error, CantFailDeath) { EXPECT_DEATH(cantFail(make_error("foo")), "cantFail called on failure value") << "cantFail(Error) did not cause an abort for failure value"; EXPECT_DEATH(cantFail(Expected(make_error("foo"))), "cantFail called on failure value") << "cantFail(Expected) did not cause an abort for failure value"; }