296 lines · cpp
1//===-- error_test.cpp ----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of the ORC runtime.10//11// Note:12// This unit test was adapted from13// llvm/unittests/Support/ErrorTest.cpp14//15//===----------------------------------------------------------------------===//16 17#include "error.h"18#include "gtest/gtest.h"19 20using namespace orc_rt;21 22namespace {23 24class CustomError : public RTTIExtends<CustomError, ErrorInfoBase> {25public:26 CustomError(int V1) : V1(V1) {}27 std::string toString() const override {28 return "CustomError V1 = " + std::to_string(V1);29 }30 int getV1() const { return V1; }31 32protected:33 int V1;34};35 36class CustomSubError : public RTTIExtends<CustomSubError, CustomError> {37public:38 CustomSubError(int V1, std::string V2)39 : RTTIExtends<CustomSubError, CustomError>(V1), V2(std::move(V2)) {}40 std::string toString() const override {41 return "CustomSubError V1 = " + std::to_string(V1) + ", " + V2;42 }43 const std::string &getV2() const { return V2; }44 45protected:46 std::string V2;47};48 49} // end anonymous namespace50 51// Test that a checked success value doesn't cause any issues.52TEST(Error, CheckedSuccess) {53 Error E = Error::success();54 EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'";55}56 57// Check that a consumed success value doesn't cause any issues.58TEST(Error, ConsumeSuccess) { consumeError(Error::success()); }59 60TEST(Error, ConsumeError) {61 Error E = make_error<CustomError>(42);62 if (E) {63 consumeError(std::move(E));64 } else65 ADD_FAILURE() << "Error failure value should convert to true";66}67 68// Test that unchecked success values cause an abort.69TEST(Error, UncheckedSuccess) {70 EXPECT_DEATH({ Error E = Error::success(); },71 "Error must be checked prior to destruction")72 << "Unchecked Error Succes value did not cause abort()";73}74 75// Test that a checked but unhandled error causes an abort.76TEST(Error, CheckedButUnhandledError) {77 auto DropUnhandledError = []() {78 Error E = make_error<CustomError>(42);79 (void)!E;80 };81 EXPECT_DEATH(DropUnhandledError(),82 "Error must be checked prior to destruction")83 << "Unhandled Error failure value did not cause an abort()";84}85 86// Test that error_cast works as expected.87TEST(Error, BasicErrorCast) {88 {89 // Check casting base error value to base error type.90 auto E = make_error<CustomError>(42);91 if (auto CSE = error_cast<CustomSubError>(E)) {92 ADD_FAILURE() << "Derived cast incorrectly matched base error";93 } else if (auto CE = error_cast<CustomError>(E)) {94 EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value";95 } else96 ADD_FAILURE() << "Unexpected error value";97 }98 99 {100 // Check casting derived error value to base error type.101 auto E = make_error<CustomSubError>(42, "foo");102 if (auto CE = error_cast<CustomError>(E)) {103 EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value";104 } else105 ADD_FAILURE() << "Unexpected error value";106 }107 108 {109 // Check casting derived error value to derived error type.110 auto E = make_error<CustomSubError>(42, "foo");111 if (auto CSE = error_cast<CustomSubError>(E)) {112 EXPECT_EQ(CSE->getV1(), 42) << "Unexpected wrapped value";113 EXPECT_EQ(CSE->getV2(), "foo") << "Unexpected wrapped value";114 } else115 ADD_FAILURE() << "Unexpected error value";116 }117}118 119// ErrorAsOutParameter tester.120static void errAsOutParamHelper(Error &Err) {121 ErrorAsOutParameter ErrAsOutParam(&Err);122 // Verify that checked flag is raised - assignment should not crash.123 Err = Error::success();124 // Raise the checked bit manually - caller should still have to test the125 // error.126 (void)!!Err;127}128 129// Test that ErrorAsOutParameter sets the checked flag on construction.130TEST(Error, ErrorAsOutParameterChecked) {131 Error E = Error::success();132 errAsOutParamHelper(E);133 (void)!!E;134}135 136// Test that ErrorAsOutParameter clears the checked flag on destruction.137TEST(Error, ErrorAsOutParameterUnchecked) {138 EXPECT_DEATH(139 {140 Error E = Error::success();141 errAsOutParamHelper(E);142 },143 "Error must be checked prior to destruction")144 << "ErrorAsOutParameter did not clear the checked flag on destruction.";145}146 147// Check 'Error::isA<T>' method handling.148TEST(Error, IsAHandling) {149 // Check 'isA' handling.150 Error E = make_error<CustomError>(42);151 Error F = make_error<CustomSubError>(42, "foo");152 Error G = Error::success();153 154 EXPECT_TRUE(E.isA<CustomError>());155 EXPECT_FALSE(E.isA<CustomSubError>());156 EXPECT_TRUE(F.isA<CustomError>());157 EXPECT_TRUE(F.isA<CustomSubError>());158 EXPECT_FALSE(G.isA<CustomError>());159 160 consumeError(std::move(E));161 consumeError(std::move(F));162 consumeError(std::move(G));163}164 165TEST(Error, StringError) {166 auto E = make_error<StringError>("foo");167 if (auto SE = error_cast<StringError>(E)) {168 EXPECT_EQ(SE->toString(), "foo") << "Unexpected StringError value";169 } else170 ADD_FAILURE() << "Expected StringError value";171}172 173// Test Checked Expected<T> in success mode.174TEST(Error, CheckedExpectedInSuccessMode) {175 Expected<int> A = 7;176 EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'";177 // Access is safe in second test, since we checked the error in the first.178 EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value";179}180 181// Test Expected with reference type.182TEST(Error, ExpectedWithReferenceType) {183 int A = 7;184 Expected<int &> B = A;185 // 'Check' B.186 (void)!!B;187 int &C = *B;188 EXPECT_EQ(&A, &C) << "Expected failed to propagate reference";189}190 191// Test Unchecked Expected<T> in success mode.192// We expect this to blow up the same way Error would.193// Test runs in debug mode only.194TEST(Error, UncheckedExpectedInSuccessModeDestruction) {195 EXPECT_DEATH({ Expected<int> A = 7; },196 "Expected<T> must be checked before access or destruction.")197 << "Unchecekd Expected<T> success value did not cause an abort().";198}199 200// Test Unchecked Expected<T> in success mode.201// We expect this to blow up the same way Error would.202// Test runs in debug mode only.203TEST(Error, UncheckedExpectedInSuccessModeAccess) {204 EXPECT_DEATH(205 {206 Expected<int> A = 7;207 *A;208 },209 "Expected<T> must be checked before access or destruction.")210 << "Unchecekd Expected<T> success value did not cause an abort().";211}212 213// Test Unchecked Expected<T> in success mode.214// We expect this to blow up the same way Error would.215// Test runs in debug mode only.216TEST(Error, UncheckedExpectedInSuccessModeAssignment) {217 EXPECT_DEATH(218 {219 Expected<int> A = 7;220 A = 7;221 },222 "Expected<T> must be checked before access or destruction.")223 << "Unchecekd Expected<T> success value did not cause an abort().";224}225 226// Test Expected<T> in failure mode.227TEST(Error, ExpectedInFailureMode) {228 Expected<int> A = make_error<CustomError>(42);229 EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'";230 Error E = A.takeError();231 EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value";232 consumeError(std::move(E));233}234 235// Check that an Expected instance with an error value doesn't allow access to236// operator*.237// Test runs in debug mode only.238TEST(Error, AccessExpectedInFailureMode) {239 Expected<int> A = make_error<CustomError>(42);240 EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.")241 << "Incorrect Expected error value";242 consumeError(A.takeError());243}244 245// Check that an Expected instance with an error triggers an abort if246// unhandled.247// Test runs in debug mode only.248TEST(Error, UnhandledExpectedInFailureMode) {249 EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); },250 "Expected<T> must be checked before access or destruction.")251 << "Unchecked Expected<T> failure value did not cause an abort()";252}253 254// Test covariance of Expected.255TEST(Error, ExpectedCovariance) {256 class B {};257 class D : public B {};258 259 Expected<B *> A1(Expected<D *>(nullptr));260 // Check A1 by converting to bool before assigning to it.261 (void)!!A1;262 A1 = Expected<D *>(nullptr);263 // Check A1 again before destruction.264 (void)!!A1;265 266 Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr));267 // Check A2 by converting to bool before assigning to it.268 (void)!!A2;269 A2 = Expected<std::unique_ptr<D>>(nullptr);270 // Check A2 again before destruction.271 (void)!!A2;272}273 274// Test that the ExitOnError utility works as expected.275TEST(Error, CantFailSuccess) {276 cantFail(Error::success());277 278 int X = cantFail(Expected<int>(42));279 EXPECT_EQ(X, 42) << "Expected value modified by cantFail";280 281 int Dummy = 42;282 int &Y = cantFail(Expected<int &>(Dummy));283 EXPECT_EQ(&Dummy, &Y) << "Reference mangled by cantFail";284}285 286// Test that cantFail results in a crash if you pass it a failure value.287TEST(Error, CantFailDeath) {288 EXPECT_DEATH(cantFail(make_error<StringError>("foo")),289 "cantFail called on failure value")290 << "cantFail(Error) did not cause an abort for failure value";291 292 EXPECT_DEATH(cantFail(Expected<int>(make_error<StringError>("foo"))),293 "cantFail called on failure value")294 << "cantFail(Expected<int>) did not cause an abort for failure value";295}296