Files
Mahesh Pimpale 42075d5c75 components/esp_matter: generated data model using automated script
- data_model/legacy/: moved old data model to this folder
- data_model/generated/: contain the automatically generated data model
- tools/data_model_gen: contains the script to generate the data model
2026-04-15 17:05:50 +05:30

428 lines
15 KiB
Python

# Copyright 2026 Espressif Systems (Shanghai) PTE LTD
#
# 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.
"""
Tests for code_generation/elements.py — Cluster, Attribute, Command, Event, Feature, Device
used during Jinja template rendering.
"""
import unittest
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from code_generation.elements import ( # noqa: E402
Cluster,
Attribute,
Command,
Event,
Feature,
Device,
get_choice_group,
get_id_name_lambda,
)
from code_generation.conformance_codegen import ConformanceDecision # noqa: E402
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_attr(name, id_, type_="uint8", mandatory=True, default="0"):
a = Attribute(
name=name, id=id_, type_=type_, is_mandatory=mandatory, default_value=default
)
a.converted_type = "uint8_t"
a._flag = "ATTRIBUTE_FLAG_NONE"
return a
def _make_cmd(name, id_, mandatory=True, direction="commandToServer", response="Y"):
c = Command(
name=name,
id=id_,
is_mandatory=mandatory,
direction=direction,
response=response,
)
c._flag = "COMMAND_FLAG_ACCEPTED"
c.has_callback = True
return c
def _make_event(name, id_, mandatory=True):
return Event(name=name, id=id_, is_mandatory=mandatory)
def _make_feature(name, id_, code="XX", mandatory=False, conformance=None):
f = Feature(name=name, id=id_, code=code, is_mandatory=mandatory)
if conformance:
f.conformance = conformance
return f
def _make_cluster(name="TestCluster", id_="0x0001", revision=1):
return Cluster(name=name, id=id_, revision=revision, is_mandatory=True)
# ---------------------------------------------------------------------------
# Attribute (codegen)
# ---------------------------------------------------------------------------
class TestCodegenAttribute(unittest.TestCase):
"""Test code_generation.elements.Attribute."""
def test_basic_creation(self):
a = _make_attr("Temp", "0x0001")
self.assertEqual(a.get_id(), "0x0001")
self.assertTrue(a.is_mandatory)
def test_get_flag(self):
a = _make_attr("Temp", "0x0001")
a._flag = "ATTRIBUTE_FLAG_WRITABLE | ATTRIBUTE_FLAG_NULLABLE"
self.assertIn("WRITABLE", a.get_flag())
def test_get_type(self):
a = _make_attr("Temp", "0x0001")
a.converted_type = "int16_t"
self.assertEqual(a.get_type(), "int16_t")
def test_get_default_value(self):
a = _make_attr("Temp", "0x0001", default="42")
self.assertEqual(a.get_default_value(), "42")
def test_get_default_value_type_small(self):
a = _make_attr("Temp", "0x0001", default="10")
self.assertEqual(a.get_default_value_type(), "uint8_t")
def test_get_default_value_type_medium(self):
a = _make_attr("Temp", "0x0001", default="1000")
self.assertEqual(a.get_default_value_type(), "uint16_t")
def test_get_default_value_type_large(self):
a = _make_attr("Temp", "0x0001", default="100000")
self.assertEqual(a.get_default_value_type(), "uint32_t")
def test_get_default_value_type_non_numeric(self):
a = _make_attr("Temp", "0x0001", default="abc")
self.assertEqual(a.get_default_value_type(), "uint32_t")
def test_min_max_value(self):
a = _make_attr("Temp", "0x0001")
a.min_value = -100
a.max_value = 200
self.assertEqual(a.get_min_value(), -100)
self.assertEqual(a.get_max_value(), 200)
def test_max_value_falls_back_to_default(self):
a = _make_attr("Temp", "0x0001", default="50")
a.max_value = None
self.assertEqual(a.get_max_value(), "50")
def test_conformance_condition_default_none(self):
a = _make_attr("Temp", "0x0001")
self.assertIsNone(a.get_conformance_condition())
def test_is_internally_managed_default(self):
a = _make_attr("Temp", "0x0001")
self.assertFalse(a.is_internally_managed)
def test_is_complex_default(self):
a = _make_attr("Temp", "0x0001")
self.assertFalse(a.is_complex)
# ---------------------------------------------------------------------------
# Command (codegen)
# ---------------------------------------------------------------------------
class TestCodegenCommand(unittest.TestCase):
"""Test code_generation.elements.Command."""
def test_basic(self):
c = _make_cmd("Off", "0x0001")
self.assertEqual(c.get_flag(), "COMMAND_FLAG_ACCEPTED")
self.assertTrue(c.has_callback)
def test_conformance_condition_default(self):
c = _make_cmd("Off", "0x0001")
self.assertIsNone(c.get_conformance_condition())
def test_fabric_scoped_default(self):
c = _make_cmd("Off", "0x0001")
self.assertFalse(c.is_fabric_scoped)
# ---------------------------------------------------------------------------
# Event (codegen)
# ---------------------------------------------------------------------------
class TestCodegenEvent(unittest.TestCase):
"""Test code_generation.elements.Event."""
def test_basic(self):
e = _make_event("StateChange", "0x0001")
self.assertEqual(e.priority, "Info")
def test_conformance_condition_default(self):
e = _make_event("StateChange", "0x0001")
self.assertIsNone(e.get_conformance_condition())
# ---------------------------------------------------------------------------
# Feature (codegen)
# ---------------------------------------------------------------------------
class TestCodegenFeature(unittest.TestCase):
"""Test code_generation.elements.Feature."""
def test_basic(self):
f = _make_feature("Lighting", "0x0001", code="LT")
self.assertEqual(f.code, "LT")
def test_get_attributes_sorted(self):
f = _make_feature("Lighting", "0x0001", code="LT")
f.attributes = [_make_attr("B", "0x0002"), _make_attr("A", "0x0001")]
attrs = f.get_attributes()
self.assertEqual(attrs[0].get_id(), "0x0001")
def test_get_externally_managed_attributes(self):
f = _make_feature("Lighting", "0x0001", code="LT")
a1 = _make_attr("Ext", "0x0001")
a1.is_internally_managed = False
a2 = _make_attr("Int", "0x0002")
a2.is_internally_managed = True
f.attributes = [a1, a2]
ext = f.get_externally_managed_attributes()
self.assertEqual(len(ext), 1)
self.assertEqual(ext[0].get_id(), "0x0001")
def test_get_commands_sorted(self):
f = _make_feature("Lighting", "0x0001", code="LT")
f.commands = [_make_cmd("B", "0x0002"), _make_cmd("A", "0x0001")]
cmds = f.get_commands()
self.assertEqual(cmds[0].get_id(), "0x0001")
def test_get_events_sorted(self):
f = _make_feature("Lighting", "0x0001", code="LT")
f.events = [_make_event("B", "0x0002"), _make_event("A", "0x0001")]
evts = f.get_events()
self.assertEqual(evts[0].get_id(), "0x0001")
# ---------------------------------------------------------------------------
# Cluster (codegen)
# ---------------------------------------------------------------------------
class TestCodegenCluster(unittest.TestCase):
"""Test code_generation.elements.Cluster — sorting, mandatory, choice groups."""
def test_get_attributes_sorted(self):
c = _make_cluster()
c.attributes = [_make_attr("B", "0x0002"), _make_attr("A", "0x0001")]
self.assertEqual(c.get_attributes()[0].get_id(), "0x0001")
def test_get_commands_sorted(self):
c = _make_cluster()
c.commands = [_make_cmd("B", "0x0002"), _make_cmd("A", "0x0001")]
self.assertEqual(c.get_commands()[0].get_id(), "0x0001")
def test_get_events_sorted(self):
c = _make_cluster()
c.events = [_make_event("B", "0x0002"), _make_event("A", "0x0001")]
self.assertEqual(c.get_events()[0].get_id(), "0x0001")
def test_get_features_sorted(self):
c = _make_cluster()
c.features = [
_make_feature("B", "0x0002", code="BB"),
_make_feature("A", "0x0001", code="AA"),
]
self.assertEqual(c.get_features()[0].get_id(), "0x0001")
def test_get_mandatory_attributes(self):
c = _make_cluster()
a1 = _make_attr("M", "0x0001", mandatory=True)
a2 = _make_attr("O", "0x0002", mandatory=False)
c.attributes = [a1, a2]
mandatory = c.get_mandatory_attributes()
self.assertEqual(len(mandatory), 1)
self.assertEqual(mandatory[0].get_id(), "0x0001")
def test_get_mandatory_commands(self):
c = _make_cluster()
c1 = _make_cmd("M", "0x0001", mandatory=True)
c2 = _make_cmd("O", "0x0002", mandatory=False)
c.commands = [c1, c2]
mandatory = c.get_mandatory_commands()
self.assertEqual(len(mandatory), 1)
def test_get_mandatory_events(self):
c = _make_cluster()
e1 = _make_event("M", "0x0001", mandatory=True)
e2 = _make_event("O", "0x0002", mandatory=False)
c.events = [e1, e2]
mandatory = c.get_mandatory_events()
self.assertEqual(len(mandatory), 1)
def test_has_choice_features_false(self):
c = _make_cluster()
self.assertFalse(c.has_choice_features())
def test_get_cluster_init_callback(self):
c = _make_cluster(name="OnOff")
cb = c.get_cluster_init_callback()
self.assertIn("OnOff", cb)
self.assertIn("Init", cb)
def test_get_cluster_shutdown_callback(self):
c = _make_cluster(name="OnOff")
cb = c.get_cluster_shutdown_callback()
self.assertIn("OnOff", cb)
self.assertIn("Shutdown", cb)
def test_get_response_command(self):
c = _make_cluster()
cmd = _make_cmd("GetResponse", "0x0001")
c.commands = [cmd]
self.assertEqual(c.get_response_command("GetResponse"), cmd)
self.assertIsNone(c.get_response_command("Unknown"))
def test_get_destroyable_elements_empty(self):
c = _make_cluster()
c.attributes = [_make_attr("A", "0x0001")]
result = c.get_destroyable_elements("lighting")
self.assertEqual(result["attributes"], [])
self.assertEqual(result["commands"], [])
self.assertEqual(result["events"], [])
def test_get_independent_features_all_independent(self):
c = _make_cluster()
f1 = _make_feature("A", "0x0001", code="AA")
f2 = _make_feature("B", "0x0002", code="BB")
c.features = [f1, f2]
independent = c.get_independent_features()
self.assertEqual(len(independent), 2)
def test_standalone_choice_groups_empty(self):
c = _make_cluster()
self.assertEqual(c.get_standalone_choice_groups(), [])
# ---------------------------------------------------------------------------
# Device (codegen)
# ---------------------------------------------------------------------------
class TestCodegenDevice(unittest.TestCase):
"""Test code_generation.elements.Device."""
def test_basic(self):
d = Device(id="0x0100", name="On/Off Light", revision=3)
self.assertEqual(d.get_device_type_id(), "0x0100")
self.assertEqual(d.get_device_type_version(), 3)
def test_get_clusters_sorted(self):
d = Device(id="0x0100", name="Test", revision=1)
c1 = _make_cluster("B", "0x0006")
c1.server_cluster = True
c2 = _make_cluster("A", "0x0003")
c2.server_cluster = True
d.clusters = [c1, c2]
self.assertEqual(d.get_clusters()[0].get_id(), "0x0003")
def test_get_mandatory_clusters(self):
d = Device(id="0x0100", name="Test", revision=1)
c1 = _make_cluster("M", "0x0001")
c1.is_mandatory = True
c2 = Cluster(name="O", id="0x0002", revision=1, is_mandatory=False)
d.clusters = [c1, c2]
self.assertEqual(len(d.get_mandatory_clusters()), 1)
def test_get_unique_clusters_deduplicates(self):
d = Device(id="0x0100", name="Test", revision=1)
c1 = _make_cluster("Same", "0x0006")
c1.server_cluster = True
c2 = _make_cluster("Same", "0x0006")
c2.server_cluster = False
d.clusters = [c1, c2]
unique = d.get_unique_clusters()
self.assertEqual(len(unique), 1)
def test_binding_cluster_available(self):
d = Device(id="0x0100", name="Test", revision=1)
c = _make_cluster("Test", "0x0006")
c.client_cluster = True
c.is_mandatory = True
d.clusters = [c]
self.assertTrue(d.binding_cluster_available())
def test_no_binding_cluster(self):
d = Device(id="0x0100", name="Test", revision=1)
c = _make_cluster("Test", "0x0006")
c.server_cluster = True
c.client_cluster = False
c.is_mandatory = True
d.clusters = [c]
self.assertFalse(d.binding_cluster_available())
def test_filename(self):
d = Device(id="0x0100", name="Temperature Sensor", revision=1)
self.assertTrue(d.filename.endswith("_device"))
# ---------------------------------------------------------------------------
# Deserializer roundtrip helpers
# ---------------------------------------------------------------------------
class TestGetIdNameLambda(unittest.TestCase):
"""Test sorting helper."""
def test_sorts_correctly(self):
items = [
_make_attr("C", "0x0003"),
_make_attr("A", "0x0001"),
_make_attr("B", "0x0002"),
]
sorted_items = sorted(items, key=get_id_name_lambda())
self.assertEqual(
[i.get_id() for i in sorted_items], ["0x0001", "0x0002", "0x0003"]
)
class TestGetChoiceGroup(unittest.TestCase):
"""Test get_choice_group() utility."""
def test_empty_features(self):
result = get_choice_group("mandatory_parent", ConformanceDecision.OTHERWISE, [])
self.assertEqual(result, [])
def test_no_matching_conformance(self):
f = _make_feature("A", "0x0001", code="AA")
result = get_choice_group(
"mandatory_parent", ConformanceDecision.OTHERWISE, [f]
)
self.assertEqual(result, [])
if __name__ == "__main__":
unittest.main()