Skip to content

Commit

Permalink
Add tests for string APIs for implicit presence fields.
Browse files Browse the repository at this point in the history
By "string APIs", I mean the following APIs:
- mutable_foo
- set_allocated_foo
- release_foo
See also: https://protobuf.dev/reference/cpp/cpp-generated/#implicit-string

Add the following test cases (for singular implicit-presence fields):
- A single call to mutable_foo() should not result in additional serialization
  on the wire.
- Assigning an empty string to mutable_foo() should not result in additional
  serialization on the wire.
- Calling set_allocated_foo() with an empty string should not result in
  additional serialization on the wire.
- If a field is nonempty, release_foo() effectively clears the field (while
  returning a pointer to the original data).

Adding coverage for these behaviours can increase confidence when we introduce
internal hasbits to help with presence tracking for implicit presence fields.
mutable_foo will in general set the hasbit, so the generated code will need to
check that field is nonempty before serializing.

PiperOrigin-RevId: 658562530
  • Loading branch information
tonyliaoss authored and copybara-github committed Aug 1, 2024
1 parent fe7b64e commit 58a97a4
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/google/protobuf/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,7 @@ cc_test(
":cc_test_protos",
":protobuf",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings:string_view",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
Expand Down
53 changes: 53 additions & 0 deletions src/google/protobuf/no_field_presence_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include <cstddef>
#include <memory>
#include <string>

#include "google/protobuf/descriptor.pb.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/log/absl_check.h"
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/unittest.pb.h"
Expand All @@ -20,6 +23,7 @@ namespace google {
namespace protobuf {
namespace {

using ::testing::Gt;
using ::testing::StrEq;

// Helper: checks that all fields have default (zero/empty) values.
Expand Down Expand Up @@ -441,6 +445,55 @@ TEST(NoFieldPresenceTest, DontSerializeDefaultValuesTest) {
EXPECT_EQ(0, output.size());
}

TEST(NoFieldPresenceTest, NullMutableSerializesEmpty) {
// Check that, if mutable_foo() was called, but fields were not modified,
// nothing is serialized on the wire.
proto2_nofieldpresence_unittest::TestAllTypes message;
std::string output;

// All default values -> no output.
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_TRUE(output.empty());

// No-op mutable calls -> no output.
message.mutable_optional_string();
message.mutable_optional_bytes();
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_TRUE(output.empty());

// Assign to nonempty string -> some output.
*message.mutable_optional_bytes() = "bar";
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_THAT(output.size(), Gt(3)); // 3-byte-long string + tag/value + len
}

TEST(NoFieldPresenceTest, SetAllocatedAndReleaseTest) {
// Check that setting an empty string via set_allocated_foo behaves properly;
// Check that serializing after release_foo does not generate output for foo.
proto2_nofieldpresence_unittest::TestAllTypes message;
std::string output;

// All default values -> no output.
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_TRUE(output.empty());

auto allocated_bytes = std::make_unique<std::string>("test");
message.set_allocated_optional_bytes(allocated_bytes.release());
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_THAT(output.size(), Gt(4)); // 4-byte-long string + tag/value + len

size_t former_output_size = output.size();

auto allocated_string = std::make_unique<std::string>("");
message.set_allocated_optional_string(allocated_string.release());
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_EQ(former_output_size, output.size()); // empty string not serialized.

auto bytes_ptr = absl::WrapUnique(message.release_optional_bytes());
ASSERT_TRUE(message.SerializeToString(&output));
EXPECT_TRUE(output.empty()); // released fields are not serialized.
}

TEST(NoFieldPresenceTest, MergeFromIfNonzeroTest) {
// check that MergeFrom copies if nonzero/nondefault only.
proto2_nofieldpresence_unittest::TestAllTypes source;
Expand Down

0 comments on commit 58a97a4

Please sign in to comment.