/
githubmirror
/
carbon-lang
Обзор
Документация
Войти
/
githubmirror
/
carbon-lang
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
trunk
common/error_test.cpp
272 строки
8 KB
Chandler Carruth
Rework the `IsSuccess` matcher to be fully polymorphic (#5981)
25 авг 2025, 19:28
Не верифицирован
25 авг 2025, 19:28
74016d4
Код
Авторство
О чём код?
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/error.h" #include <gtest/gtest.h> #include <concepts> #include <memory> #include "common/error_test_helpers.h" #include "common/raw_string_ostream.h" namespace Carbon { namespace { using ::Carbon::Testing::IsError; using ::Carbon::Testing::IsSuccess; using ::testing::_; using ::testing::Eq; using ::testing::VariantWith; TEST(ErrorTest, Error) { Error err("test"); EXPECT_EQ(err.message(), "test"); } TEST(ErrorTest, ErrorEmptyString) { ASSERT_DEATH({ Error err(""); }, "CHECK failure at"); } auto IndirectError() -> Error { return Error("test"); } TEST(ErrorTest, IndirectError) { EXPECT_EQ(IndirectError().message(), "test"); } TEST(ErrorTest, ErrorBuilderOperatorImplicitCast) { ErrorOr<int> result = ErrorBuilder() << "msg"; EXPECT_THAT(result, IsError("msg")); } // Make sure a custom error type can be forward declared and used with `ErrorOr` // until the `ErrorOr` is required to be complete itself. class CustomError; auto TestFunction() -> ErrorOr<int, CustomError>; class CustomError : public ErrorBase<CustomError> { public: auto Print(llvm::raw_ostream& os) const -> void { os << "Custom test error!"; } }; auto TestFunction() -> ErrorOr<int, CustomError> { return CustomError(); } TEST(ErrorTest, UseErrorOrWithCustomError) { // Uses `TestFunction` to ensure it compiles correctly with forward // declarations above. EXPECT_THAT(TestFunction(), IsError("Custom test error!")); } template <typename ErrorT> class ErrorOrTest : public ::testing::Test { public: auto ErrorStr() -> std::string { if constexpr (std::same_as<ErrorT, Error>) { return "test error"; } else if constexpr (std::same_as<ErrorT, CustomError>) { return CustomError().ToString(); } else { static_assert(false, "Unsupported custom error type!"); } } auto MakeError() -> ErrorT { if constexpr (std::same_as<ErrorT, Error>) { return Error("test error"); } else if constexpr (std::same_as<ErrorT, CustomError>) { return CustomError(); } else { static_assert(false, "Unsupported custom error type!"); } } }; using ErrorOrTestParams = ::testing::Types<Error, CustomError>; TYPED_TEST_SUITE(ErrorOrTest, ErrorOrTestParams); TYPED_TEST(ErrorOrTest, ErrorOr) { using TestErrorOr = ErrorOr<int, TypeParam>; TestErrorOr err(this->MakeError()); EXPECT_THAT(err, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, ErrorOrValue) { using TestErrorOr = ErrorOr<int, TypeParam>; EXPECT_TRUE(TestErrorOr(0).ok()); } template <typename ErrorT, typename Fixture> auto IndirectErrorOrTest(Fixture& fixture) -> ErrorOr<int, ErrorT> { return fixture.MakeError(); } TYPED_TEST(ErrorOrTest, IndirectErrorOr) { EXPECT_FALSE(IndirectErrorOrTest<TypeParam>(*this).ok()); } struct Val { int val; }; TYPED_TEST(ErrorOrTest, ErrorOrArrowOp) { using TestErrorOr = ErrorOr<Val, TypeParam>; TestErrorOr err({1}); EXPECT_EQ(err->val, 1); } TYPED_TEST(ErrorOrTest, ErrorOrReference) { using TestErrorOr = ErrorOr<Val&, TypeParam>; Val val = {1}; TestErrorOr maybe_val(val); EXPECT_EQ(maybe_val->val, 1); } template <typename ErrorT> auto IndirectErrorOrSuccessTest() -> ErrorOr<Success, ErrorT> { return Success(); } TYPED_TEST(ErrorOrTest, IndirectErrorOrSuccess) { EXPECT_TRUE(IndirectErrorOrSuccessTest<TypeParam>().ok()); } TYPED_TEST(ErrorOrTest, MoveValue) { using TestErrorOr = ErrorOr<std::unique_ptr<int>, TypeParam>; auto make_value = []() -> TestErrorOr { return std::make_unique<int>(42); }; std::unique_ptr<int> p = *make_value(); EXPECT_THAT(*p, Eq(42)); auto result = make_value(); std::unique_ptr<int> p2 = *std::move(result); EXPECT_THAT(*p2, Eq(42)); } TYPED_TEST(ErrorOrTest, UnprintableValue) { struct X { int i; }; using TestErrorOr = ErrorOr<X, TypeParam>; TestErrorOr value(X{.i = 42}); EXPECT_THAT(value, IsSuccess(_)); TestErrorOr error = this->MakeError(); EXPECT_THAT(error, IsError(this->ErrorStr())); } // Note that this is more of a test of `IsSuccess` than `ErrorOr` itself. TYPED_TEST(ErrorOrTest, NestedMatching) { using TestErrorOr = ErrorOr<std::variant<int, float>, TypeParam>; TestErrorOr i(42); EXPECT_THAT(i, IsSuccess(VariantWith<int>(Eq(42)))); TestErrorOr f(0.42F); EXPECT_THAT(f, IsSuccess(VariantWith<float>(Eq(0.42F)))); } TYPED_TEST(ErrorOrTest, ReturnIfErrorNoError) { using TestErrorOr = ErrorOr<Success, TypeParam>; auto result = []() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); return Success(); }(); EXPECT_TRUE(result.ok()); } TYPED_TEST(ErrorOrTest, ReturnIfErrorHasError) { using TestErrorOr = ErrorOr<Success, TypeParam>; auto result = [this]() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); CARBON_RETURN_IF_ERROR(TestErrorOr(this->MakeError())); return Success(); }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, AssignOrReturnNoError) { using TestErrorOr = ErrorOr<int, TypeParam>; auto result = []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, TestErrorOr(2)); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); EXPECT_THAT(result, IsSuccess(Eq(6))); } TYPED_TEST(ErrorOrTest, AssignOrReturnHasDirectError) { using TestErrorOr = ErrorOr<int, TypeParam>; auto result = [this]() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(this->MakeError())); return 0; }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, AssignOrReturnHasErrorInExpected) { using TestErrorOr = ErrorOr<int, TypeParam>; auto result = [this]() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(this->MakeError())); return a; }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } class AnotherCustomError : public ErrorBase<AnotherCustomError> { public: auto Print(llvm::raw_ostream& os) const -> void { os << "Another custom test error!"; } explicit operator CustomError() { return CustomError(); } }; TYPED_TEST(ErrorOrTest, AssignOrReturnNoErrorAcrossErrorTypes) { using TestErrorOr = ErrorOr<int, TypeParam>; auto result = []() -> ErrorOr<int> { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int inner, (ErrorOr<int, AnotherCustomError>(2))); return inner; }()); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); EXPECT_THAT(result, IsSuccess(Eq(6))); } TYPED_TEST(ErrorOrTest, AssignOrReturnErrorAcrossErrorTypes) { using TestErrorOr = ErrorOr<int, TypeParam>; auto result = []() -> ErrorOr<int> { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN( int inner, (ErrorOr<int, AnotherCustomError>(AnotherCustomError()))); return inner; }()); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); // When directly using the `Error` type, the explicit custom type above has // its message preserved. When testing against `CustomError`, that one // overrides the message. if constexpr (std::same_as<TypeParam, Error>) { EXPECT_THAT(result, IsError("Another custom test error!")); } else { EXPECT_THAT(result, IsError("Custom test error!")); } } } // namespace } // namespace Carbon