Add a test that validates python type parsing matches to chip-types.xml (#30711)
* Add unit test for supported types to have python match spec
* Restyle
* Add test to list of runnable tests
* Add semtag handling
* Add enum24 since chip-types defines it
* Update imports
* Remove unused imports
* zap regen
* Fix todo comment
diff --git a/scripts/py_matter_idl/BUILD.gn b/scripts/py_matter_idl/BUILD.gn
index 01983d0..9fef16e 100644
--- a/scripts/py_matter_idl/BUILD.gn
+++ b/scripts/py_matter_idl/BUILD.gn
@@ -68,6 +68,7 @@
"matter_idl/test_matter_idl_parser.py",
"matter_idl/test_generators.py",
"matter_idl/test_idl_generator.py",
+ "matter_idl/test_supported_types.py",
"matter_idl/test_zapxml.py",
]
diff --git a/scripts/py_matter_idl/matter_idl/generators/type_definitions.py b/scripts/py_matter_idl/matter_idl/generators/type_definitions.py
index 29d496b..b5243e4 100644
--- a/scripts/py_matter_idl/matter_idl/generators/type_definitions.py
+++ b/scripts/py_matter_idl/matter_idl/generators/type_definitions.py
@@ -169,6 +169,7 @@
"bitmap64": BasicInteger(idl_name="bitmap64", byte_count=8, is_signed=False),
"bitmap8": BasicInteger(idl_name="bitmap8", byte_count=1, is_signed=False),
"enum16": BasicInteger(idl_name="enum16", byte_count=2, is_signed=False),
+ "enum24": BasicInteger(idl_name="enum24", byte_count=3, is_signed=False),
"enum32": BasicInteger(idl_name="enum32", byte_count=4, is_signed=False),
"enum8": BasicInteger(idl_name="enum8", byte_count=1, is_signed=False),
"int16s": BasicInteger(idl_name="int16s", byte_count=2, is_signed=True),
@@ -188,7 +189,7 @@
"int8s": BasicInteger(idl_name="int8s", byte_count=1, is_signed=True),
"int8u": BasicInteger(idl_name="int8u", byte_count=1, is_signed=False),
# Derived types
- # Specification describes them in section '7.18.2. Derived Data Types'
+ # Specification describes them in section '7.19.2. Derived Data Types'
"action_id": BasicInteger(idl_name="action_id", byte_count=1, is_signed=False),
"attrib_id": BasicInteger(idl_name="attrib_id", byte_count=4, is_signed=False),
"cluster_id": BasicInteger(idl_name="cluster_id", byte_count=4, is_signed=False),
@@ -213,7 +214,8 @@
"percent100ths": BasicInteger(idl_name="percent100ths", byte_count=2, is_signed=False),
"posix_ms": BasicInteger(idl_name="posix_ms", byte_count=8, is_signed=False),
"priority": BasicInteger(idl_name="priority", byte_count=1, is_signed=False),
- "status": BasicInteger(idl_name="status", byte_count=2, is_signed=False),
+ "semtag": BasicInteger(idl_name="semtag", byte_count=4, is_signed=False),
+ "status": BasicInteger(idl_name="status", byte_count=1, is_signed=False),
"systime_ms": BasicInteger(idl_name="systime_ms", byte_count=8, is_signed=False),
"systime_us": BasicInteger(idl_name="systime_us", byte_count=8, is_signed=False),
"tag": BasicInteger(idl_name="tag", byte_count=1, is_signed=False),
@@ -334,7 +336,7 @@
Handles both standard names (like enum8) as well as enumerations defined
within the current lookup context.
"""
- if name.lower() in ["enum8", "enum16", "enum32"]:
+ if name.lower() in ["enum8", "enum16", "enum24", "enum32"]:
return True
return any(map(lambda e: e.name == name, self.all_enums))
@@ -384,9 +386,9 @@
return BasicString(idl_name=lowercase_name, is_binary=False, max_length=data_type.max_length)
elif lowercase_name in ['octet_string', 'long_octet_string']:
return BasicString(idl_name=lowercase_name, is_binary=True, max_length=data_type.max_length)
- elif lowercase_name in ['enum8', 'enum16', 'enum32']:
+ elif lowercase_name in ['enum8', 'enum16', 'enum24', 'enum32']:
return IdlEnumType(idl_name=lowercase_name, base_type=__CHIP_SIZED_TYPES__[lowercase_name])
- elif lowercase_name in ['bitmap8', 'bitmap16', 'bitmap24', 'bitmap32']:
+ elif lowercase_name in ['bitmap8', 'bitmap16', 'bitmap24', 'bitmap32', 'bitmap64']:
return IdlBitmapType(idl_name=lowercase_name, base_type=__CHIP_SIZED_TYPES__[lowercase_name])
int_type = __CHIP_SIZED_TYPES__.get(lowercase_name, None)
diff --git a/scripts/py_matter_idl/matter_idl/test_supported_types.py b/scripts/py_matter_idl/matter_idl/test_supported_types.py
new file mode 100755
index 0000000..62f7d4c
--- /dev/null
+++ b/scripts/py_matter_idl/matter_idl/test_supported_types.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# Copyright (c) 2023 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import unittest
+import xml.etree.ElementTree as ET
+
+try:
+ from matter_idl.generators.type_definitions import ParseDataType
+except ImportError:
+ import sys
+
+ sys.path.append(os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..')))
+ from matter_idl.generators.type_definitions import ParseDataType
+
+from matter_idl.generators.type_definitions import BasicInteger, TypeLookupContext
+from matter_idl.matter_idl_types import DataType, Idl
+
+
+class TestSupportedTypes(unittest.TestCase):
+
+ def __init__(self, *args, **kargs):
+ super().__init__(*args, **kargs)
+ self.maxDiff = None
+
+ def testAllTypesSupported(self):
+ # ALL types defined in chip-types.xml should be understandable
+ # by the generator type parsing
+ path = "src/app/zap-templates/zcl/data-model/chip/chip-types.xml"
+ path = os.path.join(os.path.dirname(__file__), "../../..", path)
+ dom = ET.parse(path).getroot()
+
+ # Format we expect:
+ # - configurator/atomic/type
+ self.assertEqual(dom.tag, "configurator")
+ types = dom.findall("./atomic/type")
+
+ # Arbitrary non-empty assumption to make sure we
+ # test something and did not mess up our XPath query
+ self.assertTrue(len(types) > 10)
+
+ empty_lookup = TypeLookupContext(idl=Idl(), cluster=None)
+
+ for t in types:
+ # every type has the following intersting attributes:
+ # - name (to be validated)
+ # - size (in bytes, but may not be power of two)
+ # - one of discrete/analog/composite
+
+ if "composite" in t.attrib and t.attrib["composite"] == "true":
+ # struct, array, octet_string and such
+ continue
+
+ data_type = DataType(name=t.attrib["name"])
+
+ # filter some know things
+ if data_type.name in {
+ "no_data", # intentionally skipped
+ # handled as a non-integer type
+ "boolean", "single", "double",
+ # handled as specific bitmaps
+ "bitmap8", "bitmap16", "bitmap24", "bitmap32", "bitmap64",
+ # handled as specific enums
+ "enum8", "enum16", "enum24", "enum32",
+
+ # TODO: these may be bugs to fix
+ "unknown"
+ }:
+ continue
+
+ parsed = ParseDataType(data_type, empty_lookup)
+
+ self.assertTrue(parsed is not None) # this should always pass.
+ fail_message = f"{data_type.name} was parsed as {parsed}"
+
+ self.assertIs(type(parsed), BasicInteger, fail_message)
+
+ # check that types match
+ if "signed" in t.attrib and t.attrib["signed"] == "true":
+ # Oddly enough, we have no sign info for int8s and int8u
+ # Only temperature really has a signed component
+ self.assertTrue(parsed.is_signed, fail_message)
+
+ if "size" in t.attrib:
+ self.assertEqual(parsed.byte_count, int(
+ t.attrib["size"]), fail_message)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/app/zap-templates/zcl/data-model/chip/chip-types.xml b/src/app/zap-templates/zcl/data-model/chip/chip-types.xml
index 0756fd8..a9466ff 100644
--- a/src/app/zap-templates/zcl/data-model/chip/chip-types.xml
+++ b/src/app/zap-templates/zcl/data-model/chip/chip-types.xml
@@ -102,6 +102,7 @@
<type id="0xF5" description="IPv6 Prefix" name="ipv6pre" composite="true"/>
<type id="0xF6" description="Hardware Address" name="hwadr" composite="true"/>
+ <!-- TODO: Spec marks these as "compositte" yet they are set "discrete" here -->
<type id="0xC9" description="Semantic Tag" name="semtag" size="4" discrete="true" />
<type id="0xCA" description="Namespace" name="namespace" size="1" discrete="true" />
<type id="0xCB" description="Tag" name="tag" size="1" discrete="true" />
diff --git a/src/controller/java/generated/java/matter/devicecontroller/cluster/clusters/ScenesCluster.kt b/src/controller/java/generated/java/matter/devicecontroller/cluster/clusters/ScenesCluster.kt
index 4baf50c..0a19851 100644
--- a/src/controller/java/generated/java/matter/devicecontroller/cluster/clusters/ScenesCluster.kt
+++ b/src/controller/java/generated/java/matter/devicecontroller/cluster/clusters/ScenesCluster.kt
@@ -21,10 +21,10 @@
import matter.devicecontroller.cluster.structs.*
class ScenesCluster(private val controller: MatterController, private val endpointId: UShort) {
- class AddSceneResponse(val status: UShort, val groupID: UShort, val sceneID: UByte)
+ class AddSceneResponse(val status: UByte, val groupID: UShort, val sceneID: UByte)
class ViewSceneResponse(
- val status: UShort,
+ val status: UByte,
val groupID: UShort,
val sceneID: UByte,
val transitionTime: UShort?,
@@ -32,23 +32,23 @@
val extensionFieldSets: List<ScenesClusterExtensionFieldSet>?
)
- class RemoveSceneResponse(val status: UShort, val groupID: UShort, val sceneID: UByte)
+ class RemoveSceneResponse(val status: UByte, val groupID: UShort, val sceneID: UByte)
- class RemoveAllScenesResponse(val status: UShort, val groupID: UShort)
+ class RemoveAllScenesResponse(val status: UByte, val groupID: UShort)
- class StoreSceneResponse(val status: UShort, val groupID: UShort, val sceneID: UByte)
+ class StoreSceneResponse(val status: UByte, val groupID: UShort, val sceneID: UByte)
class GetSceneMembershipResponse(
- val status: UShort,
+ val status: UByte,
val capacity: UByte?,
val groupID: UShort,
val sceneList: List<UByte>?
)
- class EnhancedAddSceneResponse(val status: UShort, val groupID: UShort, val sceneID: UByte)
+ class EnhancedAddSceneResponse(val status: UByte, val groupID: UShort, val sceneID: UByte)
class EnhancedViewSceneResponse(
- val status: UShort,
+ val status: UByte,
val groupID: UShort,
val sceneID: UByte,
val transitionTime: UShort?,
@@ -57,7 +57,7 @@
)
class CopySceneResponse(
- val status: UShort,
+ val status: UByte,
val groupIdentifierFrom: UShort,
val sceneIdentifierFrom: UByte
)