blob: 15cde601e3a130c297bb220c45a1bad04985c0b3 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/types/optional_util.h"
#include <memory>
#include <optional>
#include <string>
#include "base/types/expected.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
TEST(OptionalUtilTest, OptionalToPtr) {
std::optional<float> optional;
EXPECT_EQ(nullptr, OptionalToPtr(optional));
optional = 0.1f;
EXPECT_EQ(&optional.value(), OptionalToPtr(optional));
EXPECT_NE(nullptr, OptionalToPtr(optional));
}
TEST(OptionalUtilTest, OptionalFromPtr) {
float* f_ptr = nullptr;
EXPECT_EQ(std::nullopt, OptionalFromPtr(f_ptr));
float f = 0.1f;
std::optional<float> optional_f(f);
EXPECT_EQ(optional_f, OptionalFromPtr(&f));
}
TEST(OptionalUtilTest, OptionalToExpected) {
std::optional<int> i_opt;
// No conversions.
base::expected<int, int> i_exp = OptionalToExpected(i_opt, -1);
EXPECT_EQ(i_exp, base::unexpected(-1));
// Error type converted.
i_exp = OptionalToExpected(i_opt, -1.0);
EXPECT_EQ(i_exp, base::unexpected(-1));
i_opt = 2;
// No conversions.
i_exp = OptionalToExpected(i_opt, -1);
EXPECT_EQ(i_exp, base::ok(2));
// Value type converted.
base::expected<float, int> f_exp = OptionalToExpected(i_opt, -1);
EXPECT_EQ(f_exp, base::ok(2.0));
// Non-movable error type. "is null" is a const char array, which must be
// copied before converting to a string. Forces the compiler to choose the
// OptionalToExpected override that copies its error argument, to validate
// that it's copied correctly.
auto exp_with_str_error =
OptionalToExpected<int, std::string>(std::nullopt, "is null");
EXPECT_EQ(exp_with_str_error, base::unexpected("is null"));
// Non-copyable error type. Forces the compiler to choose the
// OptionalToExpected override that moves its error argument, to validate that
// it's moved correctly.
auto exp_with_ptr_error = OptionalToExpected<int, std::unique_ptr<int>>(
std::nullopt, std::make_unique<int>(-1));
ASSERT_FALSE(exp_with_ptr_error.has_value());
EXPECT_EQ(*(exp_with_ptr_error.error()), -1);
}
TEST(OptionalUtilTest, OptionalFromExpected) {
base::expected<int, std::string> i_exp = base::unexpected("uninitialized");
// No conversion.
std::optional<int> i_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(i_opt, std::nullopt);
// Value type converted.
std::optional<float> f_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(f_opt, std::nullopt);
i_exp = base::ok(1);
// No conversion.
i_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(i_opt, 1);
// Value type converted.
f_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(f_opt, 1.0);
}
// Basic test.
TEST(OptionalUtilTest, OptionalUnwrapTo_Basic) {
int i = -404;
EXPECT_FALSE(OptionalUnwrapTo(std::optional<int>(), i));
EXPECT_EQ(i, -404);
EXPECT_TRUE(OptionalUnwrapTo(std::optional<int>(5), i));
EXPECT_EQ(i, 5);
}
// Test projection to a different type.
TEST(OptionalUtilTest, OptionalUnwrapTo_ProjectionLambda) {
struct S {
int i = -404;
};
S s;
EXPECT_FALSE(
OptionalUnwrapTo(std::optional<int>(), s, [](int i) { return S(i); }));
EXPECT_EQ(s.i, -404);
EXPECT_TRUE(
OptionalUnwrapTo(std::optional<int>(5), s, [](int i) { return S(i); }));
EXPECT_EQ(s.i, 5);
}
// Test projection through a non-lambda function.
TEST(OptionalUtilTest, OptionalUnwrapTo_ProjectionFunction) {
struct S {
int i = 0;
static int IntoInt(S s) { return s.i; }
};
int i = -404;
EXPECT_FALSE(OptionalUnwrapTo(std::optional<S>(), i, S::IntoInt));
EXPECT_EQ(i, -404);
EXPECT_TRUE(OptionalUnwrapTo(std::optional<S>(S{5}), i, S::IntoInt));
EXPECT_EQ(i, 5);
}
// Test projection through a method.
TEST(OptionalUtilTest, OptionalUnwrapTo_ProjectionMethod) {
struct S {
int i = 0;
int IntoInt() const { return i; }
};
int i = -404;
EXPECT_FALSE(OptionalUnwrapTo(std::optional<S>(), i, &S::IntoInt));
EXPECT_EQ(i, -404);
EXPECT_TRUE(OptionalUnwrapTo(std::optional<S>(S{5}), i, &S::IntoInt));
EXPECT_EQ(i, 5);
}
// Verify const ref of optional<T> are passed as const T& to projection.
TEST(OptionalUtilTest, OptionalUnwrapTo_ConstRefOptional) {
struct NoCopyMove {
explicit NoCopyMove(int i) : i(i) {}
NoCopyMove(NoCopyMove&&) = delete;
NoCopyMove& operator=(NoCopyMove&&) = delete;
int i = 0;
};
std::optional<NoCopyMove> empty;
int out = -404;
EXPECT_FALSE(
OptionalUnwrapTo(empty, out, [](const NoCopyMove& n) { return n.i; }));
EXPECT_EQ(out, -404);
std::optional<NoCopyMove> full(std::in_place, 5);
EXPECT_TRUE(
OptionalUnwrapTo(full, out, [](const NoCopyMove& n) { return n.i; }));
EXPECT_EQ(out, 5);
}
// Verify rvalue of optional<T> are passed as rvalue T to projection.
TEST(OptionalUtilTest, OptionalUnwrapTo_RvalueOptional) {
struct MoveOnly {
explicit MoveOnly(int i) : i(i) {}
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
int i = 0;
};
int out = -404;
EXPECT_FALSE(OptionalUnwrapTo(std::optional<MoveOnly>(), out,
[](MoveOnly&& n) { return n.i; }));
EXPECT_EQ(out, -404);
EXPECT_TRUE(OptionalUnwrapTo(std::optional<MoveOnly>(std::in_place, 5), out,
[](MoveOnly&& n) { return n.i; }));
EXPECT_EQ(out, 5);
}
// The output type is not the same, but it's assignable.
TEST(OptionalUtilTest, OptionalUnwrapTo_AssignableOutput) {
struct Assignable {
Assignable() = default;
Assignable(int) = delete;
void operator=(int ii) { i = ii; }
int i = -404;
};
Assignable out;
EXPECT_FALSE(OptionalUnwrapTo(std::optional<int>(), out));
EXPECT_EQ(out.i, -404);
EXPECT_TRUE(OptionalUnwrapTo(std::optional<int>(5), out));
EXPECT_EQ(out.i, 5);
}
} // namespace
} // namespace base