diff --git a/src/google/protobuf/no_field_presence_test.cc b/src/google/protobuf/no_field_presence_test.cc index 3181fbb098..6350e255c0 100644 --- a/src/google/protobuf/no_field_presence_test.cc +++ b/src/google/protobuf/no_field_presence_test.cc @@ -33,6 +33,7 @@ using ::proto2_nofieldpresence_unittest::ForeignMessage; using ::proto2_nofieldpresence_unittest::TestAllTypes; using ::testing::Eq; using ::testing::Gt; +using ::testing::IsEmpty; using ::testing::Not; using ::testing::StrEq; using ::testing::UnorderedPointwise; @@ -258,6 +259,322 @@ TEST(NoFieldPresenceTest, MessageFieldPresenceTest) { TestAllTypes::default_instance().has_optional_nested_message()); } +class NoFieldPresenceSwapFieldTest : public testing::Test { + protected: + NoFieldPresenceSwapFieldTest() + : m1_(), + m2_(), + r1_(m1_.GetReflection()), + r2_(m2_.GetReflection()), + d1_(m1_.GetDescriptor()), + d2_(m2_.GetDescriptor()) {} + + // Returns a field descriptor that corresponds to the field name. + // Note that different messages would still return the same field descriptor. + const FieldDescriptor* FindFieldByName(absl::string_view field_name) { + const FieldDescriptor* f1 = d1_->FindFieldByName(field_name); + const FieldDescriptor* f2 = d2_->FindFieldByName(field_name); + + // We actually ensure uniqueness of *field descriptors* even if we try to + // obtain them from different *message descriptors*. + ABSL_CHECK_EQ(f1, f2); + return f1; + } + + TestAllTypes m1_; + TestAllTypes m2_; + const Reflection* r1_; + const Reflection* r2_; + const Descriptor* d1_; + const Descriptor* d2_; +}; + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldScalarNonZeroTest) { + m1_.set_optional_int32(1); + m2_.set_optional_int32(2); + + const FieldDescriptor* f = FindFieldByName("optional_int32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_TRUE(r1_->HasField(m1_, f)); + EXPECT_TRUE(r2_->HasField(m2_, f)); + EXPECT_EQ(2, m1_.optional_int32()); + EXPECT_EQ(1, m2_.optional_int32()); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped again. + EXPECT_TRUE(r1_->HasField(m1_, f)); + EXPECT_TRUE(r2_->HasField(m2_, f)); + EXPECT_EQ(1, m1_.optional_int32()); + EXPECT_EQ(2, m2_.optional_int32()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldScalarOneZeroTest) { + m1_.set_optional_int32(1); + + const FieldDescriptor* f = FindFieldByName("optional_int32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_FALSE(r1_->HasField(m1_, f)); + EXPECT_TRUE(r2_->HasField(m2_, f)); + EXPECT_EQ(0, m1_.optional_int32()); + EXPECT_EQ(1, m2_.optional_int32()); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped again. + EXPECT_TRUE(r1_->HasField(m1_, f)); + EXPECT_FALSE(r2_->HasField(m2_, f)); + EXPECT_EQ(1, m1_.optional_int32()); + EXPECT_EQ(0, m2_.optional_int32()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldScalarBothZeroTest) { + m1_.set_optional_int32(0); // setting an int field to zero should be noop + + const FieldDescriptor* f = FindFieldByName("optional_int32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_FALSE(r1_->HasField(m1_, f)); + EXPECT_FALSE(r2_->HasField(m2_, f)); + EXPECT_EQ(0, m1_.optional_int32()); + EXPECT_EQ(0, m2_.optional_int32()); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped again. + EXPECT_FALSE(r1_->HasField(m1_, f)); + EXPECT_FALSE(r2_->HasField(m2_, f)); + EXPECT_EQ(0, m1_.optional_int32()); + EXPECT_EQ(0, m2_.optional_int32()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldRepeatedNonZeroTest) { + m1_.add_repeated_int32(1); + m2_.add_repeated_int32(2); + m2_.add_repeated_int32(22); + + const FieldDescriptor* f = FindFieldByName("repeated_int32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_EQ(r1_->FieldSize(m1_, f), 2); + EXPECT_EQ(r2_->FieldSize(m2_, f), 1); + EXPECT_THAT(m1_.repeated_int32(), UnorderedPointwise(Eq(), {2, 22})); + EXPECT_THAT(m2_.repeated_int32(), UnorderedPointwise(Eq(), {1})); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped again. + EXPECT_EQ(r1_->FieldSize(m1_, f), 1); + EXPECT_EQ(r2_->FieldSize(m2_, f), 2); + EXPECT_THAT(m1_.repeated_int32(), UnorderedPointwise(Eq(), {1})); + EXPECT_THAT(m2_.repeated_int32(), UnorderedPointwise(Eq(), {2, 22})); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldRepeatedOneZeroTest) { + m1_.add_repeated_int32(1); + + const FieldDescriptor* f = FindFieldByName("repeated_int32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_EQ(r1_->FieldSize(m1_, f), 0); + EXPECT_EQ(r2_->FieldSize(m2_, f), 1); + EXPECT_THAT(m1_.repeated_int32(), IsEmpty()); + EXPECT_THAT(m2_.repeated_int32(), UnorderedPointwise(Eq(), {1})); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped again. + EXPECT_EQ(r1_->FieldSize(m1_, f), 1); + EXPECT_EQ(r2_->FieldSize(m2_, f), 0); + EXPECT_THAT(m1_.repeated_int32(), UnorderedPointwise(Eq(), {1})); + EXPECT_THAT(m2_.repeated_int32(), IsEmpty()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, + ReflectionSwapFieldRepeatedExplicitZeroTest) { + // For repeated fields, explicitly adding zero would cause it to be added into + // the repeated field. + m1_.add_repeated_int32(0); + + const FieldDescriptor* f = FindFieldByName("repeated_int32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_EQ(r1_->FieldSize(m1_, f), 0); + EXPECT_EQ(r2_->FieldSize(m2_, f), 1); + EXPECT_THAT(m1_.repeated_int32(), IsEmpty()); + EXPECT_THAT(m2_.repeated_int32(), UnorderedPointwise(Eq(), {0})); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped again. + EXPECT_EQ(r1_->FieldSize(m1_, f), 1); + EXPECT_EQ(r2_->FieldSize(m2_, f), 0); + EXPECT_THAT(m1_.repeated_int32(), UnorderedPointwise(Eq(), {0})); + EXPECT_THAT(m2_.repeated_int32(), IsEmpty()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, + ReflectionSwapFieldOneofFieldDescriptorTest) { + m1_.set_oneof_uint32(1); + m2_.set_oneof_string("test"); + + // NOTE: Calling swap on any field descriptor within the oneof works -- + // even a completely unrelated field. + const FieldDescriptor* never_set_field = d1_->FindFieldByName("oneof_enum"); + + r1_->SwapFields(&m1_, &m2_, /*fields=*/{never_set_field}); + + // Fields should be swapped. + EXPECT_FALSE(r1_->HasField(m1_, never_set_field)); + EXPECT_FALSE(r1_->HasField(m2_, never_set_field)); + EXPECT_TRUE(m1_.has_oneof_string()); + EXPECT_TRUE(m2_.has_oneof_uint32()); + EXPECT_EQ(m1_.oneof_string(), "test"); + EXPECT_EQ(m2_.oneof_uint32(), 1); + + // Calling oneof accessors on a swapped-out field will give the default value. + EXPECT_FALSE(m1_.has_oneof_uint32()); + EXPECT_FALSE(m2_.has_oneof_string()); + EXPECT_EQ(m1_.oneof_uint32(), 0); + EXPECT_THAT(m2_.oneof_string(), IsEmpty()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, + ReflectionSwapFieldOneofFieldMultipleIdenticalDescriptorTest) { + m1_.set_oneof_uint32(1); + m2_.set_oneof_string("test"); + + // NOTE: Calling swap on any field descriptor within the oneof works -- + // even a completely unrelated field. + const FieldDescriptor* never_set_field = d1_->FindFieldByName("oneof_enum"); + const FieldDescriptor* f1 = d1_->FindFieldByName("oneof_uint32"); + const FieldDescriptor* f2 = d2_->FindFieldByName("oneof_string"); + + // Multiple instances of the identical descriptor is ignored. + r1_->SwapFields(&m1_, &m2_, /*fields=*/{never_set_field, never_set_field}); + + // Fields should be swapped (just once). + EXPECT_EQ(m1_.oneof_string(), "test"); + EXPECT_EQ(m2_.oneof_uint32(), 1); + + // Multiple instances of the identical descriptor is ignored. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f1, f2, never_set_field}); + + // Fields should be swapped (just once). + EXPECT_TRUE(m1_.has_oneof_uint32()); + EXPECT_TRUE(m2_.has_oneof_string()); + EXPECT_TRUE(r1_->HasField(m1_, f1)); + EXPECT_TRUE(r2_->HasField(m2_, f2)); + EXPECT_EQ(m1_.oneof_uint32(), 1); + EXPECT_EQ(m2_.oneof_string(), "test"); + + // Calling oneof accessors on a swapped-out field will give the default value. + EXPECT_FALSE(m1_.has_oneof_string()); + EXPECT_FALSE(m2_.has_oneof_uint32()); + EXPECT_FALSE(r1_->HasField(m1_, d1_->FindFieldByName("oneof_string"))); + EXPECT_FALSE(r2_->HasField(m2_, d2_->FindFieldByName("oneof_uint32"))); + EXPECT_THAT(m1_.oneof_string(), IsEmpty()); + EXPECT_EQ(m2_.oneof_uint32(), 0); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldOneofNonZeroTest) { + m1_.set_oneof_uint32(1); + m2_.set_oneof_string("test"); + + const FieldDescriptor* f = FindFieldByName("oneof_uint32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_TRUE(m1_.has_oneof_string()); + EXPECT_TRUE(m2_.has_oneof_uint32()); + EXPECT_TRUE(r1_->HasField(m1_, d1_->FindFieldByName("oneof_string"))); + EXPECT_TRUE(r2_->HasField(m2_, f)); + EXPECT_EQ(m1_.oneof_string(), "test"); + EXPECT_EQ(m2_.oneof_uint32(), 1); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_TRUE(m1_.has_oneof_uint32()); + EXPECT_TRUE(m2_.has_oneof_string()); + EXPECT_TRUE(r1_->HasField(m1_, f)); + EXPECT_TRUE(r2_->HasField(m2_, d2_->FindFieldByName("oneof_string"))); + EXPECT_EQ(m1_.oneof_uint32(), 1); + EXPECT_EQ(m2_.oneof_string(), "test"); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldOneofDefaultTest) { + m1_.set_oneof_uint32(1); + + const FieldDescriptor* f = FindFieldByName("oneof_uint32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_FALSE(r1_->HasField(m1_, d1_->FindFieldByName("oneof_string"))); + EXPECT_TRUE(r2_->HasField(m2_, f)); + EXPECT_FALSE(m1_.has_oneof_string()); + EXPECT_EQ(m2_.oneof_uint32(), 1); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_TRUE(r1_->HasField(m1_, f)); + EXPECT_FALSE(r2_->HasField(m2_, d2_->FindFieldByName("oneof_string"))); + EXPECT_EQ(m1_.oneof_uint32(), 1); + EXPECT_FALSE(m2_.has_oneof_string()); +} + +TEST_F(NoFieldPresenceSwapFieldTest, ReflectionSwapFieldOneofExplicitZeroTest) { + // Oneof fields essentially have explicit presence -- if set to zero, they + // will still be considered present. + m1_.set_oneof_uint32(0); + + const FieldDescriptor* f = FindFieldByName("oneof_uint32"); + r1_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_FALSE(r1_->HasField(m1_, f)); + EXPECT_TRUE(r2_->HasField(m2_, f)); + EXPECT_FALSE(m1_.has_oneof_uint32()); + EXPECT_TRUE(m2_.has_oneof_uint32()); + EXPECT_EQ(m2_.oneof_uint32(), 0); + + // It doesn't matter which reflection or descriptor gets used; swapping should + // still work if m2_'s descriptor is provided. + r2_->SwapFields(&m1_, &m2_, /*fields=*/{f}); + + // Fields should be swapped. + EXPECT_TRUE(r1_->HasField(m1_, f)); + EXPECT_FALSE(r2_->HasField(m2_, f)); + EXPECT_TRUE(m1_.has_oneof_uint32()); + EXPECT_EQ(m1_.oneof_uint32(), 0); + EXPECT_FALSE(m2_.has_oneof_uint32()); +} + class NoFieldPresenceListFieldsTest : public testing::Test { protected: NoFieldPresenceListFieldsTest()