//===----- unittests/ErrorTest.cpp - Error.h tests ------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/Support/Error.h" #include "llvm/Support/Errc.h" #include "gtest/gtest.h" #include using namespace llvm; namespace { // Test: // // Constructing success. // - Silent acceptance of tested success. // - Programmatic error on untested success. // // Custom error class. // - Creation of a custom error class with a default base class. // - Handling of a custom error class with a default base class. // - Handler type deduction. // - Failure to handle a custom error class. // - Isa testing of a custom error class. // // - Creation of a custom error class with a custom base class. // - Handling of a custom error class with a default base class. // - Correct shadowing of handlers. // // Utility functions: // - join_errors to defer errors. // - consume_error to consume a "safe" error without any output. // - handleAllUnhandledErrors to assert that all errors are handled. // - logAllUnhandledErrors to log errors to a stream. // - ExitOnError tests. // // Expected tests: // - Expected with T. // - Expected with Error. // - Failure to handle an Expected in failure mode. // - Error extraction (Expected -> Error). // // std::error_code: // - std::error_code to Error in success mode. // - std::error_code to Error (ECError) in failure mode. // - Error to std::error_code in success mode. // - Error (ECError) to std::error_code in failure mode. // // Custom error class with a default base class and some random 'info' attached. class CustomError : public ErrorInfo { public: // Create an error with some info attached. CustomError(int Info) : Info(Info) {} // Get the info attached to this error. int getInfo() const { return Info; } // Log this error to a stream. void log(raw_ostream &OS) const override { OS << "CustomError { " << getInfo() << "}"; } protected: // This error is subclassed below, but we can't use inheriting constructors // yet, so we can't propagate the constructors through ErrorInfo. Instead // we have to have a default constructor and have the subclass initialize all // fields. CustomError() : Info(0) {} int Info; }; // Custom error class with a custom base class and some additional random // 'info'. class CustomSubError : public ErrorInfo { public: // Create a sub-error with some info attached. CustomSubError(int Info, int ExtraInfo) : ExtraInfo(ExtraInfo) { this->Info = Info; } // Get the extra info attached to this error. int getExtraInfo() const { return ExtraInfo; } // Log this error to a stream. void log(raw_ostream &OS) const override { OS << "CustomSubError { " << getInfo() << ", " << getExtraInfo() << "}"; } protected: int ExtraInfo; }; // Verify that success values that are checked (i.e. cast to 'bool') are // destructed without error, and that unchecked success values cause an // abort. TEST(Error, CheckSuccess) { // Test checked success. { Error E; EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'"; } // Test unchecked success. // Test runs in debug mode only. #ifndef NDEBUG { auto DropUncheckedSuccess = []() { Error E; }; EXPECT_DEATH(DropUncheckedSuccess(), "Program aborted due to an unhandled Error:") << "Unchecked Error Succes value did not cause abort()"; } #endif } static Error handleCustomError(const CustomError &CE) { return Error(); } static void handleCustomErrorVoid(const CustomError &CE) {} static Error handleCustomErrorUP(std::unique_ptr CE) { return Error(); } static void handleCustomErrorUPVoid(std::unique_ptr CE) {} // Verify creation and handling of custom error classes. TEST(Error, CheckCustomErrors) { // Check that we abort on unhandled failure cases. (Force conversion to bool // to make sure that we don't accidentally treat checked errors as handled). // Test runs in debug mode only. #ifndef NDEBUG { auto DropUnhandledError = []() { Error E = make_error(42); (void)!E; }; EXPECT_DEATH(DropUnhandledError(), "Program aborted due to an unhandled Error:") << "Unhandled Error failure value did not cause abort()"; } #endif // Check 'isA' handling. { Error E = make_error(1); Error F = make_error(1, 2); 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)); } // Check that we can handle a custom error. { int CaughtErrorInfo = 0; handleAllErrors(make_error(42), [&](const CustomError &CE) { CaughtErrorInfo = CE.getInfo(); }); EXPECT_TRUE(CaughtErrorInfo == 42) << "Wrong result from CustomError handler"; } // Check that handler type deduction also works for handlers // of the following types: // void (const Err&) // Error (const Err&) mutable // void (const Err&) mutable // Error (Err&) // void (Err&) // Error (Err&) mutable // void (Err&) mutable // Error (unique_ptr) // void (unique_ptr) // Error (unique_ptr) mutable // void (unique_ptr) mutable handleAllErrors(make_error(42), [](const CustomError &CE) {}); handleAllErrors( make_error(42), [](const CustomError &CE) mutable { return Error::success(); }); handleAllErrors(make_error(42), [](const CustomError &CE) mutable {}); handleAllErrors(make_error(42), [](CustomError &CE) { return Error::success(); }); handleAllErrors(make_error(42), [](CustomError &CE) {}); handleAllErrors(make_error(42), [](CustomError &CE) mutable { return Error::success(); }); handleAllErrors(make_error(42), [](CustomError &CE) mutable {}); handleAllErrors( make_error(42), [](std::unique_ptr CE) { return Error::success(); }); handleAllErrors(make_error(42), [](std::unique_ptr CE) {}); handleAllErrors( make_error(42), [](std::unique_ptr CE) mutable { return Error::success(); }); handleAllErrors(make_error(42), [](std::unique_ptr CE) mutable {}); // Check that named handlers of type 'Error (const Err&)' work. handleAllErrors(make_error(42), handleCustomError); // Check that named handlers of type 'void (const Err&)' work. handleAllErrors(make_error(42), handleCustomErrorVoid); // Check that named handlers of type 'Error (std::unique_ptr)' work. handleAllErrors(make_error(42), handleCustomErrorUP); // Check that named handlers of type 'Error (std::unique_ptr)' work. handleAllErrors(make_error(42), handleCustomErrorUPVoid); // Check that we can handle a custom error with a custom base class. { int CaughtErrorInfo = 0; int CaughtErrorExtraInfo = 0; handleAllErrors(make_error(42, 7), [&](const CustomSubError &SE) { CaughtErrorInfo = SE.getInfo(); CaughtErrorExtraInfo = SE.getExtraInfo(); }); EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7) << "Wrong result from CustomSubError handler"; } // Check that we trigger only the first handler that applies. { int DummyInfo = 0; int CaughtErrorInfo = 0; int CaughtErrorExtraInfo = 0; handleAllErrors(make_error(42, 7), [&](const CustomSubError &SE) { CaughtErrorInfo = SE.getInfo(); CaughtErrorExtraInfo = SE.getExtraInfo(); }, [&](const CustomError &CE) { DummyInfo = CE.getInfo(); }); EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7 && DummyInfo == 0) << "Activated the wrong Error handler(s)"; } // Check that general handlers shadow specific ones. { int CaughtErrorInfo = 0; int DummyInfo = 0; int DummyExtraInfo = 0; handleAllErrors( make_error(42, 7), [&](const CustomError &CE) { CaughtErrorInfo = CE.getInfo(); }, [&](const CustomSubError &SE) { DummyInfo = SE.getInfo(); DummyExtraInfo = SE.getExtraInfo(); }); EXPECT_TRUE(CaughtErrorInfo = 42 && DummyInfo == 0 && DummyExtraInfo == 0) << "General Error handler did not shadow specific handler"; } } // Test utility functions. TEST(Error, CheckErrorUtilities) { // Test joinErrors { int CustomErrorInfo1 = 0; int CustomErrorInfo2 = 0; int CustomErrorExtraInfo = 0; Error E = joinErrors(make_error(7), make_error(42, 7)); handleAllErrors(std::move(E), [&](const CustomSubError &SE) { CustomErrorInfo2 = SE.getInfo(); CustomErrorExtraInfo = SE.getExtraInfo(); }, [&](const CustomError &CE) { // Assert that the CustomError instance above is handled // before the // CustomSubError - joinErrors should preserve error // ordering. EXPECT_EQ(CustomErrorInfo2, 0) << "CustomErrorInfo2 should be 0 here. " "joinErrors failed to preserve ordering.\n"; CustomErrorInfo1 = CE.getInfo(); }); EXPECT_TRUE(CustomErrorInfo1 == 7 && CustomErrorInfo2 == 42 && CustomErrorExtraInfo == 7) << "Failed handling compound Error."; } // Test consumeError for both success and error cases. { Error E; consumeError(std::move(E)); } { Error E = make_error(7); consumeError(std::move(E)); } // Test that handleAllUnhandledErrors crashes if an error is not caught. // Test runs in debug mode only. #ifndef NDEBUG { auto FailToHandle = []() { handleAllErrors(make_error(7), [&](const CustomSubError &SE) { errs() << "This should never be called"; exit(1); }); }; EXPECT_DEATH(FailToHandle(), "Program aborted due to an unhandled Error:") << "Unhandled Error in handleAllErrors call did not cause an " "abort()"; } #endif // Test that handleAllUnhandledErrors crashes if an error is returned from a // handler. // Test runs in debug mode only. #ifndef NDEBUG { auto ReturnErrorFromHandler = []() { handleAllErrors(make_error(7), [&](std::unique_ptr SE) { return Error(std::move(SE)); }); }; EXPECT_DEATH(ReturnErrorFromHandler(), "Program aborted due to an unhandled Error:") << " Error returned from handler in handleAllErrors call did not " "cause abort()"; } #endif // Test that we can return values from handleErrors. { int ErrorInfo = 0; Error E = handleErrors( make_error(7), [&](std::unique_ptr CE) { return Error(std::move(CE)); }); handleAllErrors(std::move(E), [&](const CustomError &CE) { ErrorInfo = CE.getInfo(); }); EXPECT_EQ(ErrorInfo, 7) << "Failed to handle Error returned from handleErrors."; } // Test ExitOnError { ExitOnError ExitOnErr; ExitOnErr.setBanner("Error in tool:"); ExitOnErr.setExitCodeMapper( [](const Error &E) { if (E.isA()) return 2; return 1; }); // Make sure we don't bail on success. ExitOnErr(Error::success()); EXPECT_EQ(ExitOnErr(Expected(7)), 7) << "exitOnError returned an invalid value for Expected"; // Exit tests. EXPECT_EXIT(ExitOnErr(make_error(7)), ::testing::ExitedWithCode(1), "Error in tool:") << "exitOnError returned an unexpected error result"; EXPECT_EXIT(ExitOnErr(Expected(make_error(0, 0))), ::testing::ExitedWithCode(2), "Error in tool:") << "exitOnError returned an unexpected error result"; } } // Test Expected behavior. TEST(Error, CheckExpected) { // Check that non-errors convert to 'true'. { Expected A = 7; EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'"; } // Check that non-error values are accessible via operator*. { Expected A = 7; EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value"; } // Check that errors convert to 'false'. { Expected A = make_error(42); EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'"; consumeError(A.takeError()); } // Check that error values are accessible via takeError(). { Expected A = make_error(42); 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. #ifndef NDEBUG { Expected A = make_error(42); EXPECT_DEATH(*A, "!HasError && \"Cannot get value when an error exists!\"") << "Incorrect Expected error value"; consumeError(A.takeError()); } #endif // Check that an Expected instance with an error triggers an abort if // unhandled. // Test runs in debug mode only. #ifndef NDEBUG EXPECT_DEATH({ Expected A = make_error(42); }, "Program aborted due to an unhandled Error:") << "Unchecked Expected failure value did not cause an abort()"; #endif // Test covariance of Expected. { class B {}; class D : public B {}; Expected A1(Expected(nullptr)); A1 = Expected(nullptr); Expected> A2(Expected>(nullptr)); A2 = Expected>(nullptr); } } TEST(Error, ECError) { // Round-trip a success value to check that it converts correctly. EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())), std::error_code()) << "std::error_code() should round-trip via Error conversions"; // Round-trip an error value to check that it converts correctly. EXPECT_EQ(errorToErrorCode(errorCodeToError(errc::invalid_argument)), errc::invalid_argument) << "std::error_code error value should round-trip via Error " "conversions"; } } // end anon namespace