diff options
Diffstat (limited to 'sax/tst')
| -rw-r--r-- | sax/tst/test_buffer.cc | 272 | ||||
| -rw-r--r-- | sax/tst/test_decoder.cc | 242 |
2 files changed, 514 insertions, 0 deletions
diff --git a/sax/tst/test_buffer.cc b/sax/tst/test_buffer.cc new file mode 100644 index 0000000..13bc6d4 --- /dev/null +++ b/sax/tst/test_buffer.cc @@ -0,0 +1,272 @@ +#include "buffer.hh" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace { + +enum class BufferType { + FIXED, + DYNAMIC, +}; + +class BufferTest : public testing::TestWithParam<BufferType> { + protected: + std::unique_ptr<modxml::sax::Buffer> 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<uint8_t, 10> AAAAAAAAAA{ + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}; +std::array<uint8_t, 5> 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<uint8_t, 10> 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<uint8_t, 3> 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<uint8_t, 5> 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<uint8_t, 0> 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<uint8_t, 1> 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<uint8_t, 5> tmp5; + EXPECT_TRUE(buf->read_all(tmp5)); + EXPECT_THAT(tmp5, testing::ContainerEq(BBBBB)); + + EXPECT_EQ(5u, buf->write(AAAAAAAAAA)); + + std::array<uint8_t, 10> 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)); + + 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<uint8_t, 3> tmp3; + EXPECT_TRUE(buf->read_all(tmp3)); + EXPECT_THAT(tmp3, testing::ElementsAre('B', 'B', 'B')); + + EXPECT_TRUE(buf->write_all(AAAAAAAAAA)); + + std::array<uint8_t, 12> 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<uint8_t, 7> 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<uint8_t, 12> 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<uint8_t, 30> 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<uint8_t, 60> 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<std::size_t>::max() will fail. + auto buf = modxml::sax::make_buffer(10, std::numeric_limits<std::size_t>::max()); + EXPECT_FALSE(buf->wspan(10000).empty()); + EXPECT_TRUE(buf->wspan(std::numeric_limits<std::size_t>::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<std::size_t>(5), span.size()); + for (uint8_t i = 0; i < len; ++i) + span[i] = 'C'; + + std::array<uint8_t, 10> 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<uint8_t, 2> tmp2; + EXPECT_TRUE(buf->read_all(tmp2)); + EXPECT_THAT(tmp2, testing::ElementsAre('B', 'B')); + + EXPECT_EQ(3u, buf->uncommit(3)); + std::array<uint8_t, 5> 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<uint8_t, 5> tmp5; + EXPECT_TRUE(buf->read_all(tmp5)); + + EXPECT_TRUE(buf->write_all(BBBBB)); + + EXPECT_EQ(8u, buf->uncommit(8)); + std::array<uint8_t, 2> 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 ""; + } +); diff --git a/sax/tst/test_decoder.cc b/sax/tst/test_decoder.cc new file mode 100644 index 0000000..86f230b --- /dev/null +++ b/sax/tst/test_decoder.cc @@ -0,0 +1,242 @@ +#include "sax_decoder.hh" +#include "sax_decoder_factory.hh" +#include "sax_processor.hh" +#include "sax_delegate.hh" + +#include <memory> +#include <gtest/gtest.h> + +namespace { + +class TestDelegate : public modxml::sax::Delegate { + public: + ~TestDelegate() override = default; + + void empty_element(std::string_view name, + modxml::sax::Attributes const&) override { + EXPECT_EQ(name, "root"); + if (name == "root") { + EXPECT_FALSE(have_root_); + have_root_ = true; + } + } + + void error(std::string_view message) override { + have_error_ = true; + FAIL() << message; + } + + bool have_root() const { return have_root_; } + + bool have_error() const { return have_error_; } + + private: + bool have_root_{false}; + bool have_error_{false}; +}; + +bool process_all(modxml::sax::Processor& processor, + TestDelegate& delegate, + std::span<uint8_t const> data) { + std::size_t offset = 0; + while (offset < data.size()) { + auto consumed = processor.process(data, offset); + if (consumed == 0 || delegate.have_error()) + return false; + offset += consumed; + } + return true; +} + +} // namespace + +TEST(sax, decoder_utf8) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::string input = R"(<?xml version="1.0" encoding="utf-8"?><root />)"; + std::cerr << input << std::endl; + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(reinterpret_cast<uint8_t const*>(input.data()), + input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf8_bom) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::string input = + "\xef\xbb\xbf" R"(<?xml version="1.0" encoding="utf-8"?><root />)"; + std::cerr << input << std::endl; + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(reinterpret_cast<uint8_t const*>(input.data()), + input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf16) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u16string input = uR"(<?xml version="1.0" encoding="utf-16"?><root />)"; + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(reinterpret_cast<uint8_t const*>(input.data()), + input.size() * sizeof(char16_t)))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf16be) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u16string str = uR"(<?xml version="1.0" encoding="utf-16"?><root />)"; + std::vector<uint8_t> input; + for (char16_t c : str) { + input.push_back(c >> 8); + input.push_back(c & 0xff); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf16le) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u16string str = uR"(<?xml version="1.0" encoding="utf-16"?><root />)"; + std::vector<uint8_t> input; + for (char16_t c : str) { + input.push_back(c & 0xff); + input.push_back(c >> 8); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf16be_bom) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u16string str = + u"\ufffe" uR"(<?xml version="1.0" encoding="utf-16"?><root />)"; + std::vector<uint8_t> input; + for (char16_t c : str) { + input.push_back(c >> 8); + input.push_back(c & 0xff); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf16le_bom) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u16string str = + u"\ufffe" uR"(<?xml version="1.0" encoding="utf-16"?><root />)"; + std::vector<uint8_t> input; + for (char16_t c : str) { + input.push_back(c & 0xff); + input.push_back(c >> 8); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf32) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u32string input = UR"(<?xml version="1.0" encoding="utf-32"?><root />)"; + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(reinterpret_cast<uint8_t const*>(input.data()), + input.size() * sizeof(char32_t)))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf32be) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u32string str = UR"(<?xml version="1.0" encoding="utf-32"?><root />)"; + std::vector<uint8_t> input; + for (char32_t c : str) { + input.push_back(c >> 24); + input.push_back((c >> 16) & 0xff); + input.push_back((c >> 8) & 0xff); + input.push_back(c & 0xff); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf32le) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u32string str = UR"(<?xml version="1.0" encoding="utf-32"?><root />)"; + std::vector<uint8_t> input; + for (char32_t c : str) { + input.push_back(c & 0xff); + input.push_back((c >> 8) & 0xff); + input.push_back((c >> 16) & 0xff); + input.push_back(c >> 24); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf32be_bom) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u32string str = + U"\ufffe" UR"(<?xml version="1.0" encoding="utf-32"?><root />)"; + std::vector<uint8_t> input; + for (char32_t c : str) { + input.push_back(c >> 24); + input.push_back((c >> 16) & 0xff); + input.push_back((c >> 8) & 0xff); + input.push_back(c & 0xff); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} + +TEST(sax, decoder_utf32le_bom) { + auto delegate = std::make_shared<TestDelegate>(); + auto processor = modxml::sax::Processor::create(delegate); + std::u32string str = + U"\ufffe" R"(<?xml version="1.0" encoding="utf-32"?><root />)"; + std::vector<uint8_t> input; + for (char32_t c : str) { + input.push_back(c & 0xff); + input.push_back((c >> 8) & 0xff); + input.push_back((c >> 16) & 0xff); + input.push_back(c >> 24); + } + EXPECT_TRUE(process_all( + *processor.get(), + *delegate.get(), + std::span<uint8_t const>(input.data(), input.size()))); + EXPECT_TRUE(delegate->have_root()); +} |
