From 18dfc724cea4640f4e3ff9f764c152c40c9b715f Mon Sep 17 00:00:00 2001 From: sfja Date: Mon, 30 Mar 2026 12:03:15 +0200 Subject: [PATCH] add json tests --- backend/Makefile | 3 +-- backend/src/json.cpp | 11 ++++----- backend/src/json.hpp | 53 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index ed14531..19d6216 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -46,7 +46,6 @@ objects = $(objects_without_main) $(main_object) target = $(build_dir)/backend test_sources = $(shell find $(test_dir) -name '*.cpp') -test_objects = $(test_sources:$(test_dir)/%.cpp=$(obj_dir)/$(test_dir)/%.o) test_targets = $(test_sources:$(test_dir)/%.cpp=$(build_dir)/$(test_dir)/test_%) all: $(target) $(test_targets) @@ -61,7 +60,7 @@ $(obj_dir)/%.o:%.cpp test: $(test_targets) printf "%s\n" $^ | xargs -I % sh -c 'echo "- %..." && ./% && echo "- %: OK" || (echo "- %: FAILED" && exit 1)' -$(build_dir)/$(test_dir)/test_%: $(test_objects) $(objects_without_main) +$(build_dir)/$(test_dir)/test_%: $(obj_dir)/tests/%.o $(objects_without_main) @mkdir -p $(dir $@) $(LD) -o $@ $(CXXFLAGS) $^ $(LDFLAGS) diff --git a/backend/src/json.cpp b/backend/src/json.cpp index db699cb..efca07d 100644 --- a/backend/src/json.cpp +++ b/backend/src/json.cpp @@ -47,7 +47,7 @@ public: Loc loc = { m_idx, m_line, m_col }; size_t* i = &m_idx; if (*i >= m_len) [[unlikely]] { - return tok(TokTy::Eof, loc); + return Tok { TokTy::Eof, std::string_view(nullptr, 0), loc }; } bool matched = false; while (*i < m_len && std::strchr(" \t\r\n", m_text[*i]) != NULL) { @@ -139,7 +139,7 @@ private: } step(); } - if (*i < m_len && m_text[*i] != '\n') [[unlikely]] { + if (*i < m_len && m_text[*i] == '\n') [[unlikely]] { return std::unexpected(Error { loc, "malformed string" }); } if (*i >= m_len && m_text[*i] != '\"') [[unlikely]] { @@ -167,7 +167,7 @@ auto literal_to_string(std::string_view text) -> Result { auto result = std::string(); - for (size_t i = 1; i < text.size() - 2; ++i) { + for (size_t i = 1; i < text.size() - 1; ++i) { if (text[i] == '\\') [[unlikely]] { i += 1; if (i >= text.size()) [[unlikely]] { @@ -206,7 +206,8 @@ public: Parser(std::string_view text) : m_tokenizer(text) { - step().value(); + auto result = step(); + result.value(); } auto parse() -> Result> @@ -711,7 +712,7 @@ auto Value::write(std::ostream& stream, WriteProfile profile) const } } -auto Value::to_string(WriteProfile profile) -> std::string +auto Value::to_string(WriteProfile profile) const -> std::string { auto stream = std::stringstream(); if (profile == WriteProfile::Minified) { diff --git a/backend/src/json.hpp b/backend/src/json.hpp index 6e1013b..026d14c 100644 --- a/backend/src/json.hpp +++ b/backend/src/json.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -267,7 +268,7 @@ public: auto write(std::ostream& stream, WriteProfile profile = WriteProfile::Minified) const; - auto to_string(WriteProfile profile = WriteProfile::Minified) + auto to_string(WriteProfile profile = WriteProfile::Minified) const -> std::string; private: @@ -275,6 +276,56 @@ private: Data m_data; }; +inline auto operator==(const Value& lhs, const Value& rhs) -> bool +{ + if (lhs.type() != rhs.type()) + return false; + switch (lhs.type()) { + case Type::Null: + case Type::False: + case Type::True: + return true; + case Type::I64: + return lhs.get_i64() == rhs.get_i64(); + case Type::F64: + return lhs.get_f64() == rhs.get_f64(); + case Type::String: + return lhs.get_string() == rhs.get_string(); + case Type::Array: + if (lhs.size() != rhs.size()) + return false; + for (size_t i = 0; i < lhs.size(); ++i) + if (lhs[i] != rhs[i]) + return false; + return true; + case Type::Object: + if (lhs.size() != rhs.size()) + return false; + for (const auto& [key, val] : lhs.get_underlying_object()) { + if (!rhs.has(key)) + return false; + if (*val != rhs[key]) + return false; + } + return true; + break; + } + std::unreachable(); +} + auto parse(std::string_view text) -> Result>; } + +template <> struct std::formatter { + constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); + } + + auto format(const mst::json::Value& value, std::format_context& ctx) const + { + return std::format_to( + ctx.out(), "{}", value.to_string(mst::json::WriteProfile::Pretty)); + } +};