blob: c66efbb1827df9e194ce2bb57c1cc779f10a58fa [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>
#include "libhwsec-foundation/status/status_chain.h"
#include "libhwsec-foundation/status/status_chain_macros.h"
#include "libhwsec-foundation/status/status_chain_or.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace hwsec_foundation {
namespace status {
class StatusChainTest : public ::testing::Test {};
namespace {
class FakeBaseError : public Error {
public:
using MakeStatusTrait = DefaultMakeStatus<FakeBaseError>;
using BaseErrorType = FakeBaseError;
FakeBaseError(std::string message, int val) : Error(message), val_(val) {}
~FakeBaseError() override {}
std::string ToString() const override {
return "FakeBase: " + Error::ToString();
}
int val() const { return val_; }
void set_val(int val) { val_ = val; }
protected:
int val_;
};
class Fake1Error : public FakeBaseError {
public:
using MakeStatusTrait = DefaultMakeStatus<Fake1Error>;
using BaseErrorType = FakeBaseError;
Fake1Error(std::string message, int val) : FakeBaseError(message, val) {}
~Fake1Error() override {}
std::string ToString() const override {
return "Fake1: " + Error::ToString();
}
};
class Fake2Error : public FakeBaseError {
public:
struct MakeStatusTrait {
auto operator()(std::string message, int val) {
return NewStatus<Fake2Error>(message + ": FROM TRAIT", val);
}
};
using BaseErrorType = FakeBaseError;
Fake2Error(std::string message, int val) : FakeBaseError(message, val) {}
~Fake2Error() override {}
std::string ToString() const override {
return "Fake2: " + Error::ToString();
}
};
class Fake3Error : public FakeBaseError {
public:
using MakeStatusTrait = DefaultMakeStatus<Fake3Error>;
using BaseErrorType = FakeBaseError;
Fake3Error(std::string message, int val) : FakeBaseError(message, val) {}
~Fake3Error() override {}
void WrapTransform(StatusChain<BaseErrorType>::const_iterator_range range) {
int new_val = 0;
for (auto error_obj_ptr : range) {
if (Error::Is<Fake1Error>(error_obj_ptr)) {
// shouldn't need to cast since iterator should point to FakeBaseError.
new_val += error_obj_ptr->val();
}
}
set_val(new_val);
}
std::string ToString() const override { return Error::ToString(); }
};
class Fake4Error : public FakeBaseError {
public:
using MakeStatusTrait = DefaultMakeStatus<Fake4Error>;
using BaseErrorType = FakeBaseError;
Fake4Error(std::string message, int val) : FakeBaseError(message, val) {}
~Fake4Error() override {}
std::string ToString() const override {
return "Fake4: " + Error::ToString();
}
};
TEST_F(StatusChainTest, CtorAssign) {
StatusChain<Fake1Error> ok;
EXPECT_TRUE(ok.ok());
StatusChain<Fake1Error> assign_ok;
assign_ok = std::move(ok);
EXPECT_TRUE(assign_ok.ok());
StatusChain<Fake1Error> nullptr_ok = nullptr;
EXPECT_TRUE(nullptr_ok.ok());
StatusChain<Fake1Error> assign_nullptr_ok;
assign_nullptr_ok = std::move(nullptr_ok);
EXPECT_TRUE(assign_nullptr_ok.ok());
StatusChain<Fake1Error> ptr(new Fake1Error("e1", 1));
EXPECT_EQ(ptr->val(), 1);
ptr.WrapInPlace(MakeStatus<Fake2Error>("e2", 2));
EXPECT_EQ(ptr.Find<Fake2Error>()->val(), 2);
StatusChain<Fake1Error> ctor_type_match = std::move(ptr);
EXPECT_TRUE(ptr.ok());
EXPECT_EQ(ctor_type_match->val(), 1);
EXPECT_EQ(ctor_type_match.Find<Fake2Error>()->val(), 2);
StatusChain<Fake1Error> assign_type_match;
assign_type_match = std::move(ctor_type_match);
EXPECT_TRUE(ctor_type_match.ok());
EXPECT_EQ(assign_type_match->val(), 1);
EXPECT_EQ(assign_type_match.Find<Fake2Error>()->val(), 2);
StatusChain<FakeBaseError> ctor_type_mismatch = std::move(assign_type_match);
EXPECT_TRUE(assign_type_match.ok());
EXPECT_EQ(ctor_type_mismatch->val(), 1);
EXPECT_EQ(ctor_type_mismatch.Find<Fake2Error>()->val(), 2);
StatusChain<FakeBaseError> assign_type_mismatch;
assign_type_mismatch =
MakeStatus<Fake4Error>("e3", 3).Wrap(std::move(ctor_type_mismatch));
EXPECT_TRUE(ctor_type_mismatch.ok());
EXPECT_EQ(assign_type_mismatch->val(), 3);
EXPECT_EQ(assign_type_mismatch.Find<Fake1Error>()->val(), 1);
EXPECT_EQ(assign_type_mismatch.Find<Fake2Error>()->val(), 2);
StatusChain<FakeBaseError> from_release(assign_type_mismatch.release_stack());
EXPECT_TRUE(assign_type_mismatch.ok());
EXPECT_EQ(from_release->val(), 3);
EXPECT_EQ(from_release.Find<Fake1Error>()->val(), 1);
EXPECT_EQ(from_release.Find<Fake2Error>()->val(), 2);
}
TEST_F(StatusChainTest, PointerAccessSwapReset) {
StatusChain<Fake1Error> ptr1;
EXPECT_EQ(ptr1.get(), StatusChain<Fake1Error>::pointer());
StatusChain<Fake1Error> ptr2(new Fake1Error("e1", 1));
ptr2.WrapInPlace(MakeStatus<Fake2Error>("e2", 2));
EXPECT_EQ(ptr2->val(), 1);
EXPECT_EQ(ptr2.get()->val(), 1);
EXPECT_EQ((*ptr2).val(), 1);
EXPECT_EQ(ptr2.error().val(), 1);
EXPECT_EQ(ptr2.Find<Fake2Error>()->val(), 2);
ptr1.reset(new Fake1Error("e3", 3));
ptr1.WrapInPlace(MakeStatus<Fake2Error>("e4", 4));
EXPECT_EQ(ptr1->val(), 3);
EXPECT_EQ(ptr1.get()->val(), 3);
EXPECT_EQ((*ptr1).val(), 3);
EXPECT_EQ(ptr1.error().val(), 3);
EXPECT_EQ(ptr1.Find<Fake2Error>()->val(), 4);
std::swap(ptr1, ptr2);
EXPECT_EQ(ptr1->val(), 1);
EXPECT_EQ(ptr1.get()->val(), 1);
EXPECT_EQ((*ptr1).val(), 1);
EXPECT_EQ(ptr1.error().val(), 1);
EXPECT_EQ(ptr1.Find<Fake2Error>()->val(), 2);
EXPECT_EQ(ptr2->val(), 3);
EXPECT_EQ(ptr2.get()->val(), 3);
EXPECT_EQ((*ptr2).val(), 3);
EXPECT_EQ(ptr2.error().val(), 3);
EXPECT_EQ(ptr2.Find<Fake2Error>()->val(), 4);
ptr1.swap(ptr2);
EXPECT_EQ(ptr1->val(), 3);
EXPECT_EQ(ptr1.get()->val(), 3);
EXPECT_EQ((*ptr1).val(), 3);
EXPECT_EQ(ptr1.error().val(), 3);
EXPECT_EQ(ptr1.Find<Fake2Error>()->val(), 4);
EXPECT_EQ(ptr2->val(), 1);
EXPECT_EQ(ptr2.get()->val(), 1);
EXPECT_EQ((*ptr2).val(), 1);
EXPECT_EQ(ptr2.error().val(), 1);
EXPECT_EQ(ptr2.Find<Fake2Error>()->val(), 2);
ptr1.reset();
EXPECT_TRUE(ptr1.ok());
ptr2.reset(new Fake1Error("e5", 5));
EXPECT_EQ(ptr2->val(), 5);
EXPECT_EQ(ptr2.get()->val(), 5);
EXPECT_EQ((*ptr2).val(), 5);
EXPECT_EQ(ptr2.error().val(), 5);
EXPECT_EQ(ptr2.Find<Fake2Error>(), nullptr);
}
TEST_F(StatusChainTest, StackElementAccess) {
StatusChain<FakeBaseError> e1 = MakeStatus<Fake1Error>("e1", 1);
StatusChain<FakeBaseError> e2 =
MakeStatus<FakeBaseError>("e2", 2).Wrap(std::move(e1));
StatusChain<FakeBaseError> e3 =
MakeStatus<Fake1Error>("e3", 4).Wrap(std::move(e2));
StatusChain<FakeBaseError> e4 =
MakeStatus<Fake2Error>("e4", 8).Wrap(std::move(e3));
StatusChain<FakeBaseError> e5 =
MakeStatus<Fake1Error>("e5", 16).Wrap(std::move(e4));
StatusChain<FakeBaseError> e6 =
MakeStatus<Fake2Error>("e6", 32).Wrap(std::move(e5));
EXPECT_FALSE(e6.Is<Fake3Error>());
EXPECT_FALSE(e6.Is<Fake1Error>());
EXPECT_TRUE(e6.Is<Fake2Error>());
EXPECT_EQ(e6.Cast<Fake2Error>()->val(), 32);
EXPECT_EQ(e6.Find<Fake3Error>(), nullptr);
EXPECT_EQ(e6.Find<Fake1Error>()->val(), 16);
}
TEST_F(StatusChainTest, WrappingUnwrapping) {
StatusChain<FakeBaseError> e0;
EXPECT_FALSE(e0.IsWrapping());
e0 = MakeStatus<Fake1Error>("e0", -1);
EXPECT_FALSE(e0.IsWrapping());
EXPECT_EQ(e0.Cast<Fake1Error>()->val(), -1);
StatusChain<FakeBaseError> e1 =
MakeStatus<Fake1Error>("e1", 1).Wrap(std::move(e0));
EXPECT_FALSE(e0.IsWrapping());
EXPECT_TRUE(e1.IsWrapping());
EXPECT_EQ(e1.Cast<Fake1Error>()->val(), 1);
StatusChain<FakeBaseError> e2 =
MakeStatus<Fake1Error>("e2", 2).Wrap(std::move(e1));
EXPECT_FALSE(e1.IsWrapping());
EXPECT_TRUE(e2.IsWrapping());
EXPECT_EQ(e2.Cast<Fake1Error>()->val(), 2);
auto e1_unwrap = std::move(e2).Unwrap();
EXPECT_FALSE(e2.IsWrapping());
EXPECT_TRUE(e1_unwrap.IsWrapping());
EXPECT_EQ(e1_unwrap.Cast<Fake1Error>()->val(), 1);
StatusChain<FakeBaseError> e3 =
MakeStatus<Fake1Error>("e3", 3).Wrap(std::move(e1_unwrap));
EXPECT_FALSE(e1_unwrap.IsWrapping());
EXPECT_TRUE(e3.IsWrapping());
EXPECT_EQ(e3.Cast<Fake1Error>()->val(), 3);
auto e0_unwrap = std::move(e3).Unwrap().Unwrap();
EXPECT_FALSE(e3.IsWrapping());
EXPECT_FALSE(e0_unwrap.IsWrapping());
EXPECT_EQ(e0_unwrap.Cast<Fake1Error>()->val(), -1);
e0_unwrap.WrapInPlace(MakeStatus<Fake2Error>("e4", 4));
EXPECT_TRUE(e0_unwrap.IsWrapping());
EXPECT_EQ(e0_unwrap.Find<Fake2Error>()->val(), 4);
e0_unwrap.UnwrapInPlace().UnwrapInPlace();
EXPECT_FALSE(e0_unwrap);
EXPECT_FALSE(e0_unwrap.IsWrapping());
}
TEST_F(StatusChainTest, RangesAndIterators) {
StatusChain<FakeBaseError> e1 = MakeStatus<Fake1Error>("+", 1);
StatusChain<FakeBaseError> e2 =
MakeStatus<FakeBaseError>("-", 2).Wrap(std::move(e1));
StatusChain<FakeBaseError> e3 =
MakeStatus<Fake1Error>("+", 4).Wrap(std::move(e2));
StatusChain<FakeBaseError> e4 =
MakeStatus<Fake2Error>("-", 8).Wrap(std::move(e3));
StatusChain<FakeBaseError> e5 =
MakeStatus<Fake1Error>("+", 16).Wrap(std::move(e4));
StatusChain<Fake3Error> e6 =
MakeStatus<Fake3Error>("-", 32).Wrap(std::move(e5));
// Check various ways to iterate. In all case val should be a sum of all
// Fake1Error vals (marked with "+" error message above for clarity).
// Non-const range-for loop.
int val = 0;
for (auto error_obj_ptr : e6.range()) {
if (Error::Is<Fake1Error>(error_obj_ptr)) {
// shouldn't need to cast since iterator should point to FakeBaseError.
val += error_obj_ptr->val();
}
}
EXPECT_EQ(val, 1 + 4 + 16);
// const range-for loop.
val = 0;
for (const auto error_obj_ptr : e6.const_range()) {
if (Error::Is<Fake1Error>(error_obj_ptr)) {
// shouldn't need to cast since iterator should point to FakeBaseError.
val += error_obj_ptr->val();
}
}
EXPECT_EQ(val, 1 + 4 + 16);
// Manual non-const loop.
val = 0;
for (auto it = e6.range().begin(); it != e6.range().end(); ++it) {
if (Error::Is<Fake1Error>(*it)) {
// shouldn't need to cast since iterator should point to FakeBaseError.
val += it->val();
}
}
EXPECT_EQ(val, 1 + 4 + 16);
// Manual const loop.
val = 0;
for (auto it = e6.const_range().begin(); it != e6.const_range().end(); ++it) {
if (Error::Is<Fake1Error>(*it)) {
// shouldn't need to cast since iterator should point to FakeBaseError.
val += it->val();
}
}
EXPECT_EQ(val, 1 + 4 + 16);
// non-const range should be assignable to const one, and so iterator.
StatusChain<Fake3Error>::const_iterator_range crange = e6.range();
StatusChain<Fake3Error>::const_iterator cit = e6.range().begin();
EXPECT_EQ(crange, e6.range());
EXPECT_EQ(cit, e6.range().begin());
}
TEST_F(StatusChainTest, WrapTransform) {
StatusChain<FakeBaseError> e1 = MakeStatus<Fake1Error>("+", 1);
StatusChain<FakeBaseError> e2 =
MakeStatus<FakeBaseError>("-", 2).Wrap(std::move(e1));
StatusChain<FakeBaseError> e3 =
MakeStatus<Fake1Error>("+", 4).Wrap(std::move(e2));
StatusChain<FakeBaseError> e4 =
MakeStatus<Fake2Error>("-", 8).Wrap(std::move(e3));
StatusChain<FakeBaseError> e5 =
MakeStatus<Fake1Error>("+", 16).Wrap(std::move(e4));
StatusChain<Fake3Error> e6 =
MakeStatus<Fake3Error>("!", 32).Wrap(std::move(e5));
// The transform above sums all Fake1Error vals (marked with "+" error message
// above for clarity).
EXPECT_EQ(e6->val(), 1 + 4 + 16);
EXPECT_EQ(e6.Find<Fake1Error>()->val(), 16);
StatusChain<Fake3Error> e7_with_drop =
MakeStatus<Fake3Error>("!", 64).Wrap(std::move(e6), WrapTransformOnly);
EXPECT_EQ(e7_with_drop->val(), 1 + 4 + 16);
EXPECT_EQ(e6.Find<Fake1Error>(), nullptr);
}
TEST_F(StatusChainTest, BoolsOksAndMessages) {
StatusChain<FakeBaseError> base_ok;
EXPECT_FALSE(base_ok);
EXPECT_TRUE(base_ok.ok());
StatusChain<FakeBaseError> base_error =
MakeStatus<FakeBaseError>("base_error", 0);
EXPECT_TRUE(base_error);
EXPECT_FALSE(base_error.ok());
EXPECT_EQ(base_error.ToFullString(), "FakeBase: base_error");
StatusChain<Fake1Error> fake_1_error = MakeStatus<Fake1Error>("fake1", 0);
EXPECT_TRUE(fake_1_error);
EXPECT_FALSE(fake_1_error.ok());
EXPECT_EQ(fake_1_error.ToFullString(), "Fake1: fake1");
StatusChain<Fake2Error> fake_2_error = MakeStatus<Fake2Error>("fake2", 0);
EXPECT_TRUE(fake_2_error);
EXPECT_FALSE(fake_2_error.ok());
EXPECT_EQ(fake_2_error.ToFullString(), "Fake2: fake2: FROM TRAIT");
auto tmp_1 = std::move(fake_1_error).Wrap(std::move(base_error));
auto tmp_2 = std::move(fake_2_error).Wrap(std::move(tmp_1));
StatusChain<FakeBaseError> stack = std::move(tmp_2);
EXPECT_TRUE(stack);
EXPECT_FALSE(stack.ok());
EXPECT_EQ(stack.ToFullString(),
"Fake2: fake2: FROM TRAIT: Fake1: fake1: FakeBase: base_error");
}
TEST_F(StatusChainTest, Macros) {
auto lambda_as_is = []() -> StatusChain<Fake1Error> {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 1", 0));
return OkStatus<Fake1Error>();
};
EXPECT_EQ(lambda_as_is().ToFullString(), "Fake1: lambda 1");
auto lambda_as_is_with_log = []() -> StatusChain<Fake1Error> {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 2", 0)).LogInfo()
<< "some message";
return OkStatus<Fake1Error>();
};
EXPECT_EQ(lambda_as_is_with_log().ToFullString(), "Fake1: lambda 2");
auto lambda_as_status = []() -> StatusChain<Fake2Error> {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 3", 0))
.WithStatus<Fake2Error>("wrap", 0);
return OkStatus<Fake2Error>();
};
EXPECT_EQ(lambda_as_status().ToFullString(),
"Fake2: wrap: FROM TRAIT: Fake1: lambda 3");
auto lambda_as_value = []() -> int {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 4", 0)).As(42);
return 15;
};
EXPECT_EQ(lambda_as_value(), 42);
auto lambda_as_value_with_log = []() -> int {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 5", 0)).LogInfo().As(42);
return 15;
};
EXPECT_EQ(lambda_as_value_with_log(), 42);
auto lambda_as_false_with_log = []() -> bool {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 6", 0)).LogInfo().As(false);
return true;
};
EXPECT_FALSE(lambda_as_false_with_log());
auto lambda_convert = []() -> StatusChain<FakeBaseError> {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 7", 0));
return OkStatus<Fake2Error>();
};
EXPECT_EQ(lambda_convert().ToFullString(), "Fake1: lambda 7");
auto lambda_void = []() {
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 8", 0)).ReturnVoid();
return;
};
lambda_void();
auto lambda_handler = []() -> std::string {
auto policy = [](StatusLinker<Fake1Error> linker) {
return linker.LogError() << "wow";
};
RETURN_IF_ERROR(MakeStatus<Fake1Error>("lambda 9", 0))
.With(policy)
.With([](StatusChain<Fake1Error> status) {
return "XD: " + status.ToFullString();
});
return "";
};
EXPECT_EQ(lambda_handler(), "XD: Fake1: lambda 9");
auto lambda_success = []() -> bool {
RETURN_IF_ERROR(OkStatus<Fake1Error>()).LogInfo().As(false);
return true;
};
EXPECT_TRUE(lambda_success());
}
TEST_F(StatusChainTest, StatusChainOrAssignAndRead) {
StatusChainOr<std::string, Fake1Error> status_or1("data");
StatusChainOr<std::string, Fake1Error> status_or2("");
StatusChainOr<std::string, Fake1Error> status_or3(
MakeStatus<Fake1Error>("fake1", 0));
// Make sure the StatusChainOr is only constructable with expected nullptr.
static_assert(
std::is_constructible_v<StatusChainOr<std::unique_ptr<int>, Fake1Error>,
nullptr_t>,
"should be constructable with nullptr");
static_assert(
!std::is_constructible_v<StatusChainOr<int, Fake1Error>, nullptr_t>,
"should not be constructable with nullptr");
// Make sure converting between StatusChainOr and bool work as intended.
static_assert(!std::is_convertible_v<StatusChainOr<int, Fake1Error>, bool>,
"should not be convertible to bool");
static_assert(!std::is_convertible_v<StatusChainOr<bool, Fake1Error>, bool>,
"should not be convertible to bool");
static_assert(
!std::is_convertible_v<bool, StatusChainOr<std::string, Fake1Error>>,
"should not be convertible from bool");
static_assert(std::is_convertible_v<bool, StatusChainOr<bool, Fake1Error>>,
"should be convertible from bool");
EXPECT_TRUE(status_or1.ok());
EXPECT_TRUE(status_or2.ok());
EXPECT_FALSE(status_or3.ok());
EXPECT_EQ(*status_or1, "data");
EXPECT_TRUE(status_or2->empty());
EXPECT_EQ(status_or3.status().ToFullString(), "Fake1: fake1");
// StatusChainOr should be moveable.
StatusChainOr<std::string, Fake1Error> status_or4 = std::move(status_or1);
EXPECT_TRUE(status_or4.ok());
EXPECT_EQ(*status_or4, "data");
EXPECT_DEATH_IF_SUPPORTED(
(StatusChainOr<std::string, Fake1Error>(OkStatus<Fake1Error>()).ok()),
"Check failed");
}
TEST_F(StatusChainTest, StatusChainOrLambda) {
using StatusChainOrType1 = StatusChainOr<std::unique_ptr<int>, FakeBaseError>;
auto lambda1 = [](int value) -> StatusChainOrType1 {
if (value == 0) {
return MakeStatus<Fake1Error>("value shouldn't be zero", 0);
} else if (value < 0) {
return std::unique_ptr<int>(nullptr);
} else {
return std::make_unique<int>(123);
}
};
using StatusChainOrType2 =
StatusChainOr<std::tuple<bool, std::unique_ptr<std::string>, int>,
Fake1Error>;
auto lambda2 = [](int value) -> StatusChainOrType2 {
if (value == 0) {
return MakeStatus<Fake1Error>("value shouldn't be zero", 0);
} else if (value < 0) {
return {std::in_place, false, nullptr, 0};
} else {
return std::make_tuple(true, std::make_unique<std::string>("data"),
0x1337);
}
};
auto lambda3 = [&lambda1](int value) -> StatusChainOrType1 {
StatusChainOrType1 result = lambda1(value);
if (!result.ok()) {
return MakeStatus<Fake4Error>("lambda1 failed", 4)
.Wrap(std::move(result).status());
}
return std::move(*result);
};
auto lambda4 = [&lambda1](int value) -> StatusChain<FakeBaseError> {
if (value < 0) {
return MakeStatus<Fake4Error>("value shouldn't be negative", value);
}
ASSIGN_OR_RETURN(std::unique_ptr<int> result, lambda1(value));
LOG(INFO) << result;
return OkStatus<Fake3Error>();
};
using StatusChainOrType3 = StatusChainOr<std::vector<int>, Fake1Error>;
auto lambda5 = [](int value) -> StatusChainOrType3 {
if (value == 0) {
return MakeStatus<Fake1Error>("value shouldn't be zero", 0);
} else if (value < 0) {
return {std::in_place};
} else {
return {std::in_place,
{
value,
value + 1,
value + 2,
value + 3,
}};
}
};
EXPECT_FALSE(lambda1(0).ok());
EXPECT_FALSE(lambda1(0).status().ok());
EXPECT_TRUE(lambda1(-1).ok());
EXPECT_TRUE(lambda1(-1).status().ok());
EXPECT_TRUE(lambda1(123).ok());
EXPECT_TRUE(lambda1(123).status().ok());
auto result0 = lambda1(0);
auto result1 = lambda1(-1);
auto result123 = lambda1(123);
const auto& result0_status = result0.status();
const auto& result1_status = result1.status();
const auto& result123_status = result123.status();
EXPECT_FALSE(result0.ok());
EXPECT_FALSE(result0_status.ok());
EXPECT_TRUE(result1.ok());
EXPECT_TRUE(result1_status.ok());
EXPECT_TRUE(result123.ok());
EXPECT_TRUE(result123_status.ok());
EXPECT_EQ(result0.status().ToFullString(), "Fake1: value shouldn't be zero");
EXPECT_EQ(*result1, nullptr);
EXPECT_NE(*result123, nullptr);
EXPECT_EQ(**result123, 123);
EXPECT_FALSE(lambda2(0).ok());
EXPECT_FALSE(lambda1(0).status().ok());
EXPECT_TRUE(lambda2(-1).ok());
EXPECT_TRUE(lambda2(-1).status().ok());
EXPECT_TRUE(lambda2(123).ok());
EXPECT_TRUE(lambda2(123).status().ok());
auto result30 = lambda3(0);
auto result31 = lambda3(-1);
auto result3123 = lambda3(123);
EXPECT_FALSE(result30.ok());
EXPECT_FALSE(result30.status().ok());
EXPECT_TRUE(result31.ok());
EXPECT_TRUE(result31.status().ok());
EXPECT_TRUE(result3123.ok());
EXPECT_TRUE(result3123.status().ok());
EXPECT_EQ(result30.status().ToFullString(),
"Fake4: lambda1 failed: Fake1: value shouldn't be zero");
EXPECT_EQ(*result31, nullptr);
EXPECT_NE(*result3123, nullptr);
EXPECT_EQ(**result3123, 123);
EXPECT_FALSE(lambda4(0).ok());
EXPECT_FALSE(lambda4(-1).ok());
EXPECT_TRUE(lambda4(123).ok());
EXPECT_EQ(lambda4(0).ToFullString(), "Fake1: value shouldn't be zero");
EXPECT_EQ(lambda4(-1).ToFullString(), "Fake4: value shouldn't be negative");
auto result50 = lambda5(0);
auto result51 = lambda5(-1);
auto result5123 = lambda5(123);
EXPECT_FALSE(result50.ok());
EXPECT_TRUE(result51.ok());
EXPECT_TRUE(result5123.ok());
EXPECT_TRUE(result51->empty());
EXPECT_EQ(result5123->size(), 4);
EXPECT_EQ(result5123->at(3), 126);
}
TEST_F(StatusChainTest, StatusChainOrDerive) {
class BaseStruct {
public:
virtual ~BaseStruct() = default;
};
class DeriveStruct : public BaseStruct {
public:
virtual ~DeriveStruct() = default;
};
using StatusChainOrBase =
StatusChainOr<std::unique_ptr<BaseStruct>, FakeBaseError>;
using StatusChainOrDerive =
StatusChainOr<std::unique_ptr<DeriveStruct>, FakeBaseError>;
auto lambda1 = [](int value) -> StatusChainOrDerive {
if (value == 0) {
return MakeStatus<Fake1Error>("value shouldn't be zero", 0);
} else {
return std::make_unique<DeriveStruct>();
}
};
auto lambda2 = [&lambda1](int value) -> StatusChainOrBase {
if (value < 0) {
return std::make_unique<BaseStruct>();
}
if (value == 123) {
return std::make_unique<DeriveStruct>();
}
std::unique_ptr<DeriveStruct> result;
ASSIGN_OR_RETURN(result, lambda1(value),
(_.WithStatus<Fake4Error>("lambda1 failed", 4)));
return result;
};
auto result0 = lambda2(0);
auto result1 = lambda2(-1);
auto result123 = lambda2(123);
auto result456 = lambda2(456);
EXPECT_FALSE(result0.ok());
EXPECT_TRUE(result1.ok());
EXPECT_TRUE(result123.ok());
EXPECT_TRUE(result456.ok());
}
} // namespace
} // namespace status
} // namespace hwsec_foundation