| // Copyright 2018 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <gtest/gtest.h> | |
| #include "common/RefCounted.h" | |
| #include "common/Result.h" | |
| namespace { | |
| template <typename T, typename E> | |
| void TestError(Result<T, E>* result, E expectedError) { | |
| EXPECT_TRUE(result->IsError()); | |
| EXPECT_FALSE(result->IsSuccess()); | |
| std::unique_ptr<E> storedError = result->AcquireError(); | |
| EXPECT_EQ(*storedError, expectedError); | |
| } | |
| template <typename T, typename E> | |
| void TestSuccess(Result<T, E>* result, T expectedSuccess) { | |
| EXPECT_FALSE(result->IsError()); | |
| EXPECT_TRUE(result->IsSuccess()); | |
| const T storedSuccess = result->AcquireSuccess(); | |
| EXPECT_EQ(storedSuccess, expectedSuccess); | |
| // Once the success is acquired, result has an empty | |
| // payload and is neither in the success nor error state. | |
| EXPECT_FALSE(result->IsError()); | |
| EXPECT_FALSE(result->IsSuccess()); | |
| } | |
| static int dummyError = 0xbeef; | |
| static float dummySuccess = 42.0f; | |
| static const float dummyConstSuccess = 42.0f; | |
| class AClass : public RefCounted { | |
| public: | |
| int a = 0; | |
| }; | |
| // Tests using the following overload of TestSuccess make | |
| // local Ref instances to dummySuccessObj. Tests should | |
| // ensure any local Ref objects made along the way continue | |
| // to point to dummySuccessObj. | |
| template <typename T, typename E> | |
| void TestSuccess(Result<Ref<T>, E>* result, T* expectedSuccess) { | |
| EXPECT_FALSE(result->IsError()); | |
| EXPECT_TRUE(result->IsSuccess()); | |
| // AClass starts with a reference count of 1 and stored | |
| // on the stack in the caller. The result parameter should | |
| // hold the only other reference to the object. | |
| EXPECT_EQ(expectedSuccess->GetRefCountForTesting(), 2u); | |
| const Ref<T> storedSuccess = result->AcquireSuccess(); | |
| EXPECT_EQ(storedSuccess.Get(), expectedSuccess); | |
| // Once the success is acquired, result has an empty | |
| // payload and is neither in the success nor error state. | |
| EXPECT_FALSE(result->IsError()); | |
| EXPECT_FALSE(result->IsSuccess()); | |
| // Once we call AcquireSuccess, result no longer stores | |
| // the object. storedSuccess should contain the only other | |
| // reference to the object. | |
| EXPECT_EQ(storedSuccess->GetRefCountForTesting(), 2u); | |
| } | |
| // Result<void, E*> | |
| // Test constructing an error Result<void, E> | |
| TEST(ResultOnlyPointerError, ConstructingError) { | |
| Result<void, int> result(std::make_unique<int>(dummyError)); | |
| TestError(&result, dummyError); | |
| } | |
| // Test moving an error Result<void, E> | |
| TEST(ResultOnlyPointerError, MovingError) { | |
| Result<void, int> result(std::make_unique<int>(dummyError)); | |
| Result<void, int> movedResult(std::move(result)); | |
| TestError(&movedResult, dummyError); | |
| } | |
| // Test returning an error Result<void, E> | |
| TEST(ResultOnlyPointerError, ReturningError) { | |
| auto CreateError = []() -> Result<void, int> { | |
| return {std::make_unique<int>(dummyError)}; | |
| }; | |
| Result<void, int> result = CreateError(); | |
| TestError(&result, dummyError); | |
| } | |
| // Test constructing a success Result<void, E> | |
| TEST(ResultOnlyPointerError, ConstructingSuccess) { | |
| Result<void, int> result; | |
| EXPECT_TRUE(result.IsSuccess()); | |
| EXPECT_FALSE(result.IsError()); | |
| } | |
| // Test moving a success Result<void, E> | |
| TEST(ResultOnlyPointerError, MovingSuccess) { | |
| Result<void, int> result; | |
| Result<void, int> movedResult(std::move(result)); | |
| EXPECT_TRUE(movedResult.IsSuccess()); | |
| EXPECT_FALSE(movedResult.IsError()); | |
| } | |
| // Test returning a success Result<void, E> | |
| TEST(ResultOnlyPointerError, ReturningSuccess) { | |
| auto CreateError = []() -> Result<void, int> { return {}; }; | |
| Result<void, int> result = CreateError(); | |
| EXPECT_TRUE(result.IsSuccess()); | |
| EXPECT_FALSE(result.IsError()); | |
| } | |
| // Result<T*, E*> | |
| // Test constructing an error Result<T*, E> | |
| TEST(ResultBothPointer, ConstructingError) { | |
| Result<float*, int> result(std::make_unique<int>(dummyError)); | |
| TestError(&result, dummyError); | |
| } | |
| // Test moving an error Result<T*, E> | |
| TEST(ResultBothPointer, MovingError) { | |
| Result<float*, int> result(std::make_unique<int>(dummyError)); | |
| Result<float*, int> movedResult(std::move(result)); | |
| TestError(&movedResult, dummyError); | |
| } | |
| // Test returning an error Result<T*, E> | |
| TEST(ResultBothPointer, ReturningError) { | |
| auto CreateError = []() -> Result<float*, int> { | |
| return {std::make_unique<int>(dummyError)}; | |
| }; | |
| Result<float*, int> result = CreateError(); | |
| TestError(&result, dummyError); | |
| } | |
| // Test constructing a success Result<T*, E> | |
| TEST(ResultBothPointer, ConstructingSuccess) { | |
| Result<float*, int> result(&dummySuccess); | |
| TestSuccess(&result, &dummySuccess); | |
| } | |
| // Test moving a success Result<T*, E> | |
| TEST(ResultBothPointer, MovingSuccess) { | |
| Result<float*, int> result(&dummySuccess); | |
| Result<float*, int> movedResult(std::move(result)); | |
| TestSuccess(&movedResult, &dummySuccess); | |
| } | |
| // Test returning a success Result<T*, E> | |
| TEST(ResultBothPointer, ReturningSuccess) { | |
| auto CreateSuccess = []() -> Result<float*, int*> { return {&dummySuccess}; }; | |
| Result<float*, int*> result = CreateSuccess(); | |
| TestSuccess(&result, &dummySuccess); | |
| } | |
| // Tests converting from a Result<TChild*, E> | |
| TEST(ResultBothPointer, ConversionFromChildClass) { | |
| struct T { | |
| int a; | |
| }; | |
| struct TChild : T {}; | |
| TChild child; | |
| T* childAsT = &child; | |
| { | |
| Result<T*, int> result(&child); | |
| TestSuccess(&result, childAsT); | |
| } | |
| { | |
| Result<TChild*, int> resultChild(&child); | |
| Result<T*, int> result(std::move(resultChild)); | |
| TestSuccess(&result, childAsT); | |
| } | |
| { | |
| Result<TChild*, int> resultChild(&child); | |
| Result<T*, int> result = std::move(resultChild); | |
| TestSuccess(&result, childAsT); | |
| } | |
| } | |
| // Result<const T*, E> | |
| // Test constructing an error Result<const T*, E> | |
| TEST(ResultBothPointerWithConstResult, ConstructingError) { | |
| Result<const float*, int> result(std::make_unique<int>(dummyError)); | |
| TestError(&result, dummyError); | |
| } | |
| // Test moving an error Result<const T*, E> | |
| TEST(ResultBothPointerWithConstResult, MovingError) { | |
| Result<const float*, int> result(std::make_unique<int>(dummyError)); | |
| Result<const float*, int> movedResult(std::move(result)); | |
| TestError(&movedResult, dummyError); | |
| } | |
| // Test returning an error Result<const T*, E*> | |
| TEST(ResultBothPointerWithConstResult, ReturningError) { | |
| auto CreateError = []() -> Result<const float*, int> { | |
| return {std::make_unique<int>(dummyError)}; | |
| }; | |
| Result<const float*, int> result = CreateError(); | |
| TestError(&result, dummyError); | |
| } | |
| // Test constructing a success Result<const T*, E*> | |
| TEST(ResultBothPointerWithConstResult, ConstructingSuccess) { | |
| Result<const float*, int> result(&dummyConstSuccess); | |
| TestSuccess(&result, &dummyConstSuccess); | |
| } | |
| // Test moving a success Result<const T*, E*> | |
| TEST(ResultBothPointerWithConstResult, MovingSuccess) { | |
| Result<const float*, int> result(&dummyConstSuccess); | |
| Result<const float*, int> movedResult(std::move(result)); | |
| TestSuccess(&movedResult, &dummyConstSuccess); | |
| } | |
| // Test returning a success Result<const T*, E*> | |
| TEST(ResultBothPointerWithConstResult, ReturningSuccess) { | |
| auto CreateSuccess = []() -> Result<const float*, int> { return {&dummyConstSuccess}; }; | |
| Result<const float*, int> result = CreateSuccess(); | |
| TestSuccess(&result, &dummyConstSuccess); | |
| } | |
| // Result<Ref<T>, E> | |
| // Test constructing an error Result<Ref<T>, E> | |
| TEST(ResultRefT, ConstructingError) { | |
| Result<Ref<AClass>, int> result(std::make_unique<int>(dummyError)); | |
| TestError(&result, dummyError); | |
| } | |
| // Test moving an error Result<Ref<T>, E> | |
| TEST(ResultRefT, MovingError) { | |
| Result<Ref<AClass>, int> result(std::make_unique<int>(dummyError)); | |
| Result<Ref<AClass>, int> movedResult(std::move(result)); | |
| TestError(&movedResult, dummyError); | |
| } | |
| // Test returning an error Result<Ref<T>, E> | |
| TEST(ResultRefT, ReturningError) { | |
| auto CreateError = []() -> Result<Ref<AClass>, int> { | |
| return {std::make_unique<int>(dummyError)}; | |
| }; | |
| Result<Ref<AClass>, int> result = CreateError(); | |
| TestError(&result, dummyError); | |
| } | |
| // Test constructing a success Result<Ref<T>, E> | |
| TEST(ResultRefT, ConstructingSuccess) { | |
| AClass success; | |
| Ref<AClass> refObj(&success); | |
| Result<Ref<AClass>, int> result(std::move(refObj)); | |
| TestSuccess(&result, &success); | |
| } | |
| // Test moving a success Result<Ref<T>, E> | |
| TEST(ResultRefT, MovingSuccess) { | |
| AClass success; | |
| Ref<AClass> refObj(&success); | |
| Result<Ref<AClass>, int> result(std::move(refObj)); | |
| Result<Ref<AClass>, int> movedResult(std::move(result)); | |
| TestSuccess(&movedResult, &success); | |
| } | |
| // Test returning a success Result<Ref<T>, E> | |
| TEST(ResultRefT, ReturningSuccess) { | |
| AClass success; | |
| auto CreateSuccess = [&success]() -> Result<Ref<AClass>, int> { | |
| return Ref<AClass>(&success); | |
| }; | |
| Result<Ref<AClass>, int> result = CreateSuccess(); | |
| TestSuccess(&result, &success); | |
| } | |
| class OtherClass { | |
| public: | |
| int a = 0; | |
| }; | |
| class Base : public RefCounted {}; | |
| class Child : public OtherClass, public Base {}; | |
| // Test constructing a Result<Ref<TChild>, E> | |
| TEST(ResultRefT, ConversionFromChildConstructor) { | |
| Child child; | |
| Ref<Child> refChild(&child); | |
| Result<Ref<Base>, int> result(std::move(refChild)); | |
| TestSuccess<Base>(&result, &child); | |
| } | |
| // Test copy constructing Result<Ref<TChild>, E> | |
| TEST(ResultRefT, ConversionFromChildCopyConstructor) { | |
| Child child; | |
| Ref<Child> refChild(&child); | |
| Result<Ref<Child>, int> resultChild(std::move(refChild)); | |
| Result<Ref<Base>, int> result(std::move(resultChild)); | |
| TestSuccess<Base>(&result, &child); | |
| } | |
| // Test assignment operator for Result<Ref<TChild>, E> | |
| TEST(ResultRefT, ConversionFromChildAssignmentOperator) { | |
| Child child; | |
| Ref<Child> refChild(&child); | |
| Result<Ref<Child>, int> resultChild(std::move(refChild)); | |
| Result<Ref<Base>, int> result = std::move(resultChild); | |
| TestSuccess<Base>(&result, &child); | |
| } | |
| // Result<T, E> | |
| // Test constructing an error Result<T, E> | |
| TEST(ResultGeneric, ConstructingError) { | |
| Result<std::vector<float>, int> result(std::make_unique<int>(dummyError)); | |
| TestError(&result, dummyError); | |
| } | |
| // Test moving an error Result<T, E> | |
| TEST(ResultGeneric, MovingError) { | |
| Result<std::vector<float>, int> result(std::make_unique<int>(dummyError)); | |
| Result<std::vector<float>, int> movedResult(std::move(result)); | |
| TestError(&movedResult, dummyError); | |
| } | |
| // Test returning an error Result<T, E> | |
| TEST(ResultGeneric, ReturningError) { | |
| auto CreateError = []() -> Result<std::vector<float>, int> { | |
| return {std::make_unique<int>(dummyError)}; | |
| }; | |
| Result<std::vector<float>, int> result = CreateError(); | |
| TestError(&result, dummyError); | |
| } | |
| // Test constructing a success Result<T, E> | |
| TEST(ResultGeneric, ConstructingSuccess) { | |
| Result<std::vector<float>, int> result({1.0f}); | |
| TestSuccess(&result, {1.0f}); | |
| } | |
| // Test moving a success Result<T, E> | |
| TEST(ResultGeneric, MovingSuccess) { | |
| Result<std::vector<float>, int> result({1.0f}); | |
| Result<std::vector<float>, int> movedResult(std::move(result)); | |
| TestSuccess(&movedResult, {1.0f}); | |
| } | |
| // Test returning a success Result<T, E> | |
| TEST(ResultGeneric, ReturningSuccess) { | |
| auto CreateSuccess = []() -> Result<std::vector<float>, int> { return {{1.0f}}; }; | |
| Result<std::vector<float>, int> result = CreateSuccess(); | |
| TestSuccess(&result, {1.0f}); | |
| } | |
| } // anonymous namespace |