summaryrefslogtreecommitdiff
path: root/sax/tst
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2024-01-21 12:31:30 +0100
committerJoel Klinghed <the_jk@spawned.biz>2024-01-21 12:31:30 +0100
commit7dd49c6293172b494c78918507242cdb55d35137 (patch)
tree9c8ab822ab9501a5ea2f937e609144e00ea091c4 /sax/tst
parentfc4547b412e28164af1bf8981234c6af959ccc0b (diff)
WIP
Diffstat (limited to 'sax/tst')
-rw-r--r--sax/tst/test_buffer.cc272
-rw-r--r--sax/tst/test_decoder.cc242
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());
+}