schemagen: deduplicate enum constants that collide after sanitization
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 51s
CI / pre-commit (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m29s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m22s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 51s
CI / pre-commit (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m29s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m22s
Distinct JSON enum values can sanitize to the same C++ identifier (e.g. "foo-bar" and "foo_bar" both become `foo_bar`), producing an invalid `enum class` with duplicate constants. Add `unique_enum_identifiers()` which appends a numeric suffix to later collisions while preserving enum declaration order, so the index-to-JSON-value mapping used by the generated parser stays intact. Also add contrib/schemagen/test_schemagen.py and wire the schemagen Python tests plus the existing example.schema.json/test_gen.cpp example into ctest via CMakeLists.txt. Closes #4
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Tests for weaseljson_schemagen.py."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
SCRIPT = os.path.join(os.path.dirname(__file__), "weaseljson_schemagen.py")
|
||||
|
||||
|
||||
class SchemagenEnumTest(unittest.TestCase):
|
||||
def run_schemagen(self, schema, args=None):
|
||||
"""Run schemagen on a schema dict. Returns (returncode, stdout, stderr)."""
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as fp:
|
||||
json.dump(schema, fp)
|
||||
schema_path = fp.name
|
||||
try:
|
||||
cmd = [sys.executable, SCRIPT, schema_path]
|
||||
if args:
|
||||
cmd.extend(args)
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
||||
return result.returncode, result.stdout, result.stderr
|
||||
finally:
|
||||
os.unlink(schema_path)
|
||||
|
||||
def test_colliding_enum_values_deduplicate(self):
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {"role": {"enum": ["foo-bar", "foo_bar"]}},
|
||||
}
|
||||
rc, stdout, stderr = self.run_schemagen(schema)
|
||||
self.assertEqual(rc, 0, msg=stderr)
|
||||
self.assertIn("enum class Role : int { foo_bar, foo_bar_1 };", stdout)
|
||||
self.assertIn(
|
||||
'static constexpr const char *Role_names[] = { "foo-bar", "foo_bar" };',
|
||||
stdout,
|
||||
)
|
||||
|
||||
def test_distinct_enum_values_generate(self):
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {"role": {"enum": ["admin", "user", "guest"]}},
|
||||
}
|
||||
rc, stdout, stderr = self.run_schemagen(schema)
|
||||
self.assertEqual(rc, 0, msg=stderr)
|
||||
self.assertIn("enum class Role : int { admin, user, guest };", stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user