summaryrefslogtreecommitdiff
path: root/absl/strings/cord_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/cord_test.cc')
-rw-r--r--absl/strings/cord_test.cc148
1 files changed, 145 insertions, 3 deletions
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 1fc4be6e..9a72f7be 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -1988,6 +1988,12 @@ TEST_P(CordTest, HugeCord) {
// Tests that Append() works ok when handed a self reference
TEST_P(CordTest, AppendSelf) {
+ // Test the empty case.
+ absl::Cord empty;
+ MaybeHarden(empty);
+ empty.Append(empty);
+ ASSERT_EQ(empty, "");
+
// We run the test until data is ~16K
// This guarantees it covers small, medium and large data.
std::string control_data = "Abc";
@@ -2712,7 +2718,7 @@ class CordMutator {
// clang-format off
// This array is constant-initialized in conformant compilers.
-CordMutator cord_mutators[] ={
+CordMutator cord_mutators[] = {
{"clear", [](absl::Cord& c) { c.Clear(); }},
{"overwrite", [](absl::Cord& c) { c = "overwritten"; }},
{
@@ -2742,6 +2748,25 @@ CordMutator cord_mutators[] ={
[](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }
},
{
+ "append empty string",
+ [](absl::Cord& c) { c.Append(""); },
+ [](absl::Cord& c) { }
+ },
+ {
+ "append empty cord",
+ [](absl::Cord& c) { c.Append(absl::Cord()); },
+ [](absl::Cord& c) { }
+ },
+ {
+ "append empty checksummed cord",
+ [](absl::Cord& c) {
+ absl::Cord to_append;
+ to_append.SetExpectedChecksum(999);
+ c.Append(to_append);
+ },
+ [](absl::Cord& c) { }
+ },
+ {
"prepend string",
[](absl::Cord& c) { c.Prepend("9876543210"); },
[](absl::Cord& c) { c.RemovePrefix(10); }
@@ -2763,12 +2788,33 @@ CordMutator cord_mutators[] ={
[](absl::Cord& c) { c.RemovePrefix(10); }
},
{
+ "prepend empty string",
+ [](absl::Cord& c) { c.Prepend(""); },
+ [](absl::Cord& c) { }
+ },
+ {
+ "prepend empty cord",
+ [](absl::Cord& c) { c.Prepend(absl::Cord()); },
+ [](absl::Cord& c) { }
+ },
+ {
+ "prepend empty checksummed cord",
+ [](absl::Cord& c) {
+ absl::Cord to_prepend;
+ to_prepend.SetExpectedChecksum(999);
+ c.Prepend(to_prepend);
+ },
+ [](absl::Cord& c) { }
+ },
+ {
"prepend self",
[](absl::Cord& c) { c.Prepend(c); },
[](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }
},
- {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(2); }},
- {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(2); }},
+ {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }},
+ {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }},
+ {"remove 0-prefix", [](absl::Cord& c) { c.RemovePrefix(0); }},
+ {"remove 0-suffix", [](absl::Cord& c) { c.RemoveSuffix(0); }},
{"subcord", [](absl::Cord& c) { c = c.Subcord(1, c.size() - 2); }},
{
"swap inline",
@@ -2834,6 +2880,13 @@ TEST_P(CordTest, ExpectedChecksum) {
c2.SetExpectedChecksum(24680);
mutator.Mutate(c2);
+
+ if (c1 == c2) {
+ // Not a mutation (for example, appending the empty string).
+ // Whether the checksum is removed is not defined.
+ continue;
+ }
+
EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);
if (mutator.CanUndo()) {
@@ -2903,3 +2956,92 @@ TEST_P(CordTest, ExpectedChecksum) {
}
}
}
+
+// Test the special cases encountered with an empty checksummed cord.
+TEST_P(CordTest, ChecksummedEmptyCord) {
+ absl::Cord c1;
+ EXPECT_FALSE(c1.ExpectedChecksum().has_value());
+
+ // Setting an expected checksum works.
+ c1.SetExpectedChecksum(12345);
+ EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+ EXPECT_EQ(c1, "");
+ EXPECT_TRUE(c1.empty());
+
+ // CRC persists through copies, assignments, and moves:
+ absl::Cord c1_copy_construct = c1;
+ EXPECT_EQ(c1_copy_construct.ExpectedChecksum().value_or(0), 12345);
+
+ absl::Cord c1_copy_assign;
+ c1_copy_assign = c1;
+ EXPECT_EQ(c1_copy_assign.ExpectedChecksum().value_or(0), 12345);
+
+ absl::Cord c1_move(std::move(c1_copy_assign));
+ EXPECT_EQ(c1_move.ExpectedChecksum().value_or(0), 12345);
+
+ EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+
+ // A CRC Cord compares equal to its non-CRC value.
+ EXPECT_EQ(c1, absl::Cord());
+
+ for (const CordMutator& mutator : cord_mutators) {
+ SCOPED_TRACE(mutator.Name());
+
+ // Exercise mutating an empty checksummed cord to catch crashes and exercise
+ // memory sanitizers.
+ absl::Cord c2;
+ c2.SetExpectedChecksum(24680);
+ mutator.Mutate(c2);
+
+ if (c2.empty()) {
+ // Not a mutation
+ continue;
+ }
+ EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);
+
+ if (mutator.CanUndo()) {
+ mutator.Undo(c2);
+ }
+ }
+
+ absl::Cord c3;
+ c3.SetExpectedChecksum(999);
+ const absl::Cord& cc3 = c3;
+
+ // Test that all cord reading operations function in the face of an
+ // expected checksum.
+ EXPECT_TRUE(cc3.StartsWith(""));
+ EXPECT_TRUE(cc3.EndsWith(""));
+ EXPECT_TRUE(cc3.empty());
+ EXPECT_EQ(cc3, "");
+ EXPECT_EQ(cc3, absl::Cord());
+ EXPECT_EQ(cc3.size(), 0);
+ EXPECT_EQ(cc3.Compare(absl::Cord()), 0);
+ EXPECT_EQ(cc3.Compare(c1), 0);
+ EXPECT_EQ(cc3.Compare(cc3), 0);
+ EXPECT_EQ(cc3.Compare(""), 0);
+ EXPECT_EQ(cc3.Compare("wxyz"), -1);
+ EXPECT_EQ(cc3.Compare(absl::Cord("wxyz")), -1);
+ EXPECT_EQ(absl::Cord("wxyz").Compare(cc3), 1);
+ EXPECT_EQ(std::string(cc3), "");
+
+ std::string dest;
+ absl::CopyCordToString(cc3, &dest);
+ EXPECT_EQ(dest, "");
+
+ for (absl::string_view chunk : cc3.Chunks()) { // NOLINT(unreachable loop)
+ static_cast<void>(chunk);
+ GTEST_FAIL() << "no chunks expected";
+ }
+ EXPECT_TRUE(cc3.chunk_begin() == cc3.chunk_end());
+
+ for (char ch : cc3.Chars()) { // NOLINT(unreachable loop)
+ static_cast<void>(ch);
+ GTEST_FAIL() << "no chars expected";
+ }
+ EXPECT_TRUE(cc3.char_begin() == cc3.char_end());
+
+ EXPECT_EQ(cc3.TryFlat(), "");
+ EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::Cord()));
+ EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::string_view()));
+}