schemagen: cache non-object $defs entries to avoid duplicate types
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Failing after 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Failing after 51s
CI / pre-commit (pull_request) Successful in 52s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Failing after 1m12s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Failing after 1m14s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Failing after 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Failing after 51s
CI / pre-commit (pull_request) Successful in 52s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Failing after 1m12s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Failing after 1m14s
Extend the existing per-definition cache (`self._building`) to enum, array, and scalar $defs, not just object definitions. This ensures that multiple $refs to the same non-object definition reuse the same C++ type instead of generating Role, Role2, Role3, etc. - Cache the built (type, nullable) tuple under defname for enum, scalar, and array definitions. - Pre-register array definitions before recursing into items so $ref cycles resolve to the same TArr instance. - Store object definitions as (TObj, nullable) tuples so nullable object $defs also preserve their nullability when referenced. Add a regression test for issue #17 covering reused enum and array-of-enum $defs.
This commit is contained in:
@@ -195,6 +195,37 @@ class SchemagenCollisionTest(unittest.TestCase):
|
||||
self.assertIn("Arr0", enumerators)
|
||||
self.assertEqual(len(enumerators), len(set(enumerators)))
|
||||
|
||||
def test_non_object_defs_reused_across_refs(self):
|
||||
"""Regression test for issue #17: enum and array $defs must be reused."""
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role1": {"$ref": "#/$defs/Role"},
|
||||
"role2": {"$ref": "#/$defs/Role"},
|
||||
"roles1": {"$ref": "#/$defs/Roles"},
|
||||
"roles2": {"$ref": "#/$defs/Roles"},
|
||||
},
|
||||
"$defs": {
|
||||
"Role": {"enum": ["admin", "user"]},
|
||||
"Roles": {"type": "array", "items": {"$ref": "#/$defs/Role"}},
|
||||
},
|
||||
}
|
||||
out = self.generate_and_compile(schema)
|
||||
# Exactly one Role enum is generated.
|
||||
self.assertIn("enum class Role : int { admin, user };", out)
|
||||
self.assertNotIn("enum class Role1 : int", out)
|
||||
self.assertNotIn("enum class Role2 : int", out)
|
||||
# The array of enum is represented by a single kind.
|
||||
m = re.search(r"enum class Kind : uint8_t \{([^}]+)\}", out)
|
||||
self.assertIsNotNone(m)
|
||||
enumerators = [e.strip() for e in m.group(1).split(",")]
|
||||
self.assertEqual(enumerators.count("Arr0"), 1)
|
||||
# All four fields use the same C++ types.
|
||||
self.assertIn("std::optional<Role> role1;", out)
|
||||
self.assertIn("std::optional<Role> role2;", out)
|
||||
self.assertIn("std::optional<std::vector<Role>> roles1;", out)
|
||||
self.assertIn("std::optional<std::vector<Role>> roles2;", out)
|
||||
|
||||
|
||||
class SchemagenAdditionalPropertiesTest(unittest.TestCase):
|
||||
def run_schemagen(self, schema, args=None):
|
||||
|
||||
Reference in New Issue
Block a user