schemagen: support objects with more than 32 properties
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
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user