| #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 |