Replace the 32-bit `uint32_t seen` bitmask with a `std::vector<uint64_t>` bitset sized to the actual field count. `requiredMask` is now a vector of the same word count, and required-field checks use per-word masking so that optional fields do not cause false rejections. Adds big.schema.json + test_big.cpp regression tests covering the issue reproducer (40 required properties, missing/duplicate at index 32), plus run_tests.sh to exercise both test_gen.cpp and test_big.cpp. Fixes #3
141 lines
4.1 KiB
C++
141 lines
4.1 KiB
C++
// Regression test for issue #3: objects with more than 32 properties.
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <string>
|
|
|
|
#include "big.h"
|
|
|
|
using namespace big_schema;
|
|
|
|
static WeaselJsonStatus parseStrided(RootBuilder &b, std::string in) {
|
|
for (size_t i = 0; i < in.size(); ++i) {
|
|
char c = in[i];
|
|
WeaselJsonStatus s = b.feed(&c, 1);
|
|
if (s != WeaselJson_AGAIN)
|
|
return s;
|
|
}
|
|
return b.finish();
|
|
}
|
|
|
|
static int failures = 0;
|
|
#define CHECK(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
|
|
++failures; \
|
|
} \
|
|
} while (0)
|
|
|
|
static void expectReject(const std::string &json, const char *what) {
|
|
RootBuilder b;
|
|
WeaselJsonStatus s = parseStrided(b, json);
|
|
if (s == WeaselJson_REJECT) {
|
|
printf("ok reject: %s\n", what);
|
|
} else {
|
|
printf("FAIL expected reject (%s) got status %d for: %s\n", what, s,
|
|
json.c_str());
|
|
++failures;
|
|
}
|
|
}
|
|
|
|
static std::string all40() {
|
|
std::string json = "{";
|
|
for (int i = 0; i < 40; ++i) {
|
|
if (i)
|
|
json += ",";
|
|
json += "\"p" + std::to_string(i) + "\":\"v" + std::to_string(i) + "\"";
|
|
}
|
|
json += "}";
|
|
return json;
|
|
}
|
|
|
|
int main() {
|
|
// All 40 required fields present -> accepted, values land in right slots.
|
|
{
|
|
RootBuilder b;
|
|
std::string json = all40();
|
|
WeaselJsonStatus s = parseStrided(b, json);
|
|
CHECK(s == WeaselJson_OK);
|
|
if (s == WeaselJson_OK) {
|
|
Root r = b.take();
|
|
CHECK(r.p0 == "v0");
|
|
CHECK(r.p31 == "v31");
|
|
CHECK(r.p32 == "v32");
|
|
CHECK(r.p39 == "v39");
|
|
printf("ok all 40 required fields accepted\n");
|
|
}
|
|
}
|
|
|
|
// Missing field p32 (index 32) must be detected, not aliased to bit 0.
|
|
{
|
|
std::string json = all40();
|
|
// Remove the p32 entry: find its substring and erase it.
|
|
std::string entry = "\"p32\":\"v32\"";
|
|
size_t pos = json.find(entry);
|
|
assert(pos != std::string::npos);
|
|
// Remove the trailing comma before it if present, or the leading comma
|
|
// after it.
|
|
if (pos > 0 && json[pos - 1] == ',') {
|
|
json.erase(pos - 1, entry.size() + 1);
|
|
} else if (pos + entry.size() < json.size() &&
|
|
json[pos + entry.size()] == ',') {
|
|
json.erase(pos, entry.size() + 1);
|
|
} else {
|
|
json.erase(pos, entry.size());
|
|
}
|
|
expectReject(json, "missing required field p32 (index 32)");
|
|
}
|
|
|
|
// Missing p0 still rejected.
|
|
{
|
|
std::string json = all40();
|
|
std::string entry = "\"p0\":\"v0\"";
|
|
size_t pos = json.find(entry);
|
|
assert(pos != std::string::npos);
|
|
if (json[pos + entry.size()] == ',') {
|
|
json.erase(pos, entry.size() + 1);
|
|
} else {
|
|
json.erase(pos, entry.size());
|
|
}
|
|
expectReject(json, "missing required field p0");
|
|
}
|
|
|
|
// Duplicate keys p0 and p32 must both be detected.
|
|
{
|
|
std::string json = all40();
|
|
// Insert p32 again right after the existing p32 entry.
|
|
std::string entry = "\"p32\":\"v32\"";
|
|
size_t pos = json.find(entry);
|
|
assert(pos != std::string::npos);
|
|
json.insert(pos + entry.size(), ",\"p32\":\"dup\"");
|
|
expectReject(json, "duplicate key p32 (index 32)");
|
|
}
|
|
|
|
// Duplicate keys p0 and p31 cover boundary bits.
|
|
{
|
|
std::string json = all40();
|
|
std::string entry = "\"p0\":\"v0\"";
|
|
size_t pos = json.find(entry);
|
|
assert(pos != std::string::npos);
|
|
json.insert(pos + entry.size(), ",\"p0\":\"dup\"");
|
|
expectReject(json, "duplicate key p0");
|
|
}
|
|
|
|
// Duplicate p31/p32 around the 32-bit boundary.
|
|
{
|
|
std::string json = all40();
|
|
std::string entry = "\"p31\":\"v31\"";
|
|
size_t pos = json.find(entry);
|
|
assert(pos != std::string::npos);
|
|
json.insert(pos + entry.size(), ",\"p31\":\"dup\"");
|
|
expectReject(json, "duplicate key p31");
|
|
}
|
|
|
|
if (failures == 0) {
|
|
printf("\nALL TESTS PASSED\n");
|
|
return 0;
|
|
}
|
|
printf("\n%d FAILURE(S)\n", failures);
|
|
return 1;
|
|
}
|