This commit is contained in:
2026-05-03 11:16:56 +10:00
parent f6db9ee3e6
commit ee929374a8
14 changed files with 873 additions and 350 deletions

119
tests/RuntimeJsonTests.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include "RuntimeJson.h"
#include <cmath>
#include <iostream>
#include <string>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
JsonValue ParseOrFail(const std::string& text, const char* message)
{
JsonValue value;
std::string error;
if (!ParseJson(text, value, error))
{
std::cerr << "FAIL: " << message << ": " << error << "\n";
++gFailures;
}
return value;
}
void ExpectParseFails(const std::string& text, const char* message)
{
JsonValue value;
std::string error;
Expect(!ParseJson(text, value, error), message);
Expect(!error.empty(), "failed parse should include an error message");
}
void TestStringsAndEscaping()
{
const JsonValue value = ParseOrFail(R"({"text":"line\nquote:\" slash:\\ tab:\t"})", "escaped string parses");
const JsonValue* text = value.find("text");
Expect(text && text->isString(), "escaped string field is present");
Expect(text && text->asString().find("line\nquote:\" slash:\\ tab:\t") != std::string::npos, "escaped string unescapes expected characters");
JsonValue controlString(std::string("a\001b", 3));
Expect(SerializeJson(controlString, false) == R"("a\u0001b")", "serializer escapes raw control characters");
ExpectParseFails(std::string("{\"bad\":\"a") + static_cast<char>(1) + "b\"}", "raw control characters are rejected");
}
void TestUnicodeEscapes()
{
const JsonValue copyright = ParseOrFail(R"({"s":"\u00A9"})", "basic unicode escape parses");
Expect(copyright.find("s") && copyright.find("s")->asString() == "\xC2\xA9", "basic unicode escape becomes UTF-8");
const JsonValue music = ParseOrFail(R"({"s":"\uD834\uDD1E"})", "surrogate pair parses");
Expect(music.find("s") && music.find("s")->asString() == "\xF0\x9D\x84\x9E", "surrogate pair becomes UTF-8");
ExpectParseFails(R"({"s":"\uD834x"})", "unpaired high surrogate is rejected");
ExpectParseFails(R"({"s":"\uDD1E"})", "unpaired low surrogate is rejected");
ExpectParseFails(R"({"s":"\u00XZ"})", "invalid unicode hex is rejected");
}
void TestNumbers()
{
const JsonValue value = ParseOrFail(R"({"a":0,"b":-12.5e+2,"c":3.25})", "valid numbers parse");
Expect(value.find("a") && value.find("a")->asNumber(-1.0) == 0.0, "zero parses");
Expect(value.find("b") && std::fabs(value.find("b")->asNumber() + 1250.0) < 0.0001, "exponent parses");
Expect(value.find("c") && std::fabs(value.find("c")->asNumber() - 3.25) < 0.0001, "fraction parses");
ExpectParseFails("01", "leading zero is rejected");
ExpectParseFails("1.", "fraction without digits is rejected");
ExpectParseFails("1e", "exponent without digits is rejected");
ExpectParseFails("1e9999", "non-finite number is rejected");
}
void TestRoundtripAndMutation()
{
JsonValue root = JsonValue::MakeObject();
root.set("version", JsonValue(1.0));
root.set("name", JsonValue("Preset One"));
JsonValue layers = JsonValue::MakeArray();
JsonValue layer = JsonValue::MakeObject();
layer.set("id", JsonValue("layer-a"));
layer.set("bypass", JsonValue(false));
layers.pushBack(layer);
root.set("layers", layers);
const std::string serialized = SerializeJson(root, true);
const JsonValue parsed = ParseOrFail(serialized, "serialized preset-style object parses");
Expect(parsed.find("layers") && parsed.find("layers")->isArray(), "roundtripped layers array exists");
JsonValue mutableValue = JsonValue::MakeObject();
mutableValue.set("stale", JsonValue("value"));
mutableValue.pushBack(JsonValue(7.0));
Expect(mutableValue.isArray(), "pushBack changes object into array");
Expect(mutableValue.asObject().empty(), "object storage is cleared when changing to array");
Expect(mutableValue.asArray().size() == 1, "new array receives pushed value");
}
}
int main()
{
TestStringsAndEscaping();
TestUnicodeEscapes();
TestNumbers();
TestRoundtripAndMutation();
if (gFailures != 0)
{
std::cerr << gFailures << " RuntimeJson test failure(s).\n";
return 1;
}
std::cout << "RuntimeJson tests passed.\n";
return 0;
}