| // Copyright 2022 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quiche/balsa/simple_buffer.h" |
| |
| #include <string> |
| |
| #include "absl/strings/string_view.h" |
| #include "quiche/common/platform/api/quiche_expect_bug.h" |
| #include "quiche/common/platform/api/quiche_test.h" |
| |
| namespace quiche { |
| |
| namespace test { |
| |
| namespace { |
| |
| constexpr int kMinimumSimpleBufferSize = 10; |
| |
| // Buffer full of 40 char strings. |
| const char ibuf[] = { |
| "123456789!@#$%^&*()abcdefghijklmnopqrstu" |
| "123456789!@#$%^&*()abcdefghijklmnopqrstu" |
| "123456789!@#$%^&*()abcdefghijklmnopqrstu" |
| "123456789!@#$%^&*()abcdefghijklmnopqrstu" |
| "123456789!@#$%^&*()abcdefghijklmnopqrstu"}; |
| |
| } // namespace |
| |
| class SimpleBufferTest : public QuicheTest { |
| public: |
| static char* storage(SimpleBuffer& buffer) { return buffer.storage_; } |
| static int write_idx(SimpleBuffer& buffer) { return buffer.write_idx_; } |
| static int read_idx(SimpleBuffer& buffer) { return buffer.read_idx_; } |
| static int storage_size(SimpleBuffer& buffer) { return buffer.storage_size_; } |
| }; |
| |
| namespace { |
| |
| TEST_F(SimpleBufferTest, CreationWithSize) { |
| SimpleBuffer buffer1(5); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer1)); |
| |
| SimpleBuffer buffer2(25); |
| EXPECT_EQ(25, storage_size(buffer2)); |
| } |
| |
| // Make sure that a zero-sized initial buffer does not throw things off. |
| TEST_F(SimpleBufferTest, CreationWithZeroSize) { |
| SimpleBuffer buffer(0); |
| EXPECT_EQ(0, storage_size(buffer)); |
| EXPECT_EQ(4, buffer.Write(ibuf, 4)); |
| EXPECT_EQ(4, write_idx(buffer)); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| EXPECT_EQ(4, buffer.ReadableBytes()); |
| } |
| |
| TEST_F(SimpleBufferTest, ReadZeroBytes) { |
| SimpleBuffer buffer; |
| |
| EXPECT_EQ(0, buffer.Read(nullptr, 0)); |
| } |
| |
| TEST_F(SimpleBufferTest, WriteZeroFromNullptr) { |
| SimpleBuffer buffer; |
| |
| EXPECT_EQ(0, buffer.Write(nullptr, 0)); |
| } |
| |
| TEST(SimpleBufferExpectBug, ReserveNegativeSize) { |
| SimpleBuffer buffer; |
| |
| EXPECT_QUICHE_BUG(buffer.Reserve(-1), "size must not be negative"); |
| } |
| |
| TEST(SimpleBufferExpectBug, ReadNegativeSize) { |
| SimpleBuffer buffer; |
| |
| EXPECT_QUICHE_BUG(buffer.Read(nullptr, -1), "size must not be negative"); |
| } |
| |
| TEST(SimpleBufferExpectBug, WriteNegativeSize) { |
| SimpleBuffer buffer; |
| |
| EXPECT_QUICHE_BUG(buffer.Write(nullptr, -1), "size must not be negative"); |
| } |
| |
| TEST_F(SimpleBufferTest, Basics) { |
| SimpleBuffer buffer; |
| |
| EXPECT_TRUE(buffer.Empty()); |
| EXPECT_EQ("", buffer.GetReadableRegion()); |
| EXPECT_EQ(0, storage_size(buffer)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| |
| char* readable_ptr = nullptr; |
| int readable_size = 0; |
| buffer.GetReadablePtr(&readable_ptr, &readable_size); |
| char* writeable_ptr = nullptr; |
| int writable_size = 0; |
| buffer.GetWritablePtr(&writeable_ptr, &writable_size); |
| |
| EXPECT_EQ(storage(buffer), readable_ptr); |
| EXPECT_EQ(0, readable_size); |
| EXPECT_EQ(storage(buffer), writeable_ptr); |
| EXPECT_EQ(0, writable_size); |
| EXPECT_EQ(0, buffer.ReadableBytes()); |
| |
| const SimpleBuffer buffer2; |
| EXPECT_EQ(0, buffer2.ReadableBytes()); |
| } |
| |
| TEST_F(SimpleBufferTest, BasicWR) { |
| SimpleBuffer buffer; |
| |
| EXPECT_EQ(4, buffer.Write(ibuf, 4)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(4, write_idx(buffer)); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| EXPECT_EQ(4, buffer.ReadableBytes()); |
| EXPECT_EQ("1234", buffer.GetReadableRegion()); |
| int bytes_written = 4; |
| EXPECT_TRUE(!buffer.Empty()); |
| |
| char* readable_ptr = nullptr; |
| int readable_size = 0; |
| buffer.GetReadablePtr(&readable_ptr, &readable_size); |
| char* writeable_ptr = nullptr; |
| int writable_size = 0; |
| buffer.GetWritablePtr(&writeable_ptr, &writable_size); |
| |
| EXPECT_EQ(storage(buffer), readable_ptr); |
| EXPECT_EQ(4, readable_size); |
| EXPECT_EQ(storage(buffer) + 4, writeable_ptr); |
| EXPECT_EQ(6, writable_size); |
| |
| char obuf[ABSL_ARRAYSIZE(ibuf)]; |
| int bytes_read = 0; |
| EXPECT_EQ(4, buffer.Read(obuf + bytes_read, 40)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| EXPECT_EQ(0, buffer.ReadableBytes()); |
| EXPECT_EQ("", buffer.GetReadableRegion()); |
| bytes_read += 4; |
| EXPECT_TRUE(buffer.Empty()); |
| buffer.GetReadablePtr(&readable_ptr, &readable_size); |
| buffer.GetWritablePtr(&writeable_ptr, &writable_size); |
| EXPECT_EQ(storage(buffer), readable_ptr); |
| EXPECT_EQ(0, readable_size); |
| EXPECT_EQ(storage(buffer), writeable_ptr); |
| EXPECT_EQ(kMinimumSimpleBufferSize, writable_size); |
| |
| EXPECT_EQ(bytes_written, bytes_read); |
| for (int i = 0; i < bytes_read; ++i) { |
| EXPECT_EQ(obuf[i], ibuf[i]); |
| } |
| |
| // More R/W tests. |
| EXPECT_EQ(10, buffer.Write(ibuf + bytes_written, 10)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(10, write_idx(buffer)); |
| EXPECT_EQ(10, storage_size(buffer)); |
| EXPECT_EQ(10, buffer.ReadableBytes()); |
| bytes_written += 10; |
| |
| EXPECT_TRUE(!buffer.Empty()); |
| |
| EXPECT_EQ(6, buffer.Read(obuf + bytes_read, 6)); |
| EXPECT_EQ(6, read_idx(buffer)); |
| EXPECT_EQ(10, write_idx(buffer)); |
| EXPECT_EQ(10, storage_size(buffer)); |
| EXPECT_EQ(4, buffer.ReadableBytes()); |
| bytes_read += 6; |
| |
| EXPECT_TRUE(!buffer.Empty()); |
| |
| EXPECT_EQ(4, buffer.Read(obuf + bytes_read, 7)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| EXPECT_EQ(10, storage_size(buffer)); |
| EXPECT_EQ(0, buffer.ReadableBytes()); |
| bytes_read += 4; |
| |
| EXPECT_TRUE(buffer.Empty()); |
| |
| EXPECT_EQ(bytes_written, bytes_read); |
| for (int i = 0; i < bytes_read; ++i) { |
| EXPECT_EQ(obuf[i], ibuf[i]); |
| } |
| } |
| |
| TEST_F(SimpleBufferTest, Reserve) { |
| SimpleBuffer buffer; |
| EXPECT_EQ(0, storage_size(buffer)); |
| |
| buffer.WriteString("foo"); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| |
| // Reserve by expanding the buffer. |
| buffer.Reserve(kMinimumSimpleBufferSize + 1); |
| EXPECT_EQ(2 * kMinimumSimpleBufferSize, storage_size(buffer)); |
| |
| buffer.Clear(); |
| buffer.AdvanceWritablePtr(kMinimumSimpleBufferSize); |
| buffer.AdvanceReadablePtr(kMinimumSimpleBufferSize - 2); |
| EXPECT_EQ(kMinimumSimpleBufferSize, write_idx(buffer)); |
| EXPECT_EQ(2 * kMinimumSimpleBufferSize, storage_size(buffer)); |
| |
| // Reserve by moving data around. `storage_size` does not change. |
| buffer.Reserve(kMinimumSimpleBufferSize + 1); |
| EXPECT_EQ(2, write_idx(buffer)); |
| EXPECT_EQ(2 * kMinimumSimpleBufferSize, storage_size(buffer)); |
| } |
| |
| TEST_F(SimpleBufferTest, Extend) { |
| SimpleBuffer buffer; |
| |
| // Test a write which should not extend the buffer. |
| EXPECT_EQ(7, buffer.Write(ibuf, 7)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(7, write_idx(buffer)); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| EXPECT_EQ(7, buffer.ReadableBytes()); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(7, write_idx(buffer)); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| EXPECT_EQ(7, buffer.ReadableBytes()); |
| int bytes_written = 7; |
| |
| // Test a write which should extend the buffer. |
| EXPECT_EQ(4, buffer.Write(ibuf + bytes_written, 4)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(11, write_idx(buffer)); |
| EXPECT_EQ(20, storage_size(buffer)); |
| EXPECT_EQ(11, buffer.ReadableBytes()); |
| bytes_written += 4; |
| |
| char obuf[ABSL_ARRAYSIZE(ibuf)]; |
| EXPECT_EQ(11, buffer.Read(obuf, 11)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| EXPECT_EQ(20, storage_size(buffer)); |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| EXPECT_EQ(0, buffer.ReadableBytes()); |
| |
| const int bytes_read = 11; |
| EXPECT_EQ(bytes_written, bytes_read); |
| for (int i = 0; i < bytes_read; ++i) { |
| EXPECT_EQ(obuf[i], ibuf[i]); |
| } |
| } |
| |
| TEST_F(SimpleBufferTest, Clear) { |
| SimpleBuffer buffer; |
| |
| buffer.Clear(); |
| |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| EXPECT_EQ(0, storage_size(buffer)); |
| EXPECT_EQ(0, buffer.ReadableBytes()); |
| |
| buffer.WriteString("foo"); |
| buffer.Clear(); |
| |
| EXPECT_EQ(0, read_idx(buffer)); |
| EXPECT_EQ(0, write_idx(buffer)); |
| EXPECT_EQ(kMinimumSimpleBufferSize, storage_size(buffer)); |
| EXPECT_EQ(0, buffer.ReadableBytes()); |
| } |
| |
| TEST_F(SimpleBufferTest, LongWrite) { |
| SimpleBuffer buffer; |
| |
| std::string s1 = "HTTP/1.1 500 Service Unavailable"; |
| buffer.Write(s1.data(), s1.size()); |
| buffer.Write("\r\n", 2); |
| std::string key = "Connection"; |
| std::string value = "close"; |
| buffer.Write(key.data(), key.size()); |
| buffer.Write(": ", 2); |
| buffer.Write(value.data(), value.size()); |
| buffer.Write("\r\n", 2); |
| buffer.Write("\r\n", 2); |
| std::string message = |
| "<html><head>\n" |
| "<meta http-equiv=\"content-type\"" |
| " content=\"text/html;charset=us-ascii\">\n" |
| "<style><!--\n" |
| "body {font-family: arial,sans-serif}\n" |
| "div.nav {margin-top: 1ex}\n" |
| "div.nav A {font-size: 10pt; font-family: arial,sans-serif}\n" |
| "span.nav {font-size: 10pt; font-family: arial,sans-serif;" |
| " font-weight: bold}\n" |
| "div.nav A,span.big {font-size: 12pt; color: #0000cc}\n" |
| "div.nav A {font-size: 10pt; color: black}\n" |
| "A.l:link {color: #6f6f6f}\n" |
| "A.u:link {color: green}\n" |
| "//--></style>\n" |
| "</head>\n" |
| "<body text=#000000 bgcolor=#ffffff>\n" |
| "<table border=0 cellpadding=2 cellspacing=0 width=100%>" |
| "<tr><td rowspan=3 width=1% nowrap>\n" |
| "<b>" |
| "<font face=times color=#0039b6 size=10>G</font>" |
| "<font face=times color=#c41200 size=10>o</font>" |
| "<font face=times color=#f3c518 size=10>o</font>" |
| "<font face=times color=#0039b6 size=10>g</font>" |
| "<font face=times color=#30a72f size=10>l</font>" |
| "<font face=times color=#c41200 size=10>e</font>" |
| " </b>\n" |
| "<td> </td></tr>\n" |
| "<tr><td bgcolor=#3366cc><font face=arial,sans-serif color=#ffffff>" |
| " <b>Error</b></td></tr>\n" |
| "<tr><td> </td></tr></table>\n" |
| "<blockquote>\n" |
| "<H1> Internal Server Error</H1>\n" |
| " This server was unable to complete the request\n" |
| "<p></blockquote>\n" |
| "<table width=100% cellpadding=0 cellspacing=0>" |
| "<tr><td bgcolor=#3366cc><img alt=\"\" width=1 height=4></td></tr>" |
| "</table>" |
| "</body></html>\n"; |
| buffer.Write(message.data(), message.size()); |
| const std::string correct_result = |
| "HTTP/1.1 500 Service Unavailable\r\n" |
| "Connection: close\r\n" |
| "\r\n" |
| "<html><head>\n" |
| "<meta http-equiv=\"content-type\"" |
| " content=\"text/html;charset=us-ascii\">\n" |
| "<style><!--\n" |
| "body {font-family: arial,sans-serif}\n" |
| "div.nav {margin-top: 1ex}\n" |
| "div.nav A {font-size: 10pt; font-family: arial,sans-serif}\n" |
| "span.nav {font-size: 10pt; font-family: arial,sans-serif;" |
| " font-weight: bold}\n" |
| "div.nav A,span.big {font-size: 12pt; color: #0000cc}\n" |
| "div.nav A {font-size: 10pt; color: black}\n" |
| "A.l:link {color: #6f6f6f}\n" |
| "A.u:link {color: green}\n" |
| "//--></style>\n" |
| "</head>\n" |
| "<body text=#000000 bgcolor=#ffffff>\n" |
| "<table border=0 cellpadding=2 cellspacing=0 width=100%>" |
| "<tr><td rowspan=3 width=1% nowrap>\n" |
| "<b>" |
| "<font face=times color=#0039b6 size=10>G</font>" |
| "<font face=times color=#c41200 size=10>o</font>" |
| "<font face=times color=#f3c518 size=10>o</font>" |
| "<font face=times color=#0039b6 size=10>g</font>" |
| "<font face=times color=#30a72f size=10>l</font>" |
| "<font face=times color=#c41200 size=10>e</font>" |
| " </b>\n" |
| "<td> </td></tr>\n" |
| "<tr><td bgcolor=#3366cc><font face=arial,sans-serif color=#ffffff>" |
| " <b>Error</b></td></tr>\n" |
| "<tr><td> </td></tr></table>\n" |
| "<blockquote>\n" |
| "<H1> Internal Server Error</H1>\n" |
| " This server was unable to complete the request\n" |
| "<p></blockquote>\n" |
| "<table width=100% cellpadding=0 cellspacing=0>" |
| "<tr><td bgcolor=#3366cc><img alt=\"\" width=1 height=4></td></tr>" |
| "</table>" |
| "</body></html>\n"; |
| EXPECT_EQ(correct_result, buffer.GetReadableRegion()); |
| } |
| |
| TEST_F(SimpleBufferTest, ReleaseAsSlice) { |
| SimpleBuffer buffer; |
| |
| buffer.WriteString("abc"); |
| SimpleBuffer::ReleasedBuffer released = buffer.Release(); |
| EXPECT_EQ("abc", absl::string_view(released.buffer.get(), released.size)); |
| |
| char* readable_ptr = nullptr; |
| int readable_size = 0; |
| buffer.GetReadablePtr(&readable_ptr, &readable_size); |
| EXPECT_EQ(nullptr, readable_ptr); |
| EXPECT_EQ(0, readable_size); |
| |
| buffer.WriteString("def"); |
| released = buffer.Release(); |
| buffer.GetReadablePtr(&readable_ptr, &readable_size); |
| EXPECT_EQ(nullptr, readable_ptr); |
| EXPECT_EQ(0, readable_size); |
| EXPECT_EQ("def", absl::string_view(released.buffer.get(), released.size)); |
| } |
| |
| TEST_F(SimpleBufferTest, EmptyBufferReleaseAsSlice) { |
| SimpleBuffer buffer; |
| char* readable_ptr = nullptr; |
| int readable_size = 0; |
| |
| SimpleBuffer::ReleasedBuffer released = buffer.Release(); |
| buffer.GetReadablePtr(&readable_ptr, &readable_size); |
| EXPECT_EQ(nullptr, readable_ptr); |
| EXPECT_EQ(0, readable_size); |
| EXPECT_TRUE(released.buffer == nullptr); |
| EXPECT_EQ(released.size, 0u); |
| } |
| |
| } // namespace |
| |
| } // namespace test |
| |
| } // namespace quiche |