| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2023 Google Inc. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "google/protobuf/has_bits.h" | |
| #include <cstdint> | |
| #include <string> | |
| #include <utility> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "absl/log/absl_check.h" | |
| #include "absl/status/status.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "google/protobuf/descriptor.h" | |
| #include "google/protobuf/generated_message_tctable_impl.h" | |
| #include "google/protobuf/map_unittest.pb.h" | |
| #include "google/protobuf/message.h" | |
| #include "google/protobuf/port.h" | |
| #include "google/protobuf/repeated_field.h" | |
| #include "google/protobuf/repeated_ptr_field.h" | |
| #include "google/protobuf/unittest.pb.h" | |
| namespace google { | |
| namespace protobuf { | |
| namespace internal { | |
| namespace { | |
| using ::proto2_unittest::TestAllTypes; | |
| using ::proto2_unittest::TestMap; | |
| using ::testing::Eq; | |
| bool IsIndexInHasBitSet(const uint32_t* has_bit_set, uint32_t has_bit_index) { | |
| ABSL_DCHECK_NE(has_bit_index, static_cast<uint32_t>(kNoHasbit)); | |
| return ((has_bit_set[has_bit_index / 32] >> (has_bit_index % 32)) & | |
| static_cast<uint32_t>(1)) != 0; | |
| } | |
| } // namespace | |
| class HasBitsTestPeer { | |
| public: | |
| static bool HasBitSet(const Message& msg, const FieldDescriptor* field) { | |
| const auto* ref = msg.GetReflection(); | |
| uint32_t has_bit_idx = ref->Schema().HasBitIndex(field); | |
| return IsIndexInHasBitSet(ref->GetHasBits(msg), has_bit_idx); | |
| } | |
| static bool HasBitSet(const Message& msg, absl::string_view field_name) { | |
| return HasBitSet(msg, msg.GetDescriptor()->FindFieldByName(field_name)); | |
| } | |
| static void SetHasBit(Message* msg, const FieldDescriptor* field) { | |
| const auto* ref = msg->GetReflection(); | |
| ABSL_DCHECK_NE(ref->Schema().HasBitIndex(field), | |
| static_cast<uint32_t>(kNoHasbit)); | |
| ref->SetHasBit(msg, field); | |
| } | |
| static void ClearHasBit(Message* msg, const FieldDescriptor* field) { | |
| const auto* ref = msg->GetReflection(); | |
| ABSL_DCHECK_NE(ref->Schema().HasBitIndex(field), | |
| static_cast<uint32_t>(kNoHasbit)); | |
| ref->ClearHasBit(msg, field); | |
| } | |
| }; | |
| namespace { | |
| template <typename HasBitsT> | |
| class DefaultInitTest : public ::testing::Test {}; | |
| using DefaultInitTestTypes = | |
| testing::Types<HasBits<1>, HasBits<2>, HasBits<3>, HasBits<4>>; | |
| TYPED_TEST_SUITE(DefaultInitTest, DefaultInitTestTypes); | |
| TYPED_TEST(DefaultInitTest, DefaultInit) { | |
| TypeParam bits; | |
| EXPECT_TRUE(bits.empty()); | |
| for (int i = 0; i < TypeParam::kNumHasWords; ++i) { | |
| EXPECT_THAT(bits[i], Eq(0)); | |
| } | |
| } | |
| TEST(HasBitsTest, ValueInit) { | |
| { | |
| HasBits<4> bits; | |
| EXPECT_TRUE(bits.empty()); | |
| } | |
| { | |
| HasBits<4> bits({1}); | |
| EXPECT_FALSE(bits.empty()); | |
| EXPECT_THAT(bits[0], Eq(1)); | |
| } | |
| { | |
| HasBits<4> bits({1, 2, 3, 4}); | |
| EXPECT_FALSE(bits.empty()); | |
| EXPECT_THAT(bits[0], Eq(1)); | |
| EXPECT_THAT(bits[1], Eq(2)); | |
| EXPECT_THAT(bits[2], Eq(3)); | |
| EXPECT_THAT(bits[3], Eq(4)); | |
| } | |
| } | |
| TEST(HasBitsTest, ConstexprValueInit) { | |
| { | |
| constexpr HasBits<4> bits; | |
| EXPECT_TRUE(bits.empty()); | |
| } | |
| { | |
| constexpr HasBits<4> bits({1}); | |
| EXPECT_FALSE(bits.empty()); | |
| EXPECT_THAT(bits[0], Eq(1)); | |
| } | |
| { | |
| constexpr HasBits<4> bits({1, 2, 3, 4}); | |
| EXPECT_FALSE(bits.empty()); | |
| EXPECT_THAT(bits[0], Eq(1)); | |
| EXPECT_THAT(bits[1], Eq(2)); | |
| EXPECT_THAT(bits[2], Eq(3)); | |
| EXPECT_THAT(bits[3], Eq(4)); | |
| } | |
| } | |
| TEST(HasBitsTest, operator_equal) { | |
| EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({0, 2, 3, 4})); | |
| EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 0, 3, 4})); | |
| EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 2, 0, 4})); | |
| EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 2, 3, 0})); | |
| EXPECT_TRUE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 2, 3, 4})); | |
| } | |
| TEST(HasBitsTest, Or) { | |
| HasBits<4> bits1({1, 2, 4, 8}); | |
| HasBits<4> bits2({16, 32, 64, 128}); | |
| bits1.Or(bits2); | |
| EXPECT_TRUE(bits1 == HasBits<4>({17, 34, 68, 136})); | |
| } | |
| TEST(HasBitsTest, Copy) { | |
| HasBits<4> bits1({1, 2, 4, 8}); | |
| HasBits<4> bits2(bits1); | |
| EXPECT_TRUE(bits1 == bits2); | |
| } | |
| TEST(HasBitsTest, HasBitsUnsetForDefaultRepeatedField) { | |
| TestAllTypes msg; | |
| EXPECT_FALSE(HasBitsTestPeer::HasBitSet(msg, "repeated_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsSetOnMutable) { | |
| TestAllTypes msg; | |
| msg.mutable_repeated_int32(); | |
| EXPECT_TRUE(HasBitsTestPeer::HasBitSet(msg, "repeated_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsClearedOnFieldClear) { | |
| TestAllTypes msg; | |
| msg.mutable_repeated_int32(); | |
| msg.clear_repeated_int32(); | |
| EXPECT_FALSE(HasBitsTestPeer::HasBitSet(msg, "repeated_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsSetOnMutableWithReflection) { | |
| TestAllTypes msg; | |
| msg.GetReflection()->GetMutableRepeatedFieldRef<int32_t>( | |
| &msg, msg.GetDescriptor()->FindFieldByName("repeated_int32")); | |
| EXPECT_TRUE(HasBitsTestPeer::HasBitSet(msg, "repeated_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsClearedOnFieldClearWithReflection) { | |
| TestAllTypes msg; | |
| msg.mutable_repeated_int32(); | |
| msg.GetReflection()->ClearField( | |
| &msg, msg.GetDescriptor()->FindFieldByName("repeated_int32")); | |
| EXPECT_FALSE(HasBitsTestPeer::HasBitSet(msg, "repeated_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsSetOnMutableMap) { | |
| TestMap msg; | |
| (void)msg.mutable_map_int32_int32(); | |
| EXPECT_TRUE(HasBitsTestPeer::HasBitSet(msg, "map_int32_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsClearedOnMapFieldClear) { | |
| TestMap msg; | |
| (void)msg.mutable_map_int32_int32(); | |
| msg.clear_map_int32_int32(); | |
| EXPECT_FALSE(HasBitsTestPeer::HasBitSet(msg, "map_int32_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsSetOnMutableMapWithReflection) { | |
| TestMap msg; | |
| msg.GetReflection()->GetMutableRepeatedFieldRef<Message>( | |
| &msg, msg.GetDescriptor()->FindFieldByName("map_int32_int32")); | |
| EXPECT_TRUE(HasBitsTestPeer::HasBitSet(msg, "map_int32_int32")); | |
| } | |
| TEST(HasBitsTest, HasBitsClearedOnMapFieldClearWithReflection) { | |
| TestMap msg; | |
| (void)msg.mutable_map_int32_int32(); | |
| msg.GetReflection()->ClearField( | |
| &msg, msg.GetDescriptor()->FindFieldByName("map_int32_int32")); | |
| EXPECT_FALSE(HasBitsTestPeer::HasBitSet(msg, "map_int32_int32")); | |
| } | |
| } // namespace | |
| } // namespace internal | |
| } // namespace protobuf | |
| } // namespace google |