summaryrefslogtreecommitdiffstats
path: root/llvm/unittests/Support
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/Support')
-rw-r--r--llvm/unittests/Support/CMakeLists.txt1
-rw-r--r--llvm/unittests/Support/ErrorTest.cpp458
2 files changed, 459 insertions, 0 deletions
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index e4e053f936b..053ca32fe9d 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -16,6 +16,7 @@ add_llvm_unittest(SupportTests
DwarfTest.cpp
EndianStreamTest.cpp
EndianTest.cpp
+ ErrorTest.cpp
ErrorOrTest.cpp
FileOutputBufferTest.cpp
IteratorTest.cpp
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
new file mode 100644
index 00000000000..3fd5e093dad
--- /dev/null
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -0,0 +1,458 @@
+//===----- 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 <memory>
+
+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.
+//
+// Expected tests:
+// - Expected<T> with T.
+// - Expected<T> with Error.
+// - Failure to handle an Expected<T> 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<CustomError> {
+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<CustomSubError, CustomError> {
+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<CustomError> CE) {
+ return Error();
+}
+
+static void handleCustomErrorUPVoid(std::unique_ptr<CustomError> 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<CustomError>(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<CustomError>(1);
+ Error F = make_error<CustomSubError>(1, 2);
+
+ EXPECT_TRUE(E.isA<CustomError>());
+ EXPECT_FALSE(E.isA<CustomSubError>());
+ EXPECT_TRUE(F.isA<CustomError>());
+ EXPECT_TRUE(F.isA<CustomSubError>());
+
+ consumeError(std::move(E));
+ consumeError(std::move(F));
+ }
+
+ // Check that we can handle a custom error.
+ {
+ int CaughtErrorInfo = 0;
+ handleAllErrors(make_error<CustomError>(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<Err>)
+ // void (unique_ptr<Err>)
+ // Error (unique_ptr<Err>) mutable
+ // void (unique_ptr<Err>) mutable
+
+ handleAllErrors(make_error<CustomError>(42), [](const CustomError &CE) {});
+
+ handleAllErrors(
+ make_error<CustomError>(42),
+ [](const CustomError &CE) mutable { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](const CustomError &CE) mutable {});
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](CustomError &CE) { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) {});
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](CustomError &CE) mutable { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) mutable {});
+
+ handleAllErrors(
+ make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) {});
+
+ handleAllErrors(
+ make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) mutable { return Error::success(); });
+
+ handleAllErrors(make_error<CustomError>(42),
+ [](std::unique_ptr<CustomError> CE) mutable {});
+
+ // Check that named handlers of type 'Error (const Err&)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomError);
+
+ // Check that named handlers of type 'void (const Err&)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomErrorVoid);
+
+ // Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomErrorUP);
+
+ // Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.
+ handleAllErrors(make_error<CustomError>(42), handleCustomErrorUPVoid);
+
+ // Check that we can handle a custom error with a custom base class.
+ {
+ int CaughtErrorInfo = 0;
+ int CaughtErrorExtraInfo = 0;
+ handleAllErrors(make_error<CustomSubError>(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<CustomSubError>(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<CustomSubError>(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<CustomError>(7),
+ make_error<CustomSubError>(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<CustomError>(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<CustomError>(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<CustomError>(7),
+ [&](std::unique_ptr<CustomSubError> 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<CustomError>(7),
+ [&](std::unique_ptr<CustomError> 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 Expected behavior.
+TEST(Error, CheckExpected) {
+
+ // Check that non-errors convert to 'true'.
+ {
+ Expected<int> 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<int> A = 7;
+ EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value";
+ }
+
+ // Check that errors convert to 'false'.
+ {
+ Expected<int> A = make_error<CustomError>(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<int> A = make_error<CustomError>(42);
+ Error E = A.takeError();
+ EXPECT_TRUE(E.isA<CustomError>()) << "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<int> A = make_error<CustomError>(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<int> A = make_error<CustomError>(42); },
+ "Program aborted due to an unhandled Error:")
+ << "Unchecked Expected<T> failure value did not cause an abort()";
+#endif
+
+ // Test covariance of Expected.
+ {
+ class B {};
+ class D : public B {};
+
+ Expected<B *> A1(Expected<D *>(nullptr));
+ A1 = Expected<D *>(nullptr);
+
+ Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr));
+ A2 = Expected<std::unique_ptr<D>>(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
OpenPOWER on IntegriCloud