Add Ref<T> specialization for Result

Ref<T> specialization will allow us to, in a future change, return
Result<Ref<T>> instances from Create methods while still keeping the
tagged pointer optimization.

Change-Id: I20c764358af22ba1dc53458d59b0b2b4770a0c6a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19801
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Rafael Cintron <rafael.cintron@microsoft.com>
This commit is contained in:
Rafael Cintron
2020-04-21 17:27:00 +00:00
committed by Commit Bot service account
parent fee2783cb0
commit f204cf2c4f
2 changed files with 189 additions and 13 deletions

View File

@@ -14,32 +14,71 @@
#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) {
ASSERT_TRUE(result->IsError());
ASSERT_FALSE(result->IsSuccess());
EXPECT_TRUE(result->IsError());
EXPECT_FALSE(result->IsSuccess());
std::unique_ptr<E> storedError = result->AcquireError();
ASSERT_EQ(*storedError, expectedError);
EXPECT_EQ(*storedError, expectedError);
}
template<typename T, typename E>
void TestSuccess(Result<T, E>* result, T expectedSuccess) {
ASSERT_FALSE(result->IsError());
ASSERT_TRUE(result->IsSuccess());
EXPECT_FALSE(result->IsError());
EXPECT_TRUE(result->IsSuccess());
T storedSuccess = result->AcquireSuccess();
ASSERT_EQ(storedSuccess, expectedSuccess);
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>
@@ -66,16 +105,16 @@ TEST(ResultOnlyPointerError, ReturningError) {
// Test constructing a success Result<void, E>
TEST(ResultOnlyPointerError, ConstructingSuccess) {
Result<void, int> result;
ASSERT_TRUE(result.IsSuccess());
ASSERT_FALSE(result.IsError());
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));
ASSERT_TRUE(movedResult.IsSuccess());
ASSERT_FALSE(movedResult.IsError());
EXPECT_TRUE(movedResult.IsSuccess());
EXPECT_FALSE(movedResult.IsError());
}
// Test returning a success Result<void, E>
@@ -83,8 +122,8 @@ TEST(ResultOnlyPointerError, ReturningSuccess) {
auto CreateError = []() -> Result<void, int> { return {}; };
Result<void, int> result = CreateError();
ASSERT_TRUE(result.IsSuccess());
ASSERT_FALSE(result.IsError());
EXPECT_TRUE(result.IsSuccess());
EXPECT_FALSE(result.IsError());
}
// Result<T*, E*>
@@ -204,6 +243,59 @@ TEST(ResultBothPointerWithConstResult, ReturningSuccess) {
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);
}
// Result<T, E>
// Test constructing an error Result<T, E>