blob: 695f84d01218b6c853fc079bfcdd23e8ccd217fa [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "base/types/expected_macros.h"
#include <memory>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include "base/test/gmock_expected_support.h"
#include "base/types/expected.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
expected<void, std::string> ReturnOk() {
return ok();
}
expected<int, std::string> ReturnValue(int v) {
return v;
}
expected<int, std::string> ReturnError(std::string_view msg) {
return unexpected(std::string(msg));
}
template <class... Args>
expected<std::tuple<Args...>, std::string> ReturnTupleValue(Args&&... v) {
return std::tuple<Args...>(std::forward<Args>(v)...);
}
template <class... Args>
expected<std::tuple<Args...>, std::string> ReturnTupleError(
std::string_view msg) {
return unexpected(std::string(msg));
}
expected<std::unique_ptr<int>, std::string> ReturnPtrValue(int v) {
return std::make_unique<int>(v);
}
expected<std::string, std::unique_ptr<int>> ReturnPtrError(int v) {
return unexpected(std::make_unique<int>(v));
}
TEST(ReturnIfError, Works) {
const auto func = []() -> std::string {
RETURN_IF_ERROR(ReturnOk());
RETURN_IF_ERROR(ReturnOk());
RETURN_IF_ERROR(ReturnError("EXPECTED"));
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(ReturnIfError, WorksWithExpectedReturn) {
const auto func = []() -> expected<void, std::string> {
RETURN_IF_ERROR(ReturnOk());
RETURN_IF_ERROR(ReturnOk());
RETURN_IF_ERROR(ReturnError("EXPECTED"));
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Eq("EXPECTED")));
}
TEST(ReturnIfError, WorksWithLambda) {
const auto func = []() -> std::string {
RETURN_IF_ERROR([] { return ReturnOk(); }());
RETURN_IF_ERROR([] { return ReturnError("EXPECTED"); }());
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(ReturnIfError, WorksWithMoveOnlyType) {
const auto func = []() -> std::unique_ptr<int> {
RETURN_IF_ERROR([] { return ReturnPtrError(1); }());
return nullptr;
};
EXPECT_THAT(func(), ::testing::Pointee(::testing::Eq(1)));
}
TEST(ReturnIfError, WorksWithMoveOnlyTypeAndExpectedReturn) {
const auto func = []() -> expected<void, std::unique_ptr<int>> {
RETURN_IF_ERROR([] { return ReturnPtrError(1); }());
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Pointee(::testing::Eq(1))));
}
TEST(ReturnIfError, WorksWithAdaptorFunc) {
const auto fail_test_if_called = [](std::string error) {
ADD_FAILURE();
return error;
};
const auto adaptor = [](std::string error) { return error + " EXPECTED B"; };
const auto func = [&]() -> std::string {
RETURN_IF_ERROR(ReturnOk(), fail_test_if_called);
RETURN_IF_ERROR(ReturnError("EXPECTED A"), adaptor);
return "ERROR";
};
EXPECT_EQ("EXPECTED A EXPECTED B", func());
}
TEST(ReturnIfError, WorksWithAdaptorFuncAndExpectedReturn) {
const auto adaptor = [](std::string error) { return error + " EXPECTED B"; };
const auto func = [&]() -> expected<void, std::string> {
RETURN_IF_ERROR(ReturnError("EXPECTED A"), adaptor);
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Eq("EXPECTED A EXPECTED B")));
}
TEST(ReturnIfError, WorksWithAdaptorFuncAndMoveOnlyType) {
const auto adaptor = [](std::unique_ptr<int> error) {
return std::make_unique<int>(2);
};
const auto func = [&]() -> std::unique_ptr<int> {
RETURN_IF_ERROR(ReturnPtrError(1), adaptor);
return nullptr;
};
EXPECT_THAT(func(), ::testing::Pointee(::testing::Eq(2)));
}
TEST(ReturnIfError, WorksWithAdaptorFuncAndMoveOnlyTypeAndExpectedReturn) {
const auto adaptor = [](std::unique_ptr<int> error) {
return std::make_unique<int>(2);
};
const auto func = [&]() -> expected<void, std::unique_ptr<int>> {
RETURN_IF_ERROR(ReturnPtrError(1), adaptor);
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Pointee(::testing::Eq(2))));
}
TEST(ReturnIfError, WorksWithVoidReturnAdaptor) {
int code = 0;
int phase = 0;
const auto adaptor = [&](std::string error) { ++code; };
const auto func = [&]() -> void {
phase = 1;
RETURN_IF_ERROR(ReturnOk(), adaptor);
phase = 2;
RETURN_IF_ERROR(ReturnError("EXPECTED A"), adaptor);
phase = 3;
};
func();
EXPECT_EQ(phase, 2);
EXPECT_EQ(code, 1);
}
TEST(AssignOrReturn, Works) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN(int value1, ReturnValue(1));
EXPECT_EQ(1, value1);
ASSIGN_OR_RETURN(const int value2, ReturnValue(2));
EXPECT_EQ(2, value2);
ASSIGN_OR_RETURN(const int& value3, ReturnValue(3));
EXPECT_EQ(3, value3);
ASSIGN_OR_RETURN([[maybe_unused]] const int value4,
ReturnError("EXPECTED"));
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, WorksWithExpectedReturn) {
const auto func = []() -> expected<void, std::string> {
ASSIGN_OR_RETURN(int value1, ReturnValue(1));
EXPECT_EQ(1, value1);
ASSIGN_OR_RETURN(const int value2, ReturnValue(2));
EXPECT_EQ(2, value2);
ASSIGN_OR_RETURN(const int& value3, ReturnValue(3));
EXPECT_EQ(3, value3);
ASSIGN_OR_RETURN([[maybe_unused]] const int value4,
ReturnError("EXPECTED"));
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Eq("EXPECTED")));
}
TEST(AssignOrReturn, WorksWithLambda) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN(const int value1, [] { return ReturnValue(1); }());
EXPECT_EQ(1, value1);
ASSIGN_OR_RETURN([[maybe_unused]] const int value2,
[] { return ReturnError("EXPECTED"); }());
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, WorksWithMoveOnlyType) {
const auto func = []() -> std::unique_ptr<int> {
ASSIGN_OR_RETURN([[maybe_unused]] const std::string s,
[] { return ReturnPtrError(1); }());
return nullptr;
};
EXPECT_THAT(func(), ::testing::Pointee(::testing::Eq(1)));
}
TEST(AssignOrReturn, WorksWithMoveOnlyTypeAndExpectedReturn) {
const auto func = []() -> expected<void, std::unique_ptr<int>> {
ASSIGN_OR_RETURN([[maybe_unused]] const std::string s,
[] { return ReturnPtrError(1); }());
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Pointee(::testing::Eq(1))));
}
TEST(AssignOrReturn, WorksWithCommasInType) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN((const std::tuple<int, int> t1), ReturnTupleValue(1, 1));
EXPECT_EQ((std::tuple(1, 1)), t1);
ASSIGN_OR_RETURN((const std::tuple<int, std::tuple<int, int>, int> t2),
ReturnTupleValue(1, std::tuple(1, 1), 1));
EXPECT_EQ((std::tuple(1, std::tuple(1, 1), 1)), t2);
ASSIGN_OR_RETURN(
([[maybe_unused]] const std::tuple<int, std::tuple<int, int>, int> t3),
(ReturnTupleError<int, std::tuple<int, int>, int>("EXPECTED")));
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, WorksWithStructuredBindings) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN((const auto& [t1, t2, t3, t4, t5]),
ReturnTupleValue(std::tuple(1, 1), 1, 2, 3, 4));
EXPECT_EQ((std::tuple(1, 1)), t1);
EXPECT_EQ(1, t2);
EXPECT_EQ(2, t3);
EXPECT_EQ(3, t4);
EXPECT_EQ(4, t5);
ASSIGN_OR_RETURN([[maybe_unused]] int t6, ReturnError("EXPECTED"));
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, WorksWithParenthesesAndDereference) {
const auto func = []() -> std::string {
int integer;
int* pointer_to_integer = &integer;
ASSIGN_OR_RETURN((*pointer_to_integer), ReturnValue(1));
EXPECT_EQ(1, integer);
ASSIGN_OR_RETURN(*pointer_to_integer, ReturnValue(2));
EXPECT_EQ(2, integer);
// Test where the order of dereference matters.
pointer_to_integer--;
int* const* const pointer_to_pointer_to_integer = &pointer_to_integer;
ASSIGN_OR_RETURN((*pointer_to_pointer_to_integer)[1], ReturnValue(3));
EXPECT_EQ(3, integer);
ASSIGN_OR_RETURN([[maybe_unused]] const int t1, ReturnError("EXPECTED"));
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, WorksWithAdaptorFunc) {
const auto fail_test_if_called = [](std::string error) {
ADD_FAILURE();
return error;
};
const auto adaptor = [](std::string error) { return error + " EXPECTED B"; };
const auto func = [&]() -> std::string {
ASSIGN_OR_RETURN([[maybe_unused]] int value, ReturnValue(1),
fail_test_if_called);
ASSIGN_OR_RETURN(value, ReturnError("EXPECTED A"), adaptor);
return "ERROR";
};
EXPECT_EQ("EXPECTED A EXPECTED B", func());
}
TEST(AssignOrReturn, WorksWithAdaptorFuncAndExpectedReturn) {
const auto adaptor = [](std::string error) { return error + " EXPECTED B"; };
const auto func = [&]() -> expected<void, std::string> {
ASSIGN_OR_RETURN([[maybe_unused]] const int value,
ReturnError("EXPECTED A"), adaptor);
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Eq("EXPECTED A EXPECTED B")));
}
TEST(AssignOrReturn, WorksWithAdaptorFuncAndMoveOnlyType) {
const auto adaptor = [](std::unique_ptr<int> error) {
return std::make_unique<int>(2);
};
const auto func = [&]() -> std::unique_ptr<int> {
ASSIGN_OR_RETURN([[maybe_unused]] const std::string s, ReturnPtrError(1),
adaptor);
return nullptr;
};
EXPECT_THAT(func(), ::testing::Pointee(::testing::Eq(2)));
}
TEST(AssignOrReturn, WorksWithAdaptorFuncAndMoveOnlyTypeAndExpectedReturn) {
const auto adaptor = [](std::unique_ptr<int> error) {
return std::make_unique<int>(2);
};
const auto func = [&]() -> expected<void, std::unique_ptr<int>> {
ASSIGN_OR_RETURN([[maybe_unused]] const std::string s, ReturnPtrError(1),
adaptor);
return ok();
};
EXPECT_THAT(func(), test::ErrorIs(::testing::Pointee(::testing::Eq(2))));
}
TEST(AssignOrReturn, WorksWithVoidReturnAdaptor) {
int code = 0;
int phase = 0;
const auto adaptor = [&](std::string error) { ++code; };
const auto func = [&]() -> void {
ASSIGN_OR_RETURN(phase, ReturnValue(1), adaptor);
phase = 2;
ASSIGN_OR_RETURN(phase, ReturnError("EXPECTED A"), adaptor);
phase = 3;
};
func();
EXPECT_EQ(phase, 2);
EXPECT_EQ(code, 1);
}
TEST(AssignOrReturn, WorksWithThirdArgumentAndCommas) {
const auto fail_test_if_called = [](std::string error) {
ADD_FAILURE();
return error;
};
const auto adaptor = [](std::string error) { return error + " EXPECTED B"; };
const auto func = [&]() -> std::string {
ASSIGN_OR_RETURN((const auto& [t1, t2, t3]), ReturnTupleValue(1, 2, 3),
fail_test_if_called);
EXPECT_EQ(t1, 1);
EXPECT_EQ(t2, 2);
EXPECT_EQ(t3, 3);
ASSIGN_OR_RETURN(([[maybe_unused]] const auto& [t4, t5, t6]),
(ReturnTupleError<int, int, int>("EXPECTED A")), adaptor);
return "ERROR";
};
EXPECT_EQ("EXPECTED A EXPECTED B", func());
}
TEST(AssignOrReturn, WorksWithAppendIncludingLocals) {
const auto func = [&](const std::string& str) -> std::string {
ASSIGN_OR_RETURN([[maybe_unused]] const int value,
ReturnError("EXPECTED A"),
[&](std::string e) { return e + str; });
return "ERROR";
};
EXPECT_EQ("EXPECTED A EXPECTED B", func(" EXPECTED B"));
}
TEST(AssignOrReturn, WorksForExistingVariable) {
const auto func = []() -> std::string {
int value = 1;
ASSIGN_OR_RETURN(value, ReturnValue(2));
EXPECT_EQ(2, value);
ASSIGN_OR_RETURN(value, ReturnValue(3));
EXPECT_EQ(3, value);
ASSIGN_OR_RETURN(value, ReturnError("EXPECTED"));
return "ERROR";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, UniquePtrWorks) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN(const std::unique_ptr<int> ptr, ReturnPtrValue(1));
EXPECT_EQ(*ptr, 1);
return "EXPECTED";
};
EXPECT_EQ("EXPECTED", func());
}
TEST(AssignOrReturn, UniquePtrWorksForExistingVariable) {
const auto func = []() -> std::string {
std::unique_ptr<int> ptr;
ASSIGN_OR_RETURN(ptr, ReturnPtrValue(1));
EXPECT_EQ(*ptr, 1);
ASSIGN_OR_RETURN(ptr, ReturnPtrValue(2));
EXPECT_EQ(*ptr, 2);
return "EXPECTED";
};
EXPECT_EQ("EXPECTED", func());
}
} // namespace
} // namespace base