blob: 391a319432c55ae78fc201d61e3a6e0e154c1a54 [file]
#include "google/protobuf/repeated_field_proxy.h"
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/algorithm/container.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/repeated_field_proxy_iterator.h"
#include "google/protobuf/repeated_field_proxy_traits.h"
#include "google/protobuf/repeated_ptr_field.h"
#include "google/protobuf/test_protos/repeated_field_proxy_import_message.pb.h"
#include "google/protobuf/test_protos/repeated_field_proxy_test.pb.h"
#include "google/protobuf/test_textproto.h"
namespace google {
namespace protobuf {
namespace internal {
namespace {
using ::proto2_unittest::RepeatedFieldProxyTestImportEnum;
using ::proto2_unittest::RepeatedFieldProxyTestSimpleMessage;
using ::proto2_unittest::TestRepeatedEnumProxy;
using ::proto2_unittest::TestRepeatedImportEnumProxy;
using ::proto2_unittest::TestRepeatedImportMessageProxy;
using ::proto2_unittest::TestRepeatedIntProxy;
using ::proto2_unittest::TestRepeatedMessageProxy;
using ::proto2_unittest::TestRepeatedStdStringProxy;
using ::proto2_unittest::TestRepeatedStringViewProxy;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::Ge;
using ::testing::IsEmpty;
using ::testing::Lt;
using ::testing::Not;
static constexpr absl::string_view kLongString =
"long string that will be heap allocated";
// We want to say static_assert(false) in some places, but older compilers
// eagerly evaluate `static_assert`s in `if constexpr` expressions and fail to
// compile before templates are instantiated. `dependent_false_t` is a condition
// that is always false that depends on `T` to force lazy evaluation.
template <typename T>
static constexpr bool dependent_false_t [[maybe_unused]] =
!std::is_same_v<T, T>;
template <typename T>
auto ToStringLike(const T& val) {
if constexpr (std::is_same_v<absl::remove_cvref_t<decltype(val)>,
absl::Cord>) {
return std::string(val);
} else {
return absl::string_view(val);
}
}
MATCHER_P(StringEq, expected, "") {
auto val = ToStringLike(arg);
*result_listener << "where " << val << " is " << expected;
return val == expected;
}
template <typename T>
T StrAs(absl::string_view s) {
return T(s);
}
// Emulate a non-Abseil string_view (e.g. STL when Abseil's alias is disabled).
class CustomStringView {
public:
explicit CustomStringView(std::string str) : str_(std::move(str)) {}
const char* data() const { return str_.data(); }
size_t size() const { return str_.size(); }
explicit operator std::string() const { return str_; }
private:
std::string str_;
};
// A test-only container for a repeated field that manages construction and
// destruction of the underlying repeated field, and can construct proxies.
//
// This is necessary because RepeatedFieldProxy has no public constructors,
// aside from copy assignment.
template <typename T, bool kUseRepeatedFieldOrProxy>
class TestOnlyRepeatedFieldContainer {
using FieldType = typename internal::RepeatedFieldTraits<T>::type;
using ProxyType =
std::conditional_t<kUseRepeatedFieldOrProxy, RepeatedFieldOrProxy<T>,
RepeatedFieldProxy<T>>;
using ConstProxyType = std::conditional_t<kUseRepeatedFieldOrProxy,
RepeatedFieldOrProxy<const T>,
RepeatedFieldProxy<const T>>;
public:
static TestOnlyRepeatedFieldContainer New(Arena* arena) {
return TestOnlyRepeatedFieldContainer(Arena::MakeUnique<FieldType>(arena),
arena);
}
FieldType& operator*() { return *field_; }
const FieldType& operator*() const { return *field_; }
FieldType* operator->() { return &*field_; }
const FieldType* operator->() const { return &*field_; }
ProxyType MakeProxy() {
return ProxyType(
internal::RepeatedFieldProxyInternalPrivateAccessHelper<T>::Construct(
*field_, arena_));
}
ConstProxyType MakeConstProxy() const {
return ConstProxyType(
internal::RepeatedFieldProxyInternalPrivateAccessHelper<
const T>::Construct(*field_));
}
private:
TestOnlyRepeatedFieldContainer(Arena::UniquePtr<FieldType> field,
Arena* arena)
: field_(std::move(field)), arena_(arena) {}
Arena::UniquePtr<FieldType> field_;
Arena* arena_;
};
template <bool UseArena, bool UseRepeatedFieldOrProxy>
struct RepeatedFieldProxyTestParams {
static constexpr bool kUseArena = UseArena;
static constexpr bool kUseRepeatedFieldOrProxy = UseRepeatedFieldOrProxy;
};
template <typename Params>
static std::string BaseParamsToString() {
std::string name = "";
if constexpr (Params::kUseArena) {
name += "WithArena";
} else {
name += "WithoutArena";
}
if constexpr (Params::kUseRepeatedFieldOrProxy) {
name += "_RepeatedFieldOrProxy";
}
return name;
}
template <typename Params>
class RepeatedFieldProxyTestBase {
protected:
static constexpr bool kUseRepeatedFieldOrProxy =
Params::kUseRepeatedFieldOrProxy;
template <typename T>
using ProxyType =
std::conditional_t<kUseRepeatedFieldOrProxy, RepeatedFieldOrProxy<T>,
RepeatedFieldProxy<T>>;
template <typename T>
using ContainerType =
TestOnlyRepeatedFieldContainer<T, kUseRepeatedFieldOrProxy>;
static constexpr bool UseArena() { return Params::kUseArena; }
Arena* arena() { return UseArena() ? &arena_ : nullptr; }
template <typename ElementType>
ContainerType<ElementType> MakeRepeatedFieldContainer() {
return TestOnlyRepeatedFieldContainer<
ElementType, kUseRepeatedFieldOrProxy>::New(arena());
}
private:
Arena arena_;
};
// This is a test suite which is not generic over the element type. Tests in
// this suite have to explicitly specify the element type. Testing message or
// enum element types has to be done through this suite.
template <typename Params>
class RepeatedFieldProxyTest : public testing::Test,
public RepeatedFieldProxyTestBase<Params> {};
struct RepeatedFieldProxyTestName {
template <typename T>
static std::string GetName(int) {
return BaseParamsToString<T>();
}
};
using AllRepeatedFieldProxyTestParams = ::testing::Types<
RepeatedFieldProxyTestParams</*use_arena=*/false,
/*use_repeated_field_or_proxy=*/false>,
RepeatedFieldProxyTestParams</*use_arena=*/false,
/*use_repeated_field_or_proxy=*/true>,
RepeatedFieldProxyTestParams</*use_arena=*/true,
/*use_repeated_field_or_proxy=*/false>,
RepeatedFieldProxyTestParams</*use_arena=*/true,
/*use_repeated_field_or_proxy=*/true>>;
TYPED_TEST_SUITE(RepeatedFieldProxyTest, AllRepeatedFieldProxyTestParams,
RepeatedFieldProxyTestName);
template <typename ElementTypeParam, bool UseArena,
bool UseRepeatedFieldOrProxy>
struct RepeatedFieldProxyTypedTestParams
: public RepeatedFieldProxyTestParams<UseArena, UseRepeatedFieldOrProxy> {
using ElementType = ElementTypeParam;
};
template <typename Params>
class RepeatedFieldProxyTypedTestBase
: public RepeatedFieldProxyTestBase<Params> {
public:
using Base = RepeatedFieldProxyTestBase<Params>;
using ElementType = typename Params::ElementType;
using ContainerType = typename Base::template ContainerType<ElementType>;
ContainerType MakeRepeatedFieldContainer() {
return Base::template MakeRepeatedFieldContainer<ElementType>();
}
};
template <typename Params>
class RepeatedNumericFieldProxyTest
: public ::testing::Test,
public RepeatedFieldProxyTypedTestBase<Params> {
public:
using Base = RepeatedFieldProxyTypedTestBase<Params>;
using ElementType = typename Base::ElementType;
using ContainerType = typename Base::ContainerType;
template <typename T, typename U,
typename = std::enable_if_t<std::is_same_v<
absl::remove_cvref_t<T>, absl::remove_cvref_t<U>>>>
static void VerifyLambdaTypeRequirements(const ContainerType& field,
const U& lambda_argument) {
// Verify that `lambda_argument` is a const lvalue reference. The value from
// the repeated field was intentionally decayed to avoid exposing a
// reference to the element, but if the argument type of this lambda is a
// reference, it will alias the temporary copy. Since mutation of this
// temporary would not affect the original element, ensure it is const.
static_assert(std::is_lvalue_reference_v<T>);
static_assert(std::is_const_v<std::remove_reference_t<T>>);
// Verify that `lambda_argument` is a copy of an element from the repeated
// field, meaning it does not lie in the backing array.
const ElementType* backing_array =
reinterpret_cast<const ElementType*>(field->data());
const ElementType* backing_array_end = backing_array + field->size();
EXPECT_THAT(&lambda_argument,
AnyOf(Lt(backing_array), Ge(backing_array_end)));
}
};
struct RepeatedNumericFieldProxyTestName {
template <typename T>
static std::string GetName(int) {
using ElementType = typename T::ElementType;
std::string name;
if constexpr (std::is_same_v<ElementType, int32_t>) {
name = "Int32";
} else if constexpr (std::is_same_v<ElementType, int64_t>) {
name = "Int64";
} else if constexpr (std::is_same_v<ElementType, uint32_t>) {
name = "Uint32";
} else if constexpr (std::is_same_v<ElementType, uint64_t>) {
name = "Uint64";
} else if constexpr (std::is_same_v<ElementType, float>) {
name = "Float";
} else if constexpr (std::is_same_v<ElementType, double>) {
name = "Double";
} else {
static_assert(dependent_false_t<ElementType>, "Unsupported numeric type");
}
absl::StrAppend(&name, "_", BaseParamsToString<T>());
return name;
}
};
#define TEST_NUMERIC_ALL_TYPES(use_arena, use_repeated_field_or_proxy) \
RepeatedFieldProxyTypedTestParams<int32_t, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<int64_t, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<uint32_t, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<uint64_t, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<float, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<double, use_arena, \
use_repeated_field_or_proxy>
#define TEST_NUMERIC_USE_ARENA(use_repeated_field_or_proxy) \
TEST_NUMERIC_ALL_TYPES(false, use_repeated_field_or_proxy), \
TEST_NUMERIC_ALL_TYPES(true, use_repeated_field_or_proxy)
#define TEST_NUMERIC_USE_REPEATED_FIELD_OR_PROXY() \
TEST_NUMERIC_USE_ARENA(false), TEST_NUMERIC_USE_ARENA(true)
using AllNumericProxyTestParams =
::testing::Types<TEST_NUMERIC_USE_REPEATED_FIELD_OR_PROXY()>;
TYPED_TEST_SUITE(RepeatedNumericFieldProxyTest, AllNumericProxyTestParams,
RepeatedNumericFieldProxyTestName);
template <typename Params>
class RepeatedStringFieldProxyTest
: public ::testing::Test,
public RepeatedFieldProxyTypedTestBase<Params> {
protected:
using Base = RepeatedFieldProxyTypedTestBase<Params>;
using ElementType = typename Base::ElementType;
using ContainerType = typename Base::ContainerType;
template <typename T>
static constexpr void VerifyLambdaTypeRequirements() {
if constexpr (std::is_same_v<ElementType, absl::Cord>) {
static_assert(std::is_same_v<T, const absl::Cord&>);
} else {
static_assert(std::is_same_v<T, absl::string_view&&>);
}
}
template <typename Compare>
static auto StringCompare(Compare&& compare) {
return [&compare](auto&& lhs, auto&& rhs) {
VerifyLambdaTypeRequirements<decltype(lhs)>();
VerifyLambdaTypeRequirements<decltype(rhs)>();
return compare(lhs, rhs);
};
}
// A helper for adding strings to the legacy repeated field containers. The
// API is inconsistent across the different string types, so this centralizes
// the special casing.
void Add(ContainerType& field, absl::string_view s) {
if constexpr (std::is_same_v<ElementType, std::string> ||
std::is_same_v<ElementType, absl::string_view>) {
field->Add(std::string(s));
} else if constexpr (std::is_same_v<ElementType, absl::Cord>) {
field->Add(absl::Cord(s));
} else {
static_assert(dependent_false_t<ElementType>, "Unsupported string type");
}
}
const char* StartAddress(const ElementType& element) {
if constexpr (std::is_same_v<ElementType, std::string> ||
std::is_same_v<ElementType, absl::string_view>) {
return element.data();
} else if constexpr (std::is_same_v<ElementType, absl::Cord>) {
return &*element.char_begin();
} else {
static_assert(dependent_false_t<ElementType>, "Unsupported string type");
}
}
};
struct RepeatedStringFieldProxyTestName {
template <typename T>
static std::string GetName(int) {
using ElementType = typename T::ElementType;
std::string name;
if constexpr (std::is_same_v<ElementType, std::string>) {
name = "StdString";
} else if constexpr (std::is_same_v<ElementType, absl::string_view>) {
name = "StringView";
} else if constexpr (std::is_same_v<ElementType, absl::Cord>) {
name = "Cord";
} else {
static_assert(dependent_false_t<ElementType>, "Unsupported string type");
}
absl::StrAppend(&name, "_", BaseParamsToString<T>());
return name;
}
};
#define TEST_STRING_ALL_TYPES(use_arena, use_repeated_field_or_proxy) \
RepeatedFieldProxyTypedTestParams<std::string, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<absl::string_view, use_arena, \
use_repeated_field_or_proxy>, \
RepeatedFieldProxyTypedTestParams<absl::Cord, use_arena, \
use_repeated_field_or_proxy>
#define TEST_STRING_USE_ARENA(use_repeated_field_or_proxy) \
TEST_STRING_ALL_TYPES(false, use_repeated_field_or_proxy), \
TEST_STRING_ALL_TYPES(true, use_repeated_field_or_proxy)
#define TEST_STRING_USE_REPEATED_FIELD_OR_PROXY() \
TEST_STRING_USE_ARENA(false), TEST_STRING_USE_ARENA(true)
using AllStringProxyTestParams =
::testing::Types<TEST_STRING_USE_REPEATED_FIELD_OR_PROXY()>;
TYPED_TEST_SUITE(RepeatedStringFieldProxyTest, AllStringProxyTestParams,
RepeatedStringFieldProxyTestName);
TYPED_TEST(RepeatedNumericFieldProxyTest, DefaultEmpty) {
using ConstProxyType =
decltype(this->MakeRepeatedFieldContainer().MakeConstProxy());
ConstProxyType proxy;
EXPECT_THAT(proxy, IsEmpty());
}
TYPED_TEST(RepeatedStringFieldProxyTest, DefaultEmpty) {
using ConstProxyType =
decltype(this->MakeRepeatedFieldContainer().MakeConstProxy());
ConstProxyType proxy;
EXPECT_THAT(proxy, IsEmpty());
}
TYPED_TEST(RepeatedFieldProxyTest, MessageDefaultEmpty) {
using ConstProxyType = decltype(this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>()
.MakeConstProxy());
ConstProxyType proxy;
EXPECT_THAT(proxy, IsEmpty());
}
TYPED_TEST(RepeatedNumericFieldProxyTest, ReassignConstProxy) {
auto container = this->MakeRepeatedFieldContainer();
container->Add(1);
decltype(container.MakeConstProxy()) proxy;
proxy = container.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(1));
}
TYPED_TEST(RepeatedStringFieldProxyTest, ReassignConstProxy) {
auto container = this->MakeRepeatedFieldContainer();
this->Add(container, "1");
decltype(container.MakeConstProxy()) proxy;
proxy = container.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(StringEq("1")));
}
TYPED_TEST(RepeatedFieldProxyTest, ReassignConstMessageProxy) {
auto container = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
container->Add()->set_value(1);
decltype(container.MakeConstProxy()) proxy;
proxy = container.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, RepeatedNumeric) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
EXPECT_THAT(*field, ElementsAre(1, 2, 3));
proxy.set(1, 4);
EXPECT_THAT(proxy, ElementsAre(1, 4, 3));
EXPECT_THAT(*field, ElementsAre(1, 4, 3));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, ConstRepeatedNumeric) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
{
auto proxy = field.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
}
{
// Check that mutable proxies can be implicitly converted to const proxies.
decltype(field.MakeConstProxy()) proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Limits) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.push_back(std::numeric_limits<ElementType>::min());
proxy.push_back(std::numeric_limits<ElementType>::max());
EXPECT_THAT(proxy, ElementsAre(std::numeric_limits<ElementType>::min(),
std::numeric_limits<ElementType>::max()));
EXPECT_THAT(*field, ElementsAre(std::numeric_limits<ElementType>::min(),
std::numeric_limits<ElementType>::max()));
}
TYPED_TEST(RepeatedStringFieldProxyTest, RepeatedString) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.push_back("one");
proxy.push_back("two");
proxy.push_back("three");
EXPECT_THAT(proxy,
ElementsAre(StringEq("one"), StringEq("two"), StringEq("three")));
EXPECT_THAT(*field,
ElementsAre(StringEq("one"), StringEq("two"), StringEq("three")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, ConstRepeatedString) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "one");
this->Add(field, "two");
this->Add(field, "three");
{
auto proxy = field.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(StringEq("one"), StringEq("two"),
StringEq("three")));
}
{
// Check that mutable proxies can be implicitly converted to const proxies.
decltype(field.MakeConstProxy()) proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(StringEq("one"), StringEq("two"),
StringEq("three")));
}
}
TYPED_TEST(RepeatedFieldProxyTest, RepeatedMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
proxy.emplace_back().set_value(1);
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(2);
proxy.push_back(std::move(msg));
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
TYPED_TEST(RepeatedFieldProxyTest, ConstRepeatedMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
{
auto proxy = field.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
// Check that mutable proxies can be implicitly converted to const proxies.
decltype(field.MakeConstProxy()) proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
}
TYPED_TEST(RepeatedFieldProxyTest, Empty) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
EXPECT_TRUE(proxy.empty());
EXPECT_THAT(proxy, IsEmpty());
proxy.emplace_back();
EXPECT_FALSE(proxy.empty());
EXPECT_THAT(proxy, Not(IsEmpty()));
}
TYPED_TEST(RepeatedFieldProxyTest, ConstEmpty) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
{
auto proxy = field.MakeConstProxy();
EXPECT_TRUE(proxy.empty());
}
field->Add();
{
auto proxy = field.MakeConstProxy();
EXPECT_FALSE(proxy.empty());
}
}
TYPED_TEST(RepeatedFieldProxyTest, Size) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
EXPECT_EQ(proxy.size(), 0);
proxy.emplace_back();
EXPECT_EQ(proxy.size(), 1);
proxy.emplace_back();
proxy.emplace_back();
EXPECT_EQ(proxy.size(), 3);
}
TYPED_TEST(RepeatedFieldProxyTest, ConstSize) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
{
auto proxy = field.MakeProxy();
EXPECT_EQ(proxy.size(), 0);
}
field->Add();
{
auto proxy = field.MakeConstProxy();
EXPECT_EQ(proxy.size(), 1);
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest, ArrayIndexing) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
{
auto proxy = field.MakeProxy();
EXPECT_EQ(proxy[0], 1);
EXPECT_EQ(proxy[1], 2);
EXPECT_EQ(proxy[2], 3);
EXPECT_EQ(proxy.get(0), 1);
EXPECT_EQ(proxy.get(1), 2);
EXPECT_EQ(proxy.get(2), 3);
// Check that repeated numeric proxies return values, not references.
static_assert(std::is_same_v<decltype(proxy[0]), ElementType>);
static_assert(std::is_same_v<decltype(proxy.get(0)), ElementType>);
}
{
auto proxy = field.MakeConstProxy();
EXPECT_EQ(proxy[0], 1);
EXPECT_EQ(proxy[1], 2);
EXPECT_EQ(proxy[2], 3);
EXPECT_EQ(proxy.get(0), 1);
EXPECT_EQ(proxy.get(1), 2);
EXPECT_EQ(proxy.get(2), 3);
// Check that const repeated numeric proxies return values, not const
// references.
static_assert(std::is_same_v<decltype(proxy[0]), ElementType>);
static_assert(std::is_same_v<decltype(proxy.get(0)), ElementType>);
}
}
TYPED_TEST(RepeatedFieldProxyTest, ArrayIndexingMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
field->Add()->set_value(3);
{
auto proxy = field.MakeProxy();
EXPECT_THAT(proxy[0], EqualsProto(R"pb(value: 1)pb"));
EXPECT_THAT(proxy[1], EqualsProto(R"pb(value: 2)pb"));
EXPECT_THAT(proxy[2], EqualsProto(R"pb(value: 3)pb"));
EXPECT_THAT(proxy.get(0), EqualsProto(R"pb(value: 1)pb"));
EXPECT_THAT(proxy.get(1), EqualsProto(R"pb(value: 2)pb"));
EXPECT_THAT(proxy.get(2), EqualsProto(R"pb(value: 3)pb"));
static_assert(std::is_same_v<decltype(proxy[0]),
RepeatedFieldProxyTestSimpleMessage&>);
static_assert(std::is_same_v<decltype(proxy.get(0)),
const RepeatedFieldProxyTestSimpleMessage&>);
}
{
auto proxy = field.MakeConstProxy();
EXPECT_THAT(proxy[0], EqualsProto(R"pb(value: 1)pb"));
EXPECT_THAT(proxy[1], EqualsProto(R"pb(value: 2)pb"));
EXPECT_THAT(proxy[2], EqualsProto(R"pb(value: 3)pb"));
EXPECT_THAT(proxy.get(0), EqualsProto(R"pb(value: 1)pb"));
EXPECT_THAT(proxy.get(1), EqualsProto(R"pb(value: 2)pb"));
EXPECT_THAT(proxy.get(2), EqualsProto(R"pb(value: 3)pb"));
static_assert(std::is_same_v<decltype(proxy[0]),
const RepeatedFieldProxyTestSimpleMessage&>);
static_assert(std::is_same_v<decltype(proxy.get(0)),
const RepeatedFieldProxyTestSimpleMessage&>);
}
}
TYPED_TEST(RepeatedStringFieldProxyTest, ArrayIndexing) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
this->Add(field, "3");
{
auto proxy = field.MakeProxy();
EXPECT_THAT(proxy[0], StringEq("1"));
EXPECT_THAT(proxy[1], StringEq("2"));
EXPECT_THAT(proxy[2], StringEq("3"));
EXPECT_THAT(proxy.get(0), StringEq("1"));
EXPECT_THAT(proxy.get(1), StringEq("2"));
EXPECT_THAT(proxy.get(2), StringEq("3"));
if constexpr (std::is_same_v<ElementType, absl::string_view>) {
static_assert(std::is_same_v<decltype(proxy[0]), absl::string_view>);
static_assert(std::is_same_v<decltype(proxy.get(0)), absl::string_view>);
} else {
static_assert(std::is_same_v<decltype(proxy[0]), ElementType&>);
static_assert(std::is_same_v<decltype(proxy.get(0)), const ElementType&>);
}
}
{
auto proxy = field.MakeConstProxy();
EXPECT_THAT(proxy[0], StringEq("1"));
EXPECT_THAT(proxy[1], StringEq("2"));
EXPECT_THAT(proxy[2], StringEq("3"));
EXPECT_THAT(proxy.get(0), StringEq("1"));
EXPECT_THAT(proxy.get(1), StringEq("2"));
EXPECT_THAT(proxy.get(2), StringEq("3"));
if constexpr (std::is_same_v<ElementType, absl::string_view>) {
static_assert(std::is_same_v<decltype(proxy[0]), absl::string_view>);
static_assert(std::is_same_v<decltype(proxy.get(0)), absl::string_view>);
} else {
static_assert(std::is_same_v<decltype(proxy[0]), const ElementType&>);
static_assert(std::is_same_v<decltype(proxy.get(0)), const ElementType&>);
}
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest, MutateElementPrimitive) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
{
auto proxy = field.MakeProxy();
proxy.set(0, 4);
EXPECT_THAT(proxy, ElementsAre(4, 2, 3));
}
}
TYPED_TEST(RepeatedStringFieldProxyTest, MutateElement) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
this->Add(field, "3");
this->Add(field, "4");
auto proxy = field.MakeProxy();
if constexpr (std::is_same_v<ElementType, std::string>) {
proxy[0] = "5";
proxy[1] = StrAs<std::string>("6");
const char* c_str = "7";
proxy[2] = c_str;
proxy[3] = StrAs<absl::string_view>("8");
EXPECT_THAT(proxy, ElementsAre(StringEq("5"), StringEq("6"), StringEq("7"),
StringEq("8")));
}
auto long_string = std::string(kLongString);
const char* string_ptr = long_string.c_str();
proxy.set(0, std::move(long_string));
EXPECT_THAT(proxy[0], StringEq(kLongString));
if constexpr (std::is_same_v<ElementType, std::string> ||
std::is_same_v<ElementType, absl::string_view>) {
// Since long_string was moved, proxy[0] should point to the same heap data.
EXPECT_EQ(string_ptr, proxy[0].data());
} else {
(void)string_ptr;
}
proxy.set(0, "9");
proxy.set(1, std::string("10"));
const char* c_str2 = "11";
proxy.set(2, c_str2);
proxy.set(3, absl::string_view("12"));
EXPECT_THAT(proxy, ElementsAre(StringEq("9"), StringEq("10"), StringEq("11"),
StringEq("12")));
std::string str13 = "13", str14 = "14";
proxy.set(0, std::ref(str13));
proxy.set(1, std::ref(str14));
proxy.set(2, std::ref("15"));
proxy.set(3, std::cref("16"));
EXPECT_THAT(proxy, ElementsAre(StringEq("13"), StringEq("14"), StringEq("15"),
StringEq("16")));
auto cord = absl::Cord(kLongString);
proxy.set(0, std::move(cord));
EXPECT_THAT(proxy[0], StringEq(kLongString));
}
TYPED_TEST(RepeatedFieldProxyTest, MutateElementMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
field->Add()->set_value(3);
auto proxy = field.MakeProxy();
proxy[2].set_value(4);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 4)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 4)pb")));
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(5);
proxy.set(0, msg);
// `msg` is copied into the element, so it should be unchanged.
EXPECT_TRUE(msg.has_value());
{
auto msg2 =
Arena::MakeUnique<RepeatedFieldProxyTestSimpleMessage>(this->arena());
msg2->set_value(6);
auto* nested = msg2->mutable_nested();
nested->set_value(7);
proxy.set(1, std::move(*msg2));
// Since `msg2` was moved, `nested` should point to the same object.
EXPECT_EQ(proxy[1].mutable_nested(), nested);
}
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 6,
nested { value: 7 })pb"),
EqualsProto(R"pb(value: 4)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 6,
nested { value: 7 })pb"),
EqualsProto(R"pb(value: 4)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, PushBack) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
EXPECT_THAT(*field, ElementsAre(1, 2, 3));
}
TYPED_TEST(RepeatedFieldProxyTest, PushBackMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
auto msg1 = RepeatedFieldProxyTestSimpleMessage();
msg1.set_value(1);
proxy.push_back(msg1);
auto msg2 = RepeatedFieldProxyTestSimpleMessage();
msg2.set_value(2);
proxy.push_back(msg2);
auto msg3 = RepeatedFieldProxyTestSimpleMessage();
msg3.set_value(3);
proxy.push_back(msg3);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TYPED_TEST(RepeatedFieldProxyTest, PushBackMessageLvalueCopies) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
auto msg1 =
Arena::MakeUnique<RepeatedFieldProxyTestSimpleMessage>(this->arena());
auto* nested = msg1->mutable_nested();
proxy.push_back(*msg1);
EXPECT_NE(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
}
TYPED_TEST(RepeatedFieldProxyTest, PushBackMessageRvalueDoesNotCopy) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
auto msg1 =
Arena::MakeUnique<RepeatedFieldProxyTestSimpleMessage>(this->arena());
auto* nested = msg1->mutable_nested();
proxy.push_back(std::move(*msg1));
EXPECT_EQ(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, PushBack) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
{
proxy.push_back("1");
proxy.push_back(StrAs<std::string>("2"));
const char* c_str = "3";
proxy.push_back(c_str);
proxy.push_back(StrAs<absl::string_view>("4"));
EXPECT_THAT(proxy[0], StringEq("1"));
EXPECT_THAT(proxy[1], StringEq("2"));
EXPECT_THAT(proxy[2], StringEq("3"));
EXPECT_THAT(proxy[3], StringEq("4"));
}
{
auto long_string = std::string(kLongString);
const char* string_ptr = long_string.c_str();
proxy.push_back(std::move(long_string));
EXPECT_THAT(proxy[4], StringEq(kLongString));
if constexpr (std::is_same_v<ElementType, std::string> ||
std::is_same_v<ElementType, absl::string_view>) {
// Since long_string was moved, proxy[4] should point to the same heap
// data.
EXPECT_EQ(string_ptr, proxy[4].data());
} else {
(void)string_ptr;
}
}
{
std::string str6 = "6", str7 = "7";
proxy.push_back(std::ref(str6));
proxy.push_back(std::ref(str7));
proxy.push_back(std::ref("8"));
proxy.push_back(std::cref("9"));
EXPECT_THAT(proxy[5], StringEq("6"));
EXPECT_THAT(proxy[6], StringEq("7"));
EXPECT_THAT(proxy[7], StringEq("8"));
EXPECT_THAT(proxy[8], StringEq("9"));
}
{
auto cord = absl::Cord(kLongString);
proxy.push_back(std::move(cord));
EXPECT_THAT(proxy[9], StringEq(kLongString));
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest, EmplaceBack) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.emplace_back(1);
proxy.emplace_back(2);
proxy.emplace_back(3);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
EXPECT_THAT(*field, ElementsAre(1, 2, 3));
}
TYPED_TEST(RepeatedFieldProxyTest, EmplaceBackMessageLvalueCopies) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
auto msg1 =
Arena::MakeUnique<RepeatedFieldProxyTestSimpleMessage>(this->arena());
auto* nested = msg1->mutable_nested();
proxy.emplace_back(*msg1);
EXPECT_NE(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
}
TYPED_TEST(RepeatedFieldProxyTest, EmplaceBackMessageRvalueDoesNotCopy) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
auto msg1 =
Arena::MakeUnique<RepeatedFieldProxyTestSimpleMessage>(this->arena());
auto* nested = msg1->mutable_nested();
proxy.emplace_back(std::move(*msg1));
EXPECT_EQ(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
}
template <typename FieldType, template <typename> class ProxyType,
typename StringType>
void TestEmplaceBackVanillaString(const FieldType& field,
ProxyType<StringType> proxy) {
// Test that `emplace_back` with no arguments inserts an empty string.
EXPECT_EQ(proxy.emplace_back(), "");
// Test that `emplace_back` returns a reference to the inserted element.
EXPECT_EQ(proxy.emplace_back("1"), "1");
// Test reflexive insertions.
proxy.emplace_back(proxy[1]);
proxy.emplace_back(StrAs<std::string>("2"));
proxy.emplace_back(StrAs<absl::string_view>("3"));
const char* c_str = "4";
proxy.emplace_back(c_str);
auto moved_string = std::string(kLongString);
const char* moved_string_ptr = moved_string.data();
proxy.emplace_back(std::move(moved_string));
EXPECT_EQ(proxy[6].data(), moved_string_ptr);
auto copied_string = std::string(kLongString);
const char* copied_string_ptr = copied_string.data();
proxy.emplace_back(copied_string);
EXPECT_NE(proxy[7].data(), copied_string_ptr);
EXPECT_EQ(copied_string, kLongString);
if constexpr (std::is_same_v<StringType, std::string>) {
proxy.emplace_back(10, 'a');
const char* c_str_with_len = "5";
proxy.emplace_back(c_str_with_len, std::strlen(c_str_with_len));
const char* c_str_with_len2 = "60";
proxy.emplace_back(c_str_with_len2, 2);
std::string string_to_copy_from = "123456789";
auto start_it = absl::c_find(string_to_copy_from, '3');
auto end_it = absl::c_find(string_to_copy_from, '7');
proxy.emplace_back(start_it, end_it);
proxy.emplace_back(absl::c_find(string_to_copy_from, '1'),
absl::c_find(string_to_copy_from, '4'));
}
if constexpr (std::is_same_v<StringType, std::string>) {
EXPECT_THAT(proxy, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString, "aaaaaaaaaa", "5", "60", "3456",
"123"));
EXPECT_THAT(*field, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString, "aaaaaaaaaa", "5", "60",
"3456", "123"));
} else {
EXPECT_THAT(proxy, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString));
EXPECT_THAT(*field, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString));
}
}
TYPED_TEST(RepeatedFieldProxyTest, EmplaceBackStdString) {
auto field = this->template MakeRepeatedFieldContainer<std::string>();
TestEmplaceBackVanillaString(field, field.MakeProxy());
}
TYPED_TEST(RepeatedFieldProxyTest, EmplaceBackStringView) {
auto field = this->template MakeRepeatedFieldContainer<absl::string_view>();
auto proxy = field.MakeProxy();
TestEmplaceBackVanillaString(field, proxy);
// Check that we don't leak an `std::string` through the `emplace_back` API.
static_assert(
std::is_same_v<decltype(proxy.emplace_back("1")), absl::string_view>);
}
TYPED_TEST(RepeatedFieldProxyTest, EmplaceBackCord) {
auto field = this->template MakeRepeatedFieldContainer<absl::Cord>();
auto proxy = field.MakeProxy();
proxy.emplace_back("1");
proxy.emplace_back(StrAs<absl::string_view>("2"));
absl::Cord cord3 = absl::Cord(kLongString);
proxy.emplace_back(std::move(cord3));
absl::Cord cord4 = absl::Cord(kLongString);
proxy.emplace_back(cord4);
EXPECT_THAT(proxy, ElementsAre("1", "2", kLongString, kLongString));
EXPECT_THAT(*field, ElementsAre("1", "2", kLongString, kLongString));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Iterators) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
auto it = proxy.begin();
EXPECT_EQ(*it, 1);
EXPECT_EQ(*(++it), 2);
EXPECT_EQ(*(++it), 3);
EXPECT_EQ(++it, proxy.end());
// Post-increment
it = proxy.begin();
EXPECT_EQ(*(it++), 1);
// Pre-decrement
it = proxy.end();
EXPECT_EQ(*(--it), 3);
// Post-decrement
EXPECT_EQ(*(it--), 3);
EXPECT_EQ(*it, 2);
// Equality
EXPECT_TRUE(proxy.begin() == proxy.begin());
EXPECT_TRUE(proxy.end() == proxy.end());
EXPECT_FALSE(proxy.begin() == proxy.end());
// Inequality
EXPECT_FALSE(proxy.begin() != proxy.begin());
EXPECT_FALSE(proxy.end() != proxy.end());
EXPECT_TRUE(proxy.begin() != proxy.end());
// Random access
it = proxy.begin();
EXPECT_EQ(*(it + 0), 1);
EXPECT_EQ(*(it + 1), 2);
EXPECT_EQ(*(it + 2), 3);
// Add assign
it += 2;
EXPECT_EQ(*(it - 1), 2);
// Subtract assign
it -= 1;
EXPECT_EQ(*it, 2);
// Indexing
EXPECT_EQ(it[1], 3);
// Reverse iterators
auto rit = proxy.rbegin();
EXPECT_EQ(*rit, 3);
EXPECT_EQ(*(++rit), 2);
EXPECT_EQ(*(++rit), 1);
EXPECT_EQ(++rit, proxy.rend());
google::protobuf::sort(proxy.begin(), proxy.end(), std::greater<int>());
EXPECT_THAT(proxy, ElementsAre(3, 2, 1));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, IteratorsDoNotLeakReferences) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
auto const_proxy = field.MakeConstProxy();
// `cbegin()` on mutable proxies should return the same type as `begin()` on
// const proxies.
static_assert(
std::is_same_v<decltype(proxy.cbegin()), decltype(const_proxy.begin())>);
// All iterator types should dereference to element values, not references.
static_assert(std::is_same_v<decltype(*proxy.begin()), ElementType>);
static_assert(std::is_same_v<decltype(*proxy.cbegin()), ElementType>);
static_assert(std::is_same_v<decltype(*proxy.rbegin()), ElementType>);
// Indexing should also dereference to element values, not references.
static_assert(std::is_same_v<decltype(proxy.begin()[0]), ElementType>);
static_assert(std::is_same_v<decltype(proxy.cbegin()[0]), ElementType>);
static_assert(std::is_same_v<decltype(proxy.rbegin()[0]), ElementType>);
}
TYPED_TEST(RepeatedFieldProxyTest, EnumIterators) {
auto field =
this->template MakeRepeatedFieldContainer<TestRepeatedEnumProxy::Enum>();
auto proxy = field.MakeProxy();
proxy.push_back(TestRepeatedEnumProxy::FOO);
proxy.push_back(TestRepeatedEnumProxy::BAR);
proxy.push_back(TestRepeatedEnumProxy::BAZ);
auto it = proxy.begin();
EXPECT_EQ(*it, TestRepeatedEnumProxy::FOO);
EXPECT_EQ(it[1], TestRepeatedEnumProxy::BAR);
auto rit = proxy.rbegin();
EXPECT_EQ(*rit, TestRepeatedEnumProxy::BAZ);
EXPECT_EQ(rit[1], TestRepeatedEnumProxy::BAR);
}
TYPED_TEST(RepeatedFieldProxyTest, EnumIteratorsDoNotLeakReferences) {
auto field =
this->template MakeRepeatedFieldContainer<TestRepeatedEnumProxy::Enum>();
auto proxy = field.MakeProxy();
auto const_proxy = field.MakeConstProxy();
// `cbegin()` on mutable proxies should return the same type as `begin()` on
// const proxies.
static_assert(
std::is_same_v<decltype(proxy.cbegin()), decltype(const_proxy.begin())>);
// All iterator types should dereference to element values, not references.
static_assert(
std::is_same_v<decltype(*proxy.begin()), TestRepeatedEnumProxy::Enum>);
static_assert(
std::is_same_v<decltype(*proxy.cbegin()), TestRepeatedEnumProxy::Enum>);
static_assert(
std::is_same_v<decltype(*proxy.rbegin()), TestRepeatedEnumProxy::Enum>);
// Indexing should also dereference to element values, not references.
static_assert(
std::is_same_v<decltype(proxy.begin()[0]), TestRepeatedEnumProxy::Enum>);
static_assert(
std::is_same_v<decltype(proxy.cbegin()[0]), TestRepeatedEnumProxy::Enum>);
static_assert(
std::is_same_v<decltype(proxy.rbegin()[0]), TestRepeatedEnumProxy::Enum>);
}
TYPED_TEST(RepeatedStringFieldProxyTest, Iterators) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.push_back("1");
proxy.push_back("2");
proxy.push_back("3");
auto it = proxy.begin();
EXPECT_THAT(*it, StringEq("1"));
EXPECT_THAT(*(++it), StringEq("2"));
EXPECT_THAT(*(++it), StringEq("3"));
EXPECT_EQ(++it, proxy.end());
// Post-increment
it = proxy.begin();
EXPECT_THAT(*(it++), StringEq("1"));
// Pre-decrement
it = proxy.end();
EXPECT_THAT(*(--it), StringEq("3"));
// Post-decrement
EXPECT_THAT(*(it--), StringEq("3"));
EXPECT_THAT(*it, StringEq("2"));
// Equality
EXPECT_TRUE(proxy.begin() == proxy.begin());
EXPECT_TRUE(proxy.end() == proxy.end());
EXPECT_FALSE(proxy.begin() == proxy.end());
// Inequality
EXPECT_FALSE(proxy.begin() != proxy.begin());
EXPECT_FALSE(proxy.end() != proxy.end());
EXPECT_TRUE(proxy.begin() != proxy.end());
// Random access
it = proxy.begin();
EXPECT_THAT(*(it + 0), StringEq("1"));
EXPECT_THAT(*(it + 1), StringEq("2"));
EXPECT_THAT(*(it + 2), StringEq("3"));
// Add assign
it += 2;
EXPECT_THAT(*(it - 1), StringEq("2"));
// Subtract assign
it -= 1;
EXPECT_THAT(*it, StringEq("2"));
// Indexing
EXPECT_THAT(it[1], StringEq("3"));
// Reverse iterators
auto rit = proxy.rbegin();
EXPECT_THAT(*rit, StringEq("3"));
EXPECT_THAT(*(++rit), StringEq("2"));
EXPECT_THAT(*(++rit), StringEq("1"));
EXPECT_EQ(++rit, proxy.rend());
// Const iterators
auto cit = proxy.cbegin();
EXPECT_THAT(*cit, StringEq("1"));
EXPECT_THAT(*(++cit), StringEq("2"));
EXPECT_THAT(*(++cit), StringEq("3"));
EXPECT_EQ(++cit, proxy.cend());
}
TYPED_TEST(RepeatedFieldProxyTest, IteratorMutation) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
proxy.emplace_back().set_value(1);
proxy.emplace_back().set_value(2);
proxy.emplace_back().set_value(3);
{
auto it = proxy.begin();
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(4);
*it = msg;
msg.set_value(5);
*(++it) = std::move(msg);
}
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 4)pb"),
EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 4)pb"),
EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 3)pb")));
{
auto rit = proxy.rbegin();
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(6);
*rit = msg;
msg.set_value(7);
*(++rit) = std::move(msg);
}
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 4)pb"),
EqualsProto(R"pb(value: 7)pb"),
EqualsProto(R"pb(value: 6)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 4)pb"),
EqualsProto(R"pb(value: 7)pb"),
EqualsProto(R"pb(value: 6)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, ConstIterators) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
auto proxy = field.MakeConstProxy();
auto it = proxy.cbegin();
EXPECT_EQ(*it, 1);
EXPECT_EQ(*(++it), 2);
EXPECT_EQ(*(++it), 3);
EXPECT_EQ(++it, proxy.cend());
auto rit = proxy.rbegin();
EXPECT_EQ(*rit, 3);
EXPECT_EQ(*(++rit), 2);
EXPECT_EQ(*(++rit), 1);
EXPECT_EQ(++rit, proxy.rend());
}
TYPED_TEST(RepeatedFieldProxyTest, StringViewIteratorsElementAccess) {
auto field = this->template MakeRepeatedFieldContainer<absl::string_view>();
auto proxy = field.MakeProxy();
proxy.push_back("first");
proxy.push_back("second");
proxy.push_back("3rd");
auto it = proxy.begin();
EXPECT_EQ(it->size(), 5);
EXPECT_EQ(it->at(0), 'f');
++it;
EXPECT_EQ(it->size(), 6);
++it;
EXPECT_EQ(it->size(), 3);
++it;
EXPECT_TRUE(it == proxy.end());
google::protobuf::sort(proxy.begin(), proxy.end(),
[](absl::string_view a, absl::string_view b) {
return a.size() < b.size();
});
EXPECT_THAT(proxy, ElementsAre("3rd", "first", "second"));
}
TYPED_TEST(RepeatedFieldProxyTest, StringViewIteratorsNoStdStringLeak) {
auto field = this->template MakeRepeatedFieldContainer<absl::string_view>();
auto proxy = field.MakeProxy();
static constexpr bool kOrProxy = TypeParam::kUseRepeatedFieldOrProxy;
// Check that we don't leak an `std::string` through the iterator.
static_assert(
std::is_same_v<decltype(proxy.begin()),
RepeatedFieldProxyIteratorImpl<
absl::string_view, /*kReverse=*/false, kOrProxy>>);
static_assert(
std::is_same_v<decltype(proxy.end()),
RepeatedFieldProxyIteratorImpl<
absl::string_view, /*kReverse=*/false, kOrProxy>>);
static_assert(std::is_same_v<
decltype(proxy.cbegin()),
RepeatedFieldProxyIteratorImpl<const absl::string_view,
/*kReverse=*/false, kOrProxy>>);
static_assert(std::is_same_v<
decltype(proxy.cend()),
RepeatedFieldProxyIteratorImpl<const absl::string_view,
/*kReverse=*/false, kOrProxy>>);
static_assert(
std::is_same_v<decltype(proxy.rbegin()),
RepeatedFieldProxyIteratorImpl<
absl::string_view, /*kReverse=*/true, kOrProxy>>);
static_assert(
std::is_same_v<decltype(proxy.rend()),
RepeatedFieldProxyIteratorImpl<
absl::string_view, /*kReverse=*/true, kOrProxy>>);
auto it = proxy.begin();
static_assert(
std::is_same_v<absl::remove_cvref_t<decltype(*it)>, absl::string_view>);
}
// Helper type trait to check if `T1` and `T2` can be compared with `==`.
//
// This essentially does a "does this compile" check for the equality operator.
// In C++, a template specialization is ignored if, when substituting the
// template parameters, any of the specialization does not compile.
//
// If operator==(T1, T2) is not defined, `equality_comparable` will fall back to
// the default implementation, which is false.
template <typename T1, typename T2, typename = void>
static constexpr bool is_equality_comparable = false;
template <typename T1, typename T2>
static constexpr bool is_equality_comparable<
T1, T2, std::void_t<decltype(std::declval<T1>() == std::declval<T2>())>> =
true;
// Verify that equality_comparable works as expected for a few types:
static_assert(is_equality_comparable<int32_t, int32_t>);
static_assert(is_equality_comparable<int32_t, size_t>);
static_assert(!is_equality_comparable<int32_t, std::string>);
template <typename T1, typename T2>
constexpr bool TypesIncompatible() {
return !std::is_assignable_v<T1, T2> && !std::is_assignable_v<T2, T1> &&
!std::is_constructible_v<T1, T2> && !std::is_constructible_v<T2, T1> &&
!is_equality_comparable<T1, T2> && !is_equality_comparable<T2, T1>;
}
template <typename ElementType>
constexpr bool IteratorsIncompatible() {
return TypesIncompatible<
decltype(std::declval<RepeatedFieldProxy<ElementType>>().begin()),
decltype(std::declval<RepeatedFieldOrProxy<ElementType>>()
.begin())>() &&
TypesIncompatible<
decltype(std::declval<RepeatedFieldProxy<ElementType>>().cbegin()),
decltype(std::declval<RepeatedFieldOrProxy<ElementType>>()
.cbegin())>() &&
TypesIncompatible<
decltype(std::declval<RepeatedFieldProxy<ElementType>>().rbegin()),
decltype(std::declval<RepeatedFieldOrProxy<ElementType>>()
.rbegin())>();
}
TEST(RepeatedFieldProxyIteratorTest, ProxyAndOrProxyIteratorsIncompatible) {
EXPECT_TRUE(IteratorsIncompatible<bool>());
EXPECT_TRUE(IteratorsIncompatible<int32_t>());
EXPECT_TRUE(IteratorsIncompatible<int64_t>());
EXPECT_TRUE(IteratorsIncompatible<uint32_t>());
EXPECT_TRUE(IteratorsIncompatible<uint32_t>());
EXPECT_TRUE(IteratorsIncompatible<float>());
EXPECT_TRUE(IteratorsIncompatible<double>());
EXPECT_TRUE(IteratorsIncompatible<absl::string_view>());
EXPECT_TRUE(IteratorsIncompatible<std::string>());
EXPECT_TRUE(IteratorsIncompatible<absl::Cord>());
EXPECT_TRUE(IteratorsIncompatible<RepeatedFieldProxyTestSimpleMessage>());
}
TYPED_TEST(RepeatedNumericFieldProxyTest, PopBack) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
auto proxy = field.MakeProxy();
proxy.pop_back();
EXPECT_THAT(proxy, ElementsAre(1));
EXPECT_THAT(*field, ElementsAre(1));
}
TYPED_TEST(RepeatedFieldProxyTest, PopBackMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
auto proxy = field.MakeProxy();
proxy.pop_back();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, PopBack) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
auto proxy = field.MakeProxy();
proxy.pop_back();
EXPECT_THAT(proxy, ElementsAre(StringEq("1")));
EXPECT_THAT(*field, ElementsAre(StringEq("1")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Clear) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
auto proxy = field.MakeProxy();
proxy.clear();
EXPECT_THAT(proxy, IsEmpty());
EXPECT_THAT(*field, IsEmpty());
}
TYPED_TEST(RepeatedFieldProxyTest, ClearMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
auto proxy = field.MakeProxy();
proxy.clear();
EXPECT_THAT(proxy, IsEmpty());
EXPECT_THAT(*field, IsEmpty());
}
TYPED_TEST(RepeatedStringFieldProxyTest, Clear) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
auto proxy = field.MakeProxy();
proxy.clear();
EXPECT_THAT(proxy, IsEmpty());
EXPECT_THAT(*field, IsEmpty());
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Erase) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
auto proxy = field.MakeProxy();
proxy.erase(absl::c_find_if(proxy, [](auto value) { return value == 2; }));
EXPECT_THAT(proxy, ElementsAre(1, 3));
EXPECT_THAT(*field, ElementsAre(1, 3));
}
TYPED_TEST(RepeatedStringFieldProxyTest, Erase) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
this->Add(field, "3");
auto proxy = field.MakeProxy();
proxy.erase(proxy.begin() + 1);
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("3")));
EXPECT_THAT(*field, ElementsAre(StringEq("1"), StringEq("3")));
}
TYPED_TEST(RepeatedFieldProxyTest, Erase) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
field->Add()->set_value(3);
auto proxy = field.MakeProxy();
proxy.erase(absl::c_find_if(
proxy, [](const RepeatedFieldProxyTestSimpleMessage& msg) {
return msg.value() == 2;
}));
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, EraseRange) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
field->Add(4);
auto proxy = field.MakeProxy();
auto it = absl::c_find_if(proxy, [](auto value) { return value == 2; });
proxy.erase(it, it + 2);
EXPECT_THAT(proxy, ElementsAre(1, 4));
EXPECT_THAT(*field, ElementsAre(1, 4));
}
TYPED_TEST(RepeatedStringFieldProxyTest, EraseRange) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
this->Add(field, "3");
this->Add(field, "4");
auto proxy = field.MakeProxy();
auto it = proxy.begin() + 1;
proxy.erase(it, it + 2);
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("4")));
EXPECT_THAT(*field, ElementsAre(StringEq("1"), StringEq("4")));
}
TYPED_TEST(RepeatedFieldProxyTest, EraseRange) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
field->Add()->set_value(3);
field->Add()->set_value(4);
auto proxy = field.MakeProxy();
auto it = absl::c_find_if(proxy,
[](const RepeatedFieldProxyTestSimpleMessage& msg) {
return msg.value() == 2;
});
proxy.erase(it, it + 2);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 4)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 4)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Proto2Erase) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
field->Add(4);
auto proxy = field.MakeProxy();
size_t count = google::protobuf::erase(proxy, 2);
EXPECT_EQ(count, 1);
EXPECT_THAT(proxy, ElementsAre(1, 3, 4));
EXPECT_THAT(*field, ElementsAre(1, 3, 4));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Proto2EraseIf) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
field->Add(3);
field->Add(4);
auto proxy = field.MakeProxy();
size_t count = google::protobuf::erase_if(proxy, [this, &field](auto&& x) {
this->template VerifyLambdaTypeRequirements<decltype(x)>(field, x);
return x > ElementType{2};
});
EXPECT_EQ(count, 2);
EXPECT_THAT(proxy, ElementsAre(1, 2));
EXPECT_THAT(*field, ElementsAre(1, 2));
}
TYPED_TEST(RepeatedStringFieldProxyTest, Proto2Erase) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
this->Add(field, "3");
this->Add(field, "4");
auto proxy = field.MakeProxy();
size_t count = google::protobuf::erase(proxy, "3");
EXPECT_EQ(count, 1);
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2"), StringEq("4")));
EXPECT_THAT(*field, ElementsAre(StringEq("1"), StringEq("2"), StringEq("4")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, Proto2EraseIf) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
this->Add(field, "3");
this->Add(field, "4");
auto proxy = field.MakeProxy();
size_t count = google::protobuf::erase_if(proxy, [this](auto&& s) {
this->template VerifyLambdaTypeRequirements<decltype(s)>();
return s == "2" || s == "4";
});
EXPECT_EQ(count, 2);
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("3")));
EXPECT_THAT(*field, ElementsAre(StringEq("1"), StringEq("3")));
}
TYPED_TEST(RepeatedFieldProxyTest, Proto2EraseIfMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
field->Add()->set_value(3);
field->Add()->set_value(4);
auto proxy = field.MakeProxy();
size_t count = google::protobuf::erase_if(
proxy, [](const auto& msg) { return msg.value() % 2 == 0; });
EXPECT_EQ(count, 2);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, CopyAssign) {
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
field2->Add(3);
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
proxy1.assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(2, 3));
EXPECT_THAT(*field1, ElementsAre(2, 3));
EXPECT_THAT(proxy2, ElementsAre(2, 3));
EXPECT_THAT(*field2, ElementsAre(2, 3));
}
TYPED_TEST(RepeatedStringFieldProxyTest, CopyAssign) {
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
this->Add(field2, "3");
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
proxy1.assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field1, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(proxy2, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field2, ElementsAre(StringEq("2"), StringEq("3")));
}
TYPED_TEST(RepeatedFieldProxyTest, CopyAssignMessage) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
field2->Add()->set_value(3);
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
proxy1.assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(proxy2, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field2, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, AssignIteratorRange) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
auto proxy = field.MakeProxy();
std::vector<ElementType> ints = {2, 3};
proxy.assign(ints.begin(), ints.end());
EXPECT_THAT(proxy, ElementsAre(2, 3));
EXPECT_THAT(*field, ElementsAre(2, 3));
// Assign a subrange of `ints`.
proxy.assign(ints.begin() + 1, ints.end());
EXPECT_THAT(proxy, ElementsAre(3));
EXPECT_THAT(*field, ElementsAre(3));
}
TYPED_TEST(RepeatedStringFieldProxyTest, AssignIteratorRange) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
auto proxy = field.MakeProxy();
std::vector<absl::string_view> strs = {"2", "3"};
proxy.assign(strs.begin(), strs.end());
EXPECT_THAT(proxy, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field, ElementsAre(StringEq("2"), StringEq("3")));
// Assign a subrange of `strs`.
proxy.assign(strs.begin() + 1, strs.end());
EXPECT_THAT(proxy, ElementsAre(StringEq("3")));
EXPECT_THAT(*field, ElementsAre(StringEq("3")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, AssignIteratorRangeWithStdStrings) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
auto proxy = field.MakeProxy();
std::vector<std::string> strs = {"2", "3"};
proxy.assign(strs.begin(), strs.end());
EXPECT_THAT(proxy, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field, ElementsAre(StringEq("2"), StringEq("3")));
// Assign a subrange of `strs`.
proxy.assign(strs.begin() + 1, strs.end());
EXPECT_THAT(proxy, ElementsAre(StringEq("3")));
EXPECT_THAT(*field, ElementsAre(StringEq("3")));
}
TYPED_TEST(RepeatedFieldProxyTest, AssignMessageIteratorRange) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
std::vector<RepeatedFieldProxyTestSimpleMessage> msgs(2);
msgs[0].set_value(2);
msgs[1].set_value(3);
auto proxy = field.MakeProxy();
proxy.assign(msgs.begin(), msgs.end());
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
// Assign a subrange of `msgs`.
proxy.assign(msgs.begin() + 1, msgs.end());
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 3)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, MoveAssign) {
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
field2->Add(3);
field2->Add(4);
const void* prev_data = field2->data();
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
if (this->UseArena()) {
size_t arena_memory = this->arena()->SpaceUsed();
proxy1.move_assign(proxy2);
// Verify that the move assignment did not allocate any additional memory.
EXPECT_EQ(this->arena()->SpaceUsed(), arena_memory);
} else {
proxy1.move_assign(proxy2);
}
// Verify that the move assignment did not allocate any additional memory.
EXPECT_EQ(field1->data(), prev_data);
EXPECT_THAT(proxy1, ElementsAre(2, 3, 4));
EXPECT_THAT(*field1, ElementsAre(2, 3, 4));
}
TYPED_TEST(RepeatedStringFieldProxyTest, MoveAssign) {
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
this->Add(field2, "3");
const void* prev_data = field2->data();
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
if (this->UseArena()) {
size_t arena_memory = this->arena()->SpaceUsed();
proxy1.move_assign(proxy2);
// Verify that the move assignment did not allocate any additional memory.
EXPECT_EQ(this->arena()->SpaceUsed(), arena_memory);
} else {
proxy1.move_assign(proxy2);
}
// Verify that the move assignment did not allocate any additional memory.
EXPECT_EQ(field1->data(), prev_data);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field1, ElementsAre(StringEq("2"), StringEq("3")));
}
TYPED_TEST(RepeatedFieldProxyTest, MoveAssignMessage) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
field2->Add()->set_value(3);
const void* prev_data = field2->data();
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
if (this->UseArena()) {
size_t arena_memory = this->arena()->SpaceUsed();
proxy1.move_assign(proxy2);
// Verify that the move assignment did not allocate any additional memory.
EXPECT_EQ(this->arena()->SpaceUsed(), arena_memory);
} else {
proxy1.move_assign(proxy2);
}
// Verify that the move assignment did not allocate any additional memory.
EXPECT_EQ(field1->data(), prev_data);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Reserve) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.reserve(10);
const void* data_before = field->data();
uint64_t space_used_before = 0;
if (this->arena() != nullptr) {
space_used_before = this->arena()->SpaceUsed();
}
for (int i = 0; i < 10; ++i) {
proxy.emplace_back(i);
}
// Verify that reserve() prevented resizing.
EXPECT_EQ(field->data(), data_before);
EXPECT_THAT(proxy, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
EXPECT_THAT(*field, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
if (this->arena() != nullptr) {
// In the arena case, we verify that no additional memory was allocated
// after the initial reserve().
EXPECT_EQ(space_used_before, this->arena()->SpaceUsed());
}
}
TYPED_TEST(RepeatedStringFieldProxyTest, Reserve) {
auto field = this->MakeRepeatedFieldContainer();
auto proxy = field.MakeProxy();
proxy.reserve(10);
const void* data_before = field->data();
for (int i = 0; i < 10; ++i) {
proxy.emplace_back(absl::StrFormat("%" PRId32, i));
}
// Verify that reserve() prevented resizing.
EXPECT_EQ(field->data(), data_before);
EXPECT_THAT(proxy, ElementsAre(StringEq("0"), StringEq("1"), StringEq("2"),
StringEq("3"), StringEq("4"), StringEq("5"),
StringEq("6"), StringEq("7"), StringEq("8"),
StringEq("9")));
EXPECT_THAT(*field, ElementsAre(StringEq("0"), StringEq("1"), StringEq("2"),
StringEq("3"), StringEq("4"), StringEq("5"),
StringEq("6"), StringEq("7"), StringEq("8"),
StringEq("9")));
}
TYPED_TEST(RepeatedFieldProxyTest, ReserveMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
auto proxy = field.MakeProxy();
proxy.reserve(10);
const void* data_before = field->data();
for (int i = 0; i < 10; ++i) {
proxy.emplace_back().set_value(i);
}
// Verify that reserve() prevented resizing.
EXPECT_EQ(field->data(), data_before);
EXPECT_THAT(
proxy,
ElementsAre(
EqualsProto(R"pb(value: 0)pb"), EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"), EqualsProto(R"pb(value: 3)pb"),
EqualsProto(R"pb(value: 4)pb"), EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 6)pb"), EqualsProto(R"pb(value: 7)pb"),
EqualsProto(R"pb(value: 8)pb"), EqualsProto(R"pb(value: 9)pb")));
EXPECT_THAT(
*field,
ElementsAre(
EqualsProto(R"pb(value: 0)pb"), EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"), EqualsProto(R"pb(value: 3)pb"),
EqualsProto(R"pb(value: 4)pb"), EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 6)pb"), EqualsProto(R"pb(value: 7)pb"),
EqualsProto(R"pb(value: 8)pb"), EqualsProto(R"pb(value: 9)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Swap) {
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
field2->Add(3);
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
proxy1.swap(proxy2);
EXPECT_THAT(proxy1, ElementsAre(2, 3));
EXPECT_THAT(proxy2, ElementsAre(1));
}
TYPED_TEST(RepeatedStringFieldProxyTest, Swap) {
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
this->Add(field2, "3");
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
proxy1.swap(proxy2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(proxy2, ElementsAre(StringEq("1")));
}
TYPED_TEST(RepeatedFieldProxyTest, SwapMessage) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
field2->Add()->set_value(3);
auto proxy1 = field1.MakeProxy();
auto proxy2 = field2.MakeProxy();
proxy1.swap(proxy2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(proxy2, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, Resize) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
auto proxy = field.MakeProxy();
proxy.resize(4);
EXPECT_THAT(proxy, ElementsAre(1, 2, 0, 0));
EXPECT_THAT(*field, ElementsAre(1, 2, 0, 0));
proxy.resize(1);
EXPECT_THAT(proxy, ElementsAre(1));
EXPECT_THAT(*field, ElementsAre(1));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, ResizeWithValue) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
auto proxy = field.MakeProxy();
proxy.resize(3, 10);
EXPECT_THAT(proxy, ElementsAre(1, 10, 10));
EXPECT_THAT(*field, ElementsAre(1, 10, 10));
proxy.resize(2, 20);
EXPECT_THAT(proxy, ElementsAre(1, 10));
EXPECT_THAT(*field, ElementsAre(1, 10));
}
TYPED_TEST(RepeatedStringFieldProxyTest, Resize) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
auto proxy = field.MakeProxy();
proxy.resize(4);
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2"), StringEq(""),
StringEq("")));
EXPECT_THAT(*field, ElementsAre(StringEq("1"), StringEq("2"), StringEq(""),
StringEq("")));
proxy.resize(1);
EXPECT_THAT(proxy, ElementsAre(StringEq("1")));
EXPECT_THAT(*field, ElementsAre(StringEq("1")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, ResizeWithValue) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
auto proxy = field.MakeProxy();
if constexpr (std::is_same_v<ElementType, absl::Cord>) {
const auto value = absl::Cord("10");
proxy.resize(3, value);
} else {
proxy.resize(3, "10");
}
EXPECT_THAT(proxy,
ElementsAre(StringEq("1"), StringEq("10"), StringEq("10")));
EXPECT_THAT(*field,
ElementsAre(StringEq("1"), StringEq("10"), StringEq("10")));
if constexpr (std::is_same_v<ElementType, absl::Cord>) {
proxy.resize(2, absl::Cord("20"));
} else {
proxy.resize(2, "20");
}
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("10")));
EXPECT_THAT(*field, ElementsAre(StringEq("1"), StringEq("10")));
}
TYPED_TEST(RepeatedFieldProxyTest, ResizeMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
auto proxy = field.MakeProxy();
proxy.resize(4);
EXPECT_THAT(
proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"), EqualsProto(R"pb()pb"),
EqualsProto(R"pb()pb")));
EXPECT_THAT(*field,
ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb()pb"), EqualsProto(R"pb()pb")));
proxy.resize(1);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
}
TYPED_TEST(RepeatedFieldProxyTest, ResizeMessageWithValue) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
auto proxy = field.MakeProxy();
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(10);
proxy.resize(3, msg);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 10)pb"),
EqualsProto(R"pb(value: 10)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 10)pb"),
EqualsProto(R"pb(value: 10)pb")));
RepeatedFieldProxyTestSimpleMessage msg2;
msg2.set_value(20);
proxy.resize(2, msg2);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 10)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 10)pb")));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, RebindConstProxy) {
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
auto proxy = field1.MakeConstProxy();
proxy = field2.MakeConstProxy();
// Proxy should be rebound to field2 without having modified either field.
EXPECT_THAT(proxy, ElementsAre(2));
EXPECT_THAT(*field1, ElementsAre(1));
EXPECT_THAT(*field2, ElementsAre(2));
// Verify that mutable proxies can't be rebound.
static_assert(!std::is_copy_assignable_v<decltype(field1.MakeProxy())>);
}
TYPED_TEST(RepeatedStringFieldProxyTest, RebindConstProxy) {
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
auto proxy = field1.MakeConstProxy();
proxy = field2.MakeConstProxy();
// Proxy should be rebound to field2 without having modified either field.
EXPECT_THAT(proxy, ElementsAre(StringEq("2")));
EXPECT_THAT(*field1, ElementsAre(StringEq("1")));
EXPECT_THAT(*field2, ElementsAre(StringEq("2")));
// Verify that mutable proxies can't be rebound.
static_assert(!std::is_copy_assignable_v<decltype(field1.MakeProxy())>);
}
TYPED_TEST(RepeatedFieldProxyTest, RebindConstMessageProxy) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
auto proxy = field1.MakeConstProxy();
proxy = field2.MakeConstProxy();
// Proxy should be rebound to field2 without having modified either field.
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
EXPECT_THAT(*field2, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
// Verify that mutable proxies can't be rebound.
static_assert(!std::is_copy_assignable_v<decltype(field1.MakeProxy())>);
}
TYPED_TEST(RepeatedFieldProxyTest, ExplicitConversionToLegacyRepeatedField) {
auto field = this->template MakeRepeatedFieldContainer<int>();
field->Add(1);
auto proxy = field.MakeProxy();
// Make an explicit conversion to RepeatedField. This will perform a deep
// copy.
RepeatedField<int> field2 = RepeatedField<int>(proxy);
EXPECT_THAT(field2, ElementsAre(1));
// `field2` should be a copy of the original field.
EXPECT_NE(&field2.Get(0), &field->Get(0));
}
TYPED_TEST(RepeatedFieldProxyTest, ExplicitConversionToLegacyRepeatedPtrField) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
auto proxy = field.MakeProxy();
// Make an explicit conversion to RepeatedPtrField. This will perform a deep
// copy.
RepeatedPtrField<RepeatedFieldProxyTestSimpleMessage> field2 =
RepeatedPtrField<RepeatedFieldProxyTestSimpleMessage>(proxy);
EXPECT_THAT(field2, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
// `field2` field should be a copy of the original field.
EXPECT_NE(&field2.Get(0), &field->Get(0));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, CSort) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(4);
field->Add(1);
field->Add(3);
field->Add(2);
auto proxy = field.MakeProxy();
google::protobuf::c_sort(proxy);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3, 4));
EXPECT_THAT(*field, ElementsAre(1, 2, 3, 4));
google::protobuf::c_sort(proxy, [this, &field](auto&& lhs, auto&& rhs) {
this->template VerifyLambdaTypeRequirements<decltype(lhs)>(field, lhs);
this->template VerifyLambdaTypeRequirements<decltype(rhs)>(field, rhs);
return std::greater{}(lhs, rhs);
});
EXPECT_THAT(proxy, ElementsAre(4, 3, 2, 1));
EXPECT_THAT(*field, ElementsAre(4, 3, 2, 1));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, StableCSort) {
auto field = this->MakeRepeatedFieldContainer();
field->Add(4);
field->Add(1);
field->Add(3);
field->Add(2);
auto proxy = field.MakeProxy();
google::protobuf::c_stable_sort(proxy);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3, 4));
EXPECT_THAT(*field, ElementsAre(1, 2, 3, 4));
google::protobuf::c_stable_sort(proxy, [this, &field](auto&& lhs, auto&& rhs) {
this->template VerifyLambdaTypeRequirements<decltype(lhs)>(field, lhs);
this->template VerifyLambdaTypeRequirements<decltype(rhs)>(field, rhs);
return std::greater{}(lhs, rhs);
});
EXPECT_THAT(proxy, ElementsAre(4, 3, 2, 1));
EXPECT_THAT(*field, ElementsAre(4, 3, 2, 1));
}
TYPED_TEST(RepeatedStringFieldProxyTest, CSort) {
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "d");
this->Add(field, "a");
this->Add(field, "c");
this->Add(field, "b");
auto proxy = field.MakeProxy();
google::protobuf::c_sort(proxy);
EXPECT_THAT(proxy, ElementsAre(StringEq("a"), StringEq("b"), StringEq("c"),
StringEq("d")));
EXPECT_THAT(*field, ElementsAre(StringEq("a"), StringEq("b"), StringEq("c"),
StringEq("d")));
google::protobuf::c_sort(proxy, this->StringCompare(std::greater{}));
EXPECT_THAT(proxy, ElementsAre(StringEq("d"), StringEq("c"), StringEq("b"),
StringEq("a")));
EXPECT_THAT(*field, ElementsAre(StringEq("d"), StringEq("c"), StringEq("b"),
StringEq("a")));
}
TYPED_TEST(RepeatedStringFieldProxyTest, StableCSort) {
auto field = this->MakeRepeatedFieldContainer();
// Use long strings to ensure the backing string does not use SSO, and we can
// compare `.data()` before and after stable sorting to determine relative
// order of equivalent strings.
const auto long_string_a = absl::StrCat("a", kLongString);
const auto long_string_b = absl::StrCat("b", kLongString);
this->Add(field, long_string_b);
this->Add(field, long_string_b);
this->Add(field, long_string_a);
this->Add(field, long_string_a);
const char* str1 = this->StartAddress(field->Get(0));
const char* str2 = this->StartAddress(field->Get(1));
const char* str3 = this->StartAddress(field->Get(2));
const char* str4 = this->StartAddress(field->Get(3));
auto proxy = field.MakeProxy();
google::protobuf::c_stable_sort(proxy);
EXPECT_THAT(proxy,
ElementsAre(StringEq(long_string_a), StringEq(long_string_a),
StringEq(long_string_b), StringEq(long_string_b)));
EXPECT_THAT(*field,
ElementsAre(StringEq(long_string_a), StringEq(long_string_a),
StringEq(long_string_b), StringEq(long_string_b)));
// Stable sort should preserve the relative order of elements that compare
// equal.
EXPECT_EQ(this->StartAddress(field->Get(0)), str3);
EXPECT_EQ(this->StartAddress(field->Get(1)), str4);
EXPECT_EQ(this->StartAddress(field->Get(2)), str1);
EXPECT_EQ(this->StartAddress(field->Get(3)), str2);
google::protobuf::c_stable_sort(proxy, this->StringCompare(std::greater{}));
EXPECT_THAT(proxy,
ElementsAre(StringEq(long_string_b), StringEq(long_string_b),
StringEq(long_string_a), StringEq(long_string_a)));
EXPECT_THAT(*field,
ElementsAre(StringEq(long_string_b), StringEq(long_string_b),
StringEq(long_string_a), StringEq(long_string_a)));
// Stable sort should preserve the relative order of elements that compare
// equal.
EXPECT_EQ(this->StartAddress(field->Get(0)), str1);
EXPECT_EQ(this->StartAddress(field->Get(1)), str2);
EXPECT_EQ(this->StartAddress(field->Get(2)), str3);
EXPECT_EQ(this->StartAddress(field->Get(3)), str4);
}
TYPED_TEST(RepeatedFieldProxyTest, CSortMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(4);
field->Add()->set_value(1);
field->Add()->set_value(3);
field->Add()->set_value(2);
auto proxy = field.MakeProxy();
google::protobuf::c_sort(proxy, [](const auto& a, const auto& b) {
return a.value() < b.value();
});
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb"),
EqualsProto(R"pb(value: 4)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb"),
EqualsProto(R"pb(value: 4)pb")));
}
TYPED_TEST(RepeatedFieldProxyTest, StableCSortMessage) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(2);
field->Add()->set_value(2);
field->Add()->set_value(1);
field->Add()->set_value(1);
const auto* msg1 = &field->Get(0);
const auto* msg2 = &field->Get(1);
const auto* msg3 = &field->Get(2);
const auto* msg4 = &field->Get(3);
auto proxy = field.MakeProxy();
google::protobuf::c_stable_sort(proxy, [](const auto& a, const auto& b) {
return a.value() < b.value();
});
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 2)pb")));
// Stable sort should preserve the relative order of elements that compare
// equal.
EXPECT_EQ(&field->Get(0), msg3);
EXPECT_EQ(&field->Get(1), msg4);
EXPECT_EQ(&field->Get(2), msg1);
EXPECT_EQ(&field->Get(3), msg2);
}
TYPED_TEST(RepeatedNumericFieldProxyTest,
RepeatedFieldOrProxyImplicitConversion) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
{
// Implicit conversion from RepeatedPtrField to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy = *field;
EXPECT_THAT(proxy, ElementsAre(1, 2));
}
{
// Implicit conversion from RepeatedPtrField* to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy = &*field;
EXPECT_THAT(proxy, ElementsAre(1, 2));
}
{
// Implicit conversion from RepeatedPtrField to const RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const ElementType> proxy = *field;
EXPECT_THAT(proxy, ElementsAre(1, 2));
}
{
// Implicit conversion from const RepeatedPtrField to const
// RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const ElementType> proxy = std::as_const(*field);
EXPECT_THAT(proxy, ElementsAre(1, 2));
}
{
// Implicit conversion from RepeatedFieldProxy to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(1, 2));
}
{
// Implicit conversion from RepeatedFieldProxy to const
// RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const ElementType> proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(1, 2));
}
}
TYPED_TEST(RepeatedStringFieldProxyTest,
RepeatedFieldOrProxyImplicitConversion) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
{
// Implicit conversion from RepeatedPtrField to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy = *field;
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2")));
}
{
// Implicit conversion from RepeatedPtrField* to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy = &*field;
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2")));
}
{
// Implicit conversion from RepeatedPtrField to const RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const ElementType> proxy = *field;
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2")));
}
{
// Implicit conversion from const RepeatedPtrField to const
// RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const ElementType> proxy = std::as_const(*field);
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2")));
}
{
// Implicit conversion from RepeatedFieldProxy to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2")));
}
{
// Implicit conversion from RepeatedFieldProxy to const
// RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const ElementType> proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(StringEq("1"), StringEq("2")));
}
}
TYPED_TEST(RepeatedFieldProxyTest, RepeatedFieldOrProxyImplicitConversion) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
{
// Implicit conversion from RepeatedPtrField to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy = *field;
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
// Implicit conversion from RepeatedPtrField* to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy = &*field;
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
// Implicit conversion from RepeatedPtrField to const RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const RepeatedFieldProxyTestSimpleMessage> proxy =
*field;
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
// Implicit conversion from const RepeatedPtrField to const
// RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const RepeatedFieldProxyTestSimpleMessage> proxy =
std::as_const(*field);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
// Implicit conversion from RepeatedFieldProxy to RepeatedFieldOrProxy.
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy =
field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
// Implicit conversion from RepeatedFieldProxy to const
// RepeatedFieldOrProxy.
RepeatedFieldOrProxy<const RepeatedFieldProxyTestSimpleMessage> proxy =
field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest,
RepeatedFieldOrProxyExplicitConversionToLegacy) {
using ElementType = typename TypeParam::ElementType;
auto field = this->MakeRepeatedFieldContainer();
field->Add(1);
field->Add(2);
RepeatedFieldOrProxy<ElementType> proxy = *field;
auto legacy = google::protobuf::RepeatedField<ElementType>(proxy);
EXPECT_THAT(legacy, ElementsAre(1, 2));
// `legacy` field should be a copy of the original field.
EXPECT_NE(&legacy.Get(0), &field->Get(0));
}
TYPED_TEST(RepeatedStringFieldProxyTest,
RepeatedFieldOrProxyExplicitConversionToLegacy) {
using ElementType = typename TypeParam::ElementType;
using LegacyType = typename RepeatedFieldTraits<ElementType>::type;
auto field = this->MakeRepeatedFieldContainer();
this->Add(field, "1");
this->Add(field, "2");
RepeatedFieldOrProxy<ElementType> proxy = *field;
auto legacy = LegacyType(proxy);
EXPECT_THAT(legacy, ElementsAre(StringEq("1"), StringEq("2")));
// `legacy` field should be a copy of the original field.
EXPECT_NE(&legacy.Get(0), &field->Get(0));
}
TYPED_TEST(RepeatedFieldProxyTest,
RepeatedFieldOrProxyExplicitConversionToLegacy) {
auto field = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy = *field;
auto legacy =
google::protobuf::RepeatedPtrField<RepeatedFieldProxyTestSimpleMessage>(proxy);
EXPECT_THAT(legacy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
// `legacy` field should be a copy of the original field.
EXPECT_NE(&legacy.Get(0), &field->Get(0));
}
TYPED_TEST(RepeatedNumericFieldProxyTest, RepeatedFieldOrProxyAssign) {
using ElementType = typename TypeParam::ElementType;
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
{
// Assign a RepeatedFieldOrProxy to a RepeatedFieldOrProxy:
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
RepeatedFieldOrProxy<ElementType> proxy2 = *field2;
proxy1.assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(2));
EXPECT_THAT(*field1, ElementsAre(2));
EXPECT_THAT(proxy2, ElementsAre(2));
EXPECT_THAT(*field2, ElementsAre(2));
}
field2->Add(3);
{
// Assign a RepeatedField to a RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
proxy1.assign(*field2);
EXPECT_THAT(proxy1, ElementsAre(2, 3));
EXPECT_THAT(*field1, ElementsAre(2, 3));
EXPECT_THAT(*field2, ElementsAre(2, 3));
}
}
TYPED_TEST(RepeatedStringFieldProxyTest, RepeatedFieldOrProxyAssign) {
using ElementType = typename TypeParam::ElementType;
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
{
// Assign a RepeatedFieldOrProxy to a RepeatedFieldOrProxy:
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
RepeatedFieldOrProxy<ElementType> proxy2 = *field2;
proxy1.assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2")));
EXPECT_THAT(*field1, ElementsAre(StringEq("2")));
EXPECT_THAT(proxy2, ElementsAre(StringEq("2")));
EXPECT_THAT(*field2, ElementsAre(StringEq("2")));
}
this->Add(field2, "3");
{
// Assign a RepeatedField to a RepeatedFieldOrProxy.
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
proxy1.assign(*field2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field1, ElementsAre(StringEq("2"), StringEq("3")));
EXPECT_THAT(*field2, ElementsAre(StringEq("2"), StringEq("3")));
}
}
TYPED_TEST(RepeatedFieldProxyTest, RepeatedFieldOrProxyAssign) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
{
// Assign a RepeatedFieldOrProxy to a RepeatedFieldOrProxy:
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy1 = *field1;
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy2 = *field2;
proxy1.assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(proxy2, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field2, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
}
field2->Add()->set_value(3);
{
// Assign a RepeatedField to a RepeatedFieldOrProxy.
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy1 = *field1;
proxy1.assign(*field2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field2, ElementsAre(EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest, RepeatedFieldOrProxyMoveAssign) {
using ElementType = typename TypeParam::ElementType;
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
{
// Move-assign a RepeatedFieldOrProxy to a RepeatedFieldOrProxy:
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
RepeatedFieldOrProxy<ElementType> proxy2 = *field2;
proxy1.move_assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(2));
EXPECT_THAT(*field1, ElementsAre(2));
}
{
// Move-assign a RepeatedField to a RepeatedFieldOrProxy.
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(3);
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
proxy1.move_assign(*field2);
EXPECT_THAT(proxy1, ElementsAre(3));
EXPECT_THAT(*field1, ElementsAre(3));
}
}
TYPED_TEST(RepeatedStringFieldProxyTest, RepeatedFieldOrProxyMoveAssign) {
using ElementType = typename TypeParam::ElementType;
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
{
// Move-assign a RepeatedFieldOrProxy to a RepeatedFieldOrProxy:
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
RepeatedFieldOrProxy<ElementType> proxy2 = *field2;
proxy1.move_assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2")));
EXPECT_THAT(*field1, ElementsAre(StringEq("2")));
}
{
// Move-assign a RepeatedField to a RepeatedFieldOrProxy.
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "3");
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
proxy1.move_assign(*field2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("3")));
EXPECT_THAT(*field1, ElementsAre(StringEq("3")));
}
}
TYPED_TEST(RepeatedFieldProxyTest, RepeatedFieldOrProxyMoveAssign) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
{
// Move-assign a RepeatedFieldOrProxy to a RepeatedFieldOrProxy:
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy1 = *field1;
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy2 = *field2;
proxy1.move_assign(proxy2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
}
{
// Move-assign a RepeatedField to a RepeatedFieldOrProxy.
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(3);
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy1 = *field1;
proxy1.move_assign(*field2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 3)pb")));
}
}
TYPED_TEST(RepeatedNumericFieldProxyTest, RepeatedFieldOrProxySwap) {
using ElementType = typename TypeParam::ElementType;
auto field1 = this->MakeRepeatedFieldContainer();
field1->Add(1);
auto field2 = this->MakeRepeatedFieldContainer();
field2->Add(2);
{
// Swap two RepeatedFieldOrProxies:
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
RepeatedFieldOrProxy<ElementType> proxy2 = *field2;
proxy1.swap(proxy2);
EXPECT_THAT(proxy1, ElementsAre(2));
EXPECT_THAT(*field1, ElementsAre(2));
EXPECT_THAT(proxy2, ElementsAre(1));
EXPECT_THAT(*field2, ElementsAre(1));
}
{
// Swap a RepeatedFieldOrProxy with a RepeatedField.
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
proxy1.swap(*field2);
EXPECT_THAT(proxy1, ElementsAre(1));
EXPECT_THAT(*field1, ElementsAre(1));
EXPECT_THAT(*field2, ElementsAre(2));
}
}
TYPED_TEST(RepeatedStringFieldProxyTest, RepeatedFieldOrProxySwap) {
using ElementType = typename TypeParam::ElementType;
auto field1 = this->MakeRepeatedFieldContainer();
this->Add(field1, "1");
auto field2 = this->MakeRepeatedFieldContainer();
this->Add(field2, "2");
{
// Swap two RepeatedFieldOrProxies:
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
RepeatedFieldOrProxy<ElementType> proxy2 = *field2;
proxy1.swap(proxy2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("2")));
EXPECT_THAT(*field1, ElementsAre(StringEq("2")));
EXPECT_THAT(proxy2, ElementsAre(StringEq("1")));
EXPECT_THAT(*field2, ElementsAre(StringEq("1")));
}
{
// Swap a RepeatedFieldOrProxy with a RepeatedField.
RepeatedFieldOrProxy<ElementType> proxy1 = *field1;
proxy1.swap(*field2);
EXPECT_THAT(proxy1, ElementsAre(StringEq("1")));
EXPECT_THAT(*field1, ElementsAre(StringEq("1")));
EXPECT_THAT(*field2, ElementsAre(StringEq("2")));
}
}
TYPED_TEST(RepeatedFieldProxyTest, RepeatedFieldOrProxySwap) {
auto field1 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field1->Add()->set_value(1);
auto field2 = this->template MakeRepeatedFieldContainer<
RepeatedFieldProxyTestSimpleMessage>();
field2->Add()->set_value(2);
{
// Swap two RepeatedFieldOrProxies:
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy1 = *field1;
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy2 = *field2;
proxy1.swap(proxy2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(proxy2, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
EXPECT_THAT(*field2, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
}
{
// Swap a RepeatedFieldOrProxy with a RepeatedField.
RepeatedFieldOrProxy<RepeatedFieldProxyTestSimpleMessage> proxy1 = *field1;
proxy1.swap(*field2);
EXPECT_THAT(proxy1, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
EXPECT_THAT(*field1, ElementsAre(EqualsProto(R"pb(value: 1)pb")));
EXPECT_THAT(*field2, ElementsAre(EqualsProto(R"pb(value: 2)pb")));
}
}
// Verify the return types of all accessors for legacy and proxy repeated
// fields:
// Repeated messages:
static_assert(
std::is_same_v<
decltype(std::declval<TestRepeatedMessageProxy>().nested_messages()),
const RepeatedPtrField<TestRepeatedMessageProxy::NestedMessage>&>);
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedMessageProxy>()
.mutable_nested_messages()),
RepeatedPtrField<TestRepeatedMessageProxy::NestedMessage>*>);
static_assert(
std::is_same_v<
decltype(std::declval<TestRepeatedMessageProxy>()
.nested_messages_proxy()),
RepeatedFieldProxy<const TestRepeatedMessageProxy::NestedMessage>>);
static_assert(std::is_same_v<
decltype(std::declval<TestRepeatedMessageProxy>()
.mutable_nested_messages_proxy()),
RepeatedFieldProxy<TestRepeatedMessageProxy::NestedMessage>>);
// Repeated ints:
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedIntProxy>().ints()),
const RepeatedField<int32_t>&>);
static_assert(std::is_same_v<
decltype(std::declval<TestRepeatedIntProxy>().mutable_ints()),
RepeatedField<int32_t>*>);
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedIntProxy>().ints_proxy()),
RepeatedFieldProxy<const int32_t>>);
static_assert(std::is_same_v<decltype(std::declval<TestRepeatedIntProxy>()
.mutable_ints_proxy()),
RepeatedFieldProxy<int32_t>>);
// Repeated enums:
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedEnumProxy>().enums()),
const RepeatedField<int>&>);
static_assert(std::is_same_v<
decltype(std::declval<TestRepeatedEnumProxy>().mutable_enums()),
RepeatedField<int>*>);
static_assert(std::is_same_v<
decltype(std::declval<TestRepeatedEnumProxy>().enums_proxy()),
RepeatedFieldProxy<const TestRepeatedEnumProxy::Enum>>);
static_assert(std::is_same_v<decltype(std::declval<TestRepeatedEnumProxy>()
.mutable_enums_proxy()),
RepeatedFieldProxy<TestRepeatedEnumProxy::Enum>>);
// Repeated std::string:
static_assert(std::is_same_v<
decltype(std::declval<TestRepeatedStdStringProxy>().strings()),
const RepeatedPtrField<std::string>&>);
static_assert(std::is_same_v<decltype(std::declval<TestRepeatedStdStringProxy>()
.mutable_strings()),
RepeatedPtrField<std::string>*>);
static_assert(std::is_same_v<decltype(std::declval<TestRepeatedStdStringProxy>()
.strings_proxy()),
RepeatedFieldProxy<const std::string>>);
static_assert(std::is_same_v<decltype(std::declval<TestRepeatedStdStringProxy>()
.mutable_strings_proxy()),
RepeatedFieldProxy<std::string>>);
// Repeated absl::string_view:
static_assert(
std::is_same_v<
decltype(std::declval<TestRepeatedStringViewProxy>().string_views()),
const RepeatedPtrField<std::string>&>);
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedStringViewProxy>()
.mutable_string_views()),
RepeatedPtrField<std::string>*>);
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedStringViewProxy>()
.string_views_proxy()),
RepeatedFieldProxy<const absl::string_view>>);
static_assert(
std::is_same_v<decltype(std::declval<TestRepeatedStringViewProxy>()
.mutable_string_views_proxy()),
RepeatedFieldProxy<absl::string_view>>);
TEST(RepeatedFieldProxyInterfaceTest, RepeatedMessageProxy) {
TestRepeatedMessageProxy msg;
{
auto proxy = msg.mutable_nested_messages_proxy();
proxy.emplace_back().set_value(1);
proxy.emplace_back().set_value(2);
proxy.emplace_back().set_value(3);
}
auto proxy = msg.nested_messages_proxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TEST(RepeatedFieldProxyInterfaceTest, RepeatedImportMessageProxy) {
TestRepeatedImportMessageProxy msg;
{
auto proxy = msg.mutable_import_messages_proxy();
proxy.emplace_back().set_value(1);
proxy.emplace_back().set_value(2);
proxy.emplace_back().set_value(3);
}
auto proxy = msg.import_messages_proxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
}
TEST(RepeatedFieldProxyInterfaceTest, RepeatedIntProxy) {
TestRepeatedIntProxy msg;
{
auto proxy = msg.mutable_ints_proxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
}
auto proxy = msg.ints_proxy();
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
}
TEST(RepeatedFieldProxyInterfaceTest, RepeatedEnumProxy) {
TestRepeatedEnumProxy msg;
{
auto proxy = msg.mutable_enums_proxy();
proxy.push_back(TestRepeatedEnumProxy::FOO);
proxy.push_back(TestRepeatedEnumProxy::BAR);
proxy.push_back(TestRepeatedEnumProxy::BAZ);
}
auto proxy = msg.enums_proxy();
EXPECT_THAT(
proxy, ElementsAre(TestRepeatedEnumProxy::FOO, TestRepeatedEnumProxy::BAR,
TestRepeatedEnumProxy::BAZ));
}
TEST(RepeatedFieldProxyInterfaceTest, RepeatedImportEnumProxy) {
TestRepeatedImportEnumProxy msg;
{
auto proxy = msg.mutable_enums_proxy();
proxy.push_back(
RepeatedFieldProxyTestImportEnum::REPEATED_FIELD_PROXY_TEST_IMPORT_FOO);
proxy.push_back(
RepeatedFieldProxyTestImportEnum::REPEATED_FIELD_PROXY_TEST_IMPORT_BAR);
proxy.push_back(
RepeatedFieldProxyTestImportEnum::REPEATED_FIELD_PROXY_TEST_IMPORT_BAZ);
}
auto proxy = msg.enums_proxy();
EXPECT_THAT(proxy, ElementsAre(RepeatedFieldProxyTestImportEnum::
REPEATED_FIELD_PROXY_TEST_IMPORT_FOO,
RepeatedFieldProxyTestImportEnum::
REPEATED_FIELD_PROXY_TEST_IMPORT_BAR,
RepeatedFieldProxyTestImportEnum::
REPEATED_FIELD_PROXY_TEST_IMPORT_BAZ));
}
TEST(RepeatedFieldProxyInterfaceTest, RepeatedLegacyStringProxy) {
TestRepeatedStdStringProxy msg;
{
auto proxy = msg.mutable_strings_proxy();
proxy.emplace_back("1");
proxy.emplace_back("2");
proxy.emplace_back("3");
}
auto proxy = msg.strings_proxy();
EXPECT_THAT(proxy, ElementsAre("1", "2", "3"));
}
TEST(RepeatedFieldProxyInterfaceTest, RepeatedStringViewProxy) {
TestRepeatedStringViewProxy msg;
{
auto proxy = msg.mutable_string_views_proxy();
proxy.emplace_back("1");
proxy.emplace_back("2");
proxy.emplace_back("3");
}
auto proxy = msg.string_views_proxy();
EXPECT_THAT(proxy, ElementsAre("1", "2", "3"));
}
} // namespace
} // namespace internal
} // namespace protobuf
} // namespace google