Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a test that validates python type parsing matches to chip-types.xml #30711

Merged
merged 10 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/py_matter_idl/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pw_python_package("matter_idl") {
"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",
]

Expand Down
12 changes: 7 additions & 5 deletions scripts/py_matter_idl/matter_idl/generators/type_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def is_struct(self) -> bool:
"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),
Expand All @@ -188,7 +189,7 @@ def is_struct(self) -> bool:
"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),
Expand All @@ -213,7 +214,8 @@ def is_struct(self) -> bool:
"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),
Expand Down Expand Up @@ -334,7 +336,7 @@ def is_enum_type(self, name: str):
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))

Expand Down Expand Up @@ -384,9 +386,9 @@ def ParseDataType(data_type: DataType, lookup: TypeLookupContext) -> Union[Basic
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)
Expand Down
103 changes: 103 additions & 0 deletions scripts/py_matter_idl/matter_idl/test_supported_types.py
Original file line number Diff line number Diff line change
@@ -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()
1 change: 1 addition & 0 deletions src/app/zap-templates/zcl/data-model/chip/chip-types.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ limitations under the License.
<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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,34 @@ import matter.controller.MatterController
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?,
val sceneName: String?,
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?,
Expand All @@ -57,7 +57,7 @@ class ScenesCluster(private val controller: MatterController, private val endpoi
)

class CopySceneResponse(
val status: UShort,
val status: UByte,
val groupIdentifierFrom: UShort,
val sceneIdentifierFrom: UByte
)
Expand Down
Loading