Add unit test coverage for ArrayInputStream.

PiperOrigin-RevId: 979447752
diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc
index db7b0df..c20ef45 100644
--- a/src/google/protobuf/io/zero_copy_stream_unittest.cc
+++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc
@@ -293,6 +293,168 @@
   }
 }
 
+// -------------------------------------------------------------------
+// ArrayInputStream
+
+TEST(ArrayInputStreamTest, EmptyBuffer) {
+  {
+    ArrayInputStream input(nullptr, 0);
+    EXPECT_EQ(input.ByteCount(), 0);
+
+    const void* data = nullptr;
+    int size = -1;
+    EXPECT_FALSE(input.Next(&data, &size));
+    EXPECT_EQ(input.ByteCount(), 0);
+    EXPECT_TRUE(input.Skip(0));
+    EXPECT_EQ(input.ByteCount(), 0);
+    EXPECT_FALSE(input.Skip(1));
+    EXPECT_EQ(input.ByteCount(), 0);
+  }
+  {
+    char c = 'x';
+    ArrayInputStream input(&c, 0);
+    EXPECT_EQ(input.ByteCount(), 0);
+
+    const void* data = nullptr;
+    int size = -1;
+    EXPECT_FALSE(input.Next(&data, &size));
+    EXPECT_EQ(input.ByteCount(), 0);
+  }
+}
+
+TEST(ArrayInputStreamTest, NextReturnsWholeBuffer) {
+  constexpr absl::string_view data = "Hello, world!";
+  ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+  EXPECT_EQ(input.ByteCount(), 0);
+
+  const void* out_data = nullptr;
+  int out_size = 0;
+  ASSERT_TRUE(input.Next(&out_data, &out_size));
+  EXPECT_EQ(out_data, data.data());
+  EXPECT_EQ(out_size, static_cast<int>(data.size()));
+  EXPECT_EQ(input.ByteCount(), static_cast<int>(data.size()));
+
+  EXPECT_FALSE(input.Next(&out_data, &out_size));
+  EXPECT_EQ(input.ByteCount(), static_cast<int>(data.size()));
+}
+
+TEST(ArrayInputStreamTest, BackUp) {
+  constexpr absl::string_view data = "0123456789";
+  {
+    // Partial backup.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    const void* out_data = nullptr;
+    int out_size = 0;
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 10);
+    EXPECT_EQ(input.ByteCount(), 10);
+
+    input.BackUp(4);
+    EXPECT_EQ(input.ByteCount(), 6);
+
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 4);
+    EXPECT_EQ(out_data, data.data() + 6);
+    EXPECT_EQ(input.ByteCount(), 10);
+
+    EXPECT_FALSE(input.Next(&out_data, &out_size));
+  }
+  {
+    // Full backup.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    const void* out_data = nullptr;
+    int out_size = 0;
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 10);
+
+    input.BackUp(10);
+    EXPECT_EQ(input.ByteCount(), 0);
+
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 10);
+    EXPECT_EQ(out_data, data.data());
+    EXPECT_EQ(input.ByteCount(), 10);
+  }
+  {
+    // Zero-byte backup.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    const void* out_data = nullptr;
+    int out_size = 0;
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    input.BackUp(0);
+    EXPECT_EQ(input.ByteCount(), 10);
+    EXPECT_FALSE(input.Next(&out_data, &out_size));
+  }
+}
+
+TEST(ArrayInputStreamTest, Skip) {
+  constexpr absl::string_view data = "0123456789";
+  {
+    // Skip 0 bytes.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    EXPECT_TRUE(input.Skip(0));
+    EXPECT_EQ(input.ByteCount(), 0);
+
+    const void* out_data = nullptr;
+    int out_size = 0;
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 10);
+    EXPECT_EQ(out_data, data.data());
+  }
+  {
+    // Skip partial.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    EXPECT_TRUE(input.Skip(4));
+    EXPECT_EQ(input.ByteCount(), 4);
+
+    const void* out_data = nullptr;
+    int out_size = 0;
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 6);
+    EXPECT_EQ(out_data, data.data() + 4);
+    EXPECT_EQ(input.ByteCount(), 10);
+    EXPECT_FALSE(input.Next(&out_data, &out_size));
+  }
+  {
+    // Skip to exact end.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    EXPECT_TRUE(input.Skip(10));
+    EXPECT_EQ(input.ByteCount(), 10);
+
+    const void* out_data = nullptr;
+    int out_size = 0;
+    EXPECT_FALSE(input.Next(&out_data, &out_size));
+  }
+  {
+    // Skip past end.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    EXPECT_FALSE(input.Skip(15));
+    EXPECT_EQ(input.ByteCount(), 10);
+
+    const void* out_data = nullptr;
+    int out_size = 0;
+    EXPECT_FALSE(input.Next(&out_data, &out_size));
+  }
+  {
+    // Skip after Next and BackUp.
+    ArrayInputStream input(data.data(), static_cast<int>(data.size()));
+    const void* out_data = nullptr;
+    int out_size = 0;
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    input.BackUp(6);
+    EXPECT_EQ(input.ByteCount(), 4);
+
+    EXPECT_TRUE(input.Skip(2));
+    EXPECT_EQ(input.ByteCount(), 6);
+
+    ASSERT_TRUE(input.Next(&out_data, &out_size));
+    EXPECT_EQ(out_size, 4);
+    EXPECT_EQ(out_data, data.data() + 6);
+    EXPECT_EQ(input.ByteCount(), 10);
+  }
+}
+
+
 TEST_F(IoTest, TwoSessionWrite) {
   // Test that two concatenated write sessions read correctly