blob: 332c40f1f7fb6866c84a5c6d955d1d5079a7cd76 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// 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 "android/base/Result.h"
#include "android/base/Compiler.h"
#include "android/base/testing/ResultMatchers.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
using android::base::Err;
using android::base::IsErr;
using android::base::IsOk;
using android::base::Ok;
using android::base::Result;
using testing::Eq;
using testing::ExplainMatchResult;
using testing::Not;
using testing::Optional;
enum class MyError { Failed1, Failed2 };
std::ostream& operator<<(std::ostream& os, const MyError& value) {
switch (value) {
case MyError::Failed1:
os << "MyError::Failed1";
break;
case MyError::Failed2:
os << "MyError::Failed2";
break;
// Default intentionally omitted to generate compiler warning if fields
// are added.
}
return os;
}
template <typename MatcherType>
std::string DescribeResult(const MatcherType& m) {
std::stringstream ss;
testing::SafeMatcherCast<const Result<uint32_t, MyError>&>(m).DescribeTo(
&ss);
return ss.str();
}
TEST(Result, VoidEnum) {
{
Result<void, MyError> result = Ok();
EXPECT_TRUE(result.ok());
EXPECT_FALSE(result.err());
EXPECT_FALSE(result.err().hasValue());
}
{
Result<void, MyError> result = Err(MyError::Failed1);
EXPECT_FALSE(result.ok());
EXPECT_TRUE(result.err());
EXPECT_THAT(result.err(), Optional(Eq(MyError::Failed1)));
}
}
TEST(Result, ReturnValue) {
{
Result<uint32_t, MyError> result = Ok(1);
EXPECT_TRUE(result.ok());
EXPECT_FALSE(result.err());
EXPECT_THAT(result.ok(), Optional(Eq(1)));
}
{
Result<uint32_t, MyError> result = Err(MyError::Failed2);
EXPECT_FALSE(result.ok());
EXPECT_TRUE(result.err());
EXPECT_THAT(result.err(), Optional(Eq(MyError::Failed2)));
}
}
TEST(Result, Matchers) {
EXPECT_EQ("Ok()", DescribeResult(IsOk()));
EXPECT_EQ("Ok(0)", DescribeResult(IsOk(0)));
EXPECT_EQ("Err(MyError::Failed1)", DescribeResult(IsErr(MyError::Failed1)));
EXPECT_EQ("Err(MyError::Failed2)", DescribeResult(IsErr(MyError::Failed2)));
{
Result<void, MyError> result = Ok();
EXPECT_THAT(result, IsOk());
}
{
Result<uint32_t, MyError> result = Ok(1);
EXPECT_THAT(result, IsOk(1));
}
{
Result<uint32_t, MyError> result = Err(MyError::Failed1);
EXPECT_THAT(result, IsErr(MyError::Failed1));
}
}
TEST(Result, SameType) {
Result<uint32_t, uint32_t> success = Ok(123);
EXPECT_THAT(success.ok(), Optional(Eq(123)));
EXPECT_FALSE(success.err().hasValue());
EXPECT_EQ(*success.ok(), 123);
EXPECT_DEATH(*success.err(), "not constructed");
Result<uint32_t, uint32_t> fail = Err(456u);
EXPECT_THAT(fail.err(), Optional(Eq(456u)));
EXPECT_FALSE(fail.ok().hasValue());
EXPECT_DEATH(*fail.ok(), "not constructed");
EXPECT_EQ(*fail.err(), 456u);
}
TEST(Result, ComplexError) {
struct ComplexError {
ComplexError(MyError code, std::string message = "")
: mCode(code), mMessage(message) {}
const MyError mCode;
const std::string mMessage;
};
{
Result<void, ComplexError> result = Ok();
EXPECT_TRUE(result.ok());
}
{
Result<void, ComplexError> result =
Err(ComplexError(MyError::Failed1, "message"));
EXPECT_TRUE(result.err());
EXPECT_EQ(result.err().value().mCode, MyError::Failed1);
EXPECT_EQ(result.err().value().mMessage, "message");
}
}
TEST(Result, Invalidated) {
auto okFn = []() -> Result<uint32_t, uint32_t> { return Ok(123); };
auto errFn = []() -> Result<uint32_t, uint32_t> { return Err(456u); };
{
auto result = okFn();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result.unwrap(), 123);
EXPECT_DEATH(result.ok(), "invalid");
EXPECT_DEATH(result.err(), "invalid");
EXPECT_DEATH(result.unwrap(), "invalid");
EXPECT_DEATH(result.unwrapErr(), "invalid");
}
{
auto result = errFn();
ASSERT_TRUE(result.err());
EXPECT_EQ(result.unwrapErr(), 456);
EXPECT_DEATH(result.ok(), "invalid");
EXPECT_DEATH(result.err(), "invalid");
EXPECT_DEATH(result.unwrap(), "invalid");
EXPECT_DEATH(result.unwrapErr(), "invalid");
}
}
struct NonCopyable {
DISALLOW_COPY_AND_ASSIGN(NonCopyable);
public:
NonCopyable(uint32_t value) : mValue(value) {}
NonCopyable(NonCopyable&& other) : mValue(std::move(other.mValue)) {}
const uint32_t mValue;
};
TEST(Result, NonCopyableValue) {
auto testFn = []() -> Result<NonCopyable, MyError> {
return Ok(NonCopyable(123));
};
{
auto result = testFn();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result.unwrap().mValue, 123);
EXPECT_DEATH(result.unwrap(), "invalid");
EXPECT_DEATH(result.unwrapErr(), "invalid");
}
// Test propagating the non-copyable rtype.
auto propagateFn = [&testFn]() -> Result<NonCopyable, MyError> {
return testFn();
};
auto propagateWrapperFn = [&propagateFn]() -> Result<NonCopyable, MyError> {
return propagateFn();
};
{
auto result = propagateWrapperFn();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result.unwrap().mValue, 123);
}
}
TEST(Result, NonCopyableErr) {
auto testFn = []() -> Result<void, NonCopyable> {
return Err(NonCopyable(123));
};
{
auto result = testFn();
ASSERT_TRUE(result.err());
EXPECT_EQ(result.unwrapErr().mValue, 123);
EXPECT_DEATH(result.unwrapErr(), "invalid");
}
// Test propagating the non-copyable rtype.
auto propagateFn = [&testFn]() -> Result<void, NonCopyable> {
return testFn();
};
auto propagateWrapperFn = [&propagateFn]() -> Result<void, NonCopyable> {
return propagateFn();
};
{
auto result = propagateWrapperFn();
ASSERT_TRUE(result.err());
EXPECT_EQ(result.unwrapErr().mValue, 123);
}
auto propagateErrFn = [&testFn]() -> Result<uint32_t, NonCopyable> {
auto result = testFn();
RETURN_IF_ERR(result);
return Ok(0);
};
auto propagateErrDiscardFn = [&testFn]() -> Result<uint32_t, NonCopyable> {
RETURN_IF_ERR(testFn());
return Ok(0);
};
{
auto result = propagateErrFn();
ASSERT_TRUE(result.err());
EXPECT_EQ(result.err().value().mValue, 123);
}
{
auto result = propagateErrDiscardFn();
ASSERT_TRUE(result.err());
EXPECT_EQ(result.unwrapErr().mValue, 123);
}
}