mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-26 13:35:55 +00:00
The way in which the Result class is used in Dawn can be fragile with respect to memory management because the caller of AcquireError must know they need to delete the returned pointer or a memory leak will occur. We've had a couple of instances where developers have accidentally left out the delete call and managed to get past code review. This CL changes the Result class so that it assumes the error is allocated on the heap and forces the caller to use unique_ptr when calling AcquireError. Bug:dawn:320 Change-Id: I13ec953b0c37eaafbd6ce93c2f719b4743676acb Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14960 Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Rafael Cintron <rafael.cintron@microsoft.com>
254 lines
7.8 KiB
C++
254 lines
7.8 KiB
C++
// 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/Result.h"
|
|
|
|
namespace {
|
|
|
|
template<typename T, typename E>
|
|
void TestError(Result<T, E>* result, E expectedError) {
|
|
ASSERT_TRUE(result->IsError());
|
|
ASSERT_FALSE(result->IsSuccess());
|
|
|
|
std::unique_ptr<E> storedError = result->AcquireError();
|
|
ASSERT_EQ(*storedError, expectedError);
|
|
}
|
|
|
|
template<typename T, typename E>
|
|
void TestSuccess(Result<T, E>* result, T expectedSuccess) {
|
|
ASSERT_FALSE(result->IsError());
|
|
ASSERT_TRUE(result->IsSuccess());
|
|
|
|
T storedSuccess = result->AcquireSuccess();
|
|
ASSERT_EQ(storedSuccess, expectedSuccess);
|
|
}
|
|
|
|
static int dummyError = 0xbeef;
|
|
static float dummySuccess = 42.0f;
|
|
static const float dummyConstSuccess = 42.0f;
|
|
|
|
// 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;
|
|
ASSERT_TRUE(result.IsSuccess());
|
|
ASSERT_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());
|
|
}
|
|
|
|
// Test returning a success Result<void, E>
|
|
TEST(ResultOnlyPointerError, ReturningSuccess) {
|
|
auto CreateError = []() -> Result<void, int> { return {}; };
|
|
|
|
Result<void, int> result = CreateError();
|
|
ASSERT_TRUE(result.IsSuccess());
|
|
ASSERT_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<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
|