#include "buffer.hh" #include #include namespace { enum class BufferType { FIXED, DYNAMIC, }; class BufferTest : public testing::TestWithParam { protected: std::unique_ptr make_buffer(std::size_t size) { switch (GetParam()) { case BufferType::FIXED: return modxml::sax::make_buffer(size, size); case BufferType::DYNAMIC: return modxml::sax::make_buffer(size / 2, size); } return nullptr; } }; std::array AAAAAAAAAA{ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}; std::array BBBBB{ 'B', 'B', 'B', 'B', 'B'}; } // namespace TEST_P(BufferTest, sanity) { auto buf = make_buffer(10); EXPECT_TRUE(buf->empty()); EXPECT_FALSE(buf->full()); EXPECT_TRUE(buf->write_all(AAAAAAAAAA)); EXPECT_TRUE(buf->full()); EXPECT_FALSE(buf->empty()); EXPECT_FALSE(buf->write_all(AAAAAAAAAA)); std::array tmp10; EXPECT_TRUE(buf->read_all(tmp10)); EXPECT_THAT(tmp10, testing::ContainerEq(AAAAAAAAAA)); EXPECT_TRUE(buf->empty()); EXPECT_FALSE(buf->full()); EXPECT_TRUE(buf->write_all(BBBBB)); EXPECT_FALSE(buf->full()); EXPECT_FALSE(buf->empty()); EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); EXPECT_TRUE(buf->full()); EXPECT_FALSE(buf->empty()); std::array tmp3; EXPECT_TRUE(buf->read_all(tmp3)); EXPECT_THAT(tmp3, testing::ElementsAre('B', 'B', 'B')); EXPECT_EQ(3u, buf->write(BBBBB)); EXPECT_TRUE(buf->read_all(tmp3)); EXPECT_THAT(tmp3, testing::ElementsAre('B', 'B', 'A')); std::array tmp5; EXPECT_TRUE(buf->read_all(tmp5)); EXPECT_THAT(tmp5, testing::ElementsAre('A', 'A', 'A', 'A', 'B')); EXPECT_FALSE(buf->read_all(tmp3)); tmp3[2] = 'X'; EXPECT_EQ(2u, buf->read(tmp3)); EXPECT_THAT(tmp3, testing::ElementsAre('B', 'B', 'X')); } TEST_P(BufferTest, noop) { auto buf = make_buffer(10); EXPECT_TRUE(buf->empty()); std::array empty; EXPECT_EQ(0u, buf->write(empty)); EXPECT_EQ(0u, buf->read(empty)); EXPECT_TRUE(buf->write_all(empty)); EXPECT_TRUE(buf->read_all(empty)); buf->commit(0); buf->consume(0); EXPECT_TRUE(buf->empty()); } TEST_P(BufferTest, one_byte_filler) { auto buf = make_buffer(10); std::array tmp1; uint8_t out = 0; for (uint8_t in = 0; in <= 20; ++in) { tmp1[0] = in; EXPECT_TRUE(buf->write_all(tmp1)); if (in >= 9) { EXPECT_TRUE(buf->read_all(tmp1)); EXPECT_EQ(tmp1[0], out); ++out; } } for (; out <= 20; ++out) { EXPECT_TRUE(buf->read_all(tmp1)); EXPECT_EQ(tmp1[0], out); } EXPECT_TRUE(buf->empty()); } TEST_P(BufferTest, read_wrap) { auto buf = make_buffer(10); EXPECT_TRUE(buf->write_all(BBBBB)); EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); std::array tmp5; EXPECT_TRUE(buf->read_all(tmp5)); EXPECT_THAT(tmp5, testing::ContainerEq(BBBBB)); EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); std::array tmp10; EXPECT_TRUE(buf->read_all(tmp10)); EXPECT_THAT(tmp10, testing::ContainerEq(AAAAAAAAAA)); } TEST_P(BufferTest, skip_wrap) { auto buf = make_buffer(10); EXPECT_TRUE(buf->write_all(BBBBB)); EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); buf->consume(5); EXPECT_FALSE(buf->empty()); EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); auto data = buf->rspan(10); EXPECT_EQ(10u, data.size()); buf->consume(10); EXPECT_TRUE(buf->empty()); } TEST_P(BufferTest, write_wrap) { auto buf = make_buffer(12); EXPECT_TRUE(buf->write_all(BBBBB)); std::array tmp3; EXPECT_TRUE(buf->read_all(tmp3)); EXPECT_THAT(tmp3, testing::ElementsAre('B', 'B', 'B')); EXPECT_TRUE(buf->write_all(AAAAAAAAAA)); std::array tmp12; EXPECT_EQ(12u, buf->read(tmp12)); EXPECT_THAT(tmp12, testing::ElementsAre( 'B', 'B', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A')); } TEST_P(BufferTest, read_wrap2) { auto buf = make_buffer(12); EXPECT_TRUE(buf->write_all(AAAAAAAAAA)); std::array tmp7; EXPECT_TRUE(buf->read_all(tmp7)); EXPECT_THAT(tmp7, testing::ElementsAre('A', 'A', 'A', 'A', 'A', 'A', 'A')); EXPECT_EQ(5u, buf->write(BBBBB)); EXPECT_EQ(4u, buf->write(BBBBB)); std::array tmp12; EXPECT_TRUE(buf->read_all(tmp12)); EXPECT_THAT(tmp12, testing::ElementsAre( 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B')); } TEST(Buffer, dynamic_resize) { auto buf = modxml::sax::make_buffer(10, 1000); std::array tmp30; for (uint8_t i = 0; i < 30; ++i) tmp30[i] = i; EXPECT_TRUE(buf->write_all(tmp30)); EXPECT_TRUE(buf->write_all(tmp30)); std::array tmp60; EXPECT_TRUE(buf->read_all(tmp60)); for (uint8_t i = 0; i < 60; ++i) EXPECT_EQ(i % 30, tmp60[i]) << i; } TEST(Buffer, dynamic_overalloc) { // This test can fail, but in most configurations trying to allocate // std::numeric_limits::max() will fail. auto buf = modxml::sax::make_buffer(10, std::numeric_limits::max()); EXPECT_FALSE(buf->wspan(10000).empty()); EXPECT_TRUE(buf->wspan(std::numeric_limits::max()).empty()); } TEST_P(BufferTest, modify) { auto buf = make_buffer(10); EXPECT_TRUE(buf->write_all(AAAAAAAAAA)); auto span = buf->mspan(5); EXPECT_EQ(10u, span.size()); auto len = std::min(static_cast(5), span.size()); for (uint8_t i = 0; i < len; ++i) span[i] = 'C'; std::array tmp10; EXPECT_TRUE(buf->read_all(tmp10)); EXPECT_THAT(tmp10, testing::ElementsAre( 'C', 'C', 'C', 'C', 'C', 'A', 'A', 'A', 'A', 'A')); } TEST_P(BufferTest, uncommit) { auto buf = make_buffer(10); EXPECT_TRUE(buf->write_all(BBBBB)); EXPECT_EQ(0u, buf->uncommit(0)); EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); std::array tmp2; EXPECT_TRUE(buf->read_all(tmp2)); EXPECT_THAT(tmp2, testing::ElementsAre('B', 'B')); EXPECT_EQ(3u, buf->uncommit(3)); std::array tmp5; EXPECT_TRUE(buf->read_all(tmp5)); EXPECT_THAT(tmp5, testing::ElementsAre('B', 'B', 'B', 'A', 'A')); EXPECT_EQ(0u, buf->uncommit(2)); } TEST_P(BufferTest, uncommit_wrap) { auto buf = make_buffer(10); EXPECT_TRUE(buf->write_all(AAAAAAAAAA)); std::array tmp5; EXPECT_TRUE(buf->read_all(tmp5)); EXPECT_TRUE(buf->write_all(BBBBB)); EXPECT_EQ(8u, buf->uncommit(8)); std::array tmp2; EXPECT_TRUE(buf->read_all(tmp2)); EXPECT_THAT(tmp2, testing::ElementsAre('A', 'A')); } INSTANTIATE_TEST_SUITE_P( BufferTests, BufferTest, testing::Values(BufferType::FIXED, BufferType::DYNAMIC), [](auto& info) { switch (info.param) { case BufferType::FIXED: return "fixed"; case BufferType::DYNAMIC: return "dynamic"; } return ""; } );