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

707 lines
26 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.
"""
Comprehensive test suite for conformance parsing based on real XML examples
from connectedhomeip/data_model/1.5/clusters/
"""
import sys
import os
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import unittest # noqa: E402
import xml.etree.ElementTree as ET # noqa: E402
import json # noqa: E402
from xml_processing.conformance_parser import ( # noqa: E402
parse_conformance,
Conformance,
is_restricted_by_conformance,
match_conformance_items,
)
from utils.helper import convert_to_snake_case # noqa: E402
from utils.conformance import ConformanceDecision # noqa: E402
class MockFeature:
"""Mock feature object for testing"""
def __init__(self, name, code):
self.name = name
self.code = code
self.func_name = convert_to_snake_case(name)
class MockItem:
"""Mock item (attribute/command/event) for testing conformance matching"""
def __init__(self, name, conformance=None):
self.name = name
self.conformance = conformance
class TestBasicConformance(unittest.TestCase):
"""Test basic conformance types without conditions"""
def test_simple_mandatory_conformance(self):
"""Test parsing simple <mandatoryConform/> tags"""
xml = "<mandatoryConform/>"
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertIsNotNone(result)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIsNone(result.condition)
def test_simple_optional_conformance(self):
"""Test parsing simple <optionalConform/> tags"""
xml = "<optionalConform/>"
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertIsNotNone(result)
self.assertEqual(result.type, ConformanceDecision.OPTIONAL)
self.assertIsNone(result.condition)
def test_simple_deprecated_conformance(self):
"""Test parsing simple <deprecateConform/> tags"""
xml = "<deprecateConform/>"
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertIsNotNone(result)
self.assertEqual(result.type, ConformanceDecision.DEPRECATED)
def test_simple_disallowed_conformance(self):
"""Test parsing simple <disallowConform/> tags"""
xml = "<disallowConform/>"
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertIsNotNone(result)
self.assertEqual(result.type, ConformanceDecision.DISALLOWED)
def test_simple_provisional_conformance(self):
"""Test parsing simple <provisionalConform/> tags"""
xml = "<provisionalConform/>"
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertIsNotNone(result)
self.assertEqual(result.type, ConformanceDecision.PROVISIONAL)
class TestChoiceConformance(unittest.TestCase):
"""Test choice conformance patterns from OccupancySensing cluster"""
def test_choice_conformance_with_min_and_more(self):
"""Test: <optionalConform choice="a" more="true" min="1"/>
From OccupancySensing features - at least 1 sensing method required"""
xml = '<optionalConform choice="a" more="true" min="1"/>'
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OPTIONAL)
self.assertEqual(result.choice.marker, "a")
self.assertTrue(result.choice.more)
def test_choice_conformance_serialization(self):
"""Test JSON serialization of choice conformance"""
xml = '<optionalConform choice="a" more="true" min="1"/>'
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
dict_result = result.to_dict()
self.assertEqual(dict_result["type"], "optional")
self.assertEqual(dict_result["choice"], "a")
self.assertTrue(dict_result["more"])
self.assertEqual(dict_result["min"], 1)
class TestFeatureBasedConformance(unittest.TestCase):
"""Test feature-based conformance patterns"""
def test_mandatory_with_single_feature(self):
"""Test: <mandatoryConform><feature name="LT"/></mandatoryConform>
From OnOff cluster commands"""
xml = """<mandatoryConform>
<feature name="LT"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {"LT": MockFeature("Lighting", "LT")}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIsNotNone(result.condition)
self.assertEqual(result.condition, {"feature": "lighting"})
def test_mandatory_with_or_features(self):
"""Test: <mandatoryConform><orTerm><feature/><feature/></orTerm></mandatoryConform>
From ColorControl cluster"""
xml = """<mandatoryConform>
<orTerm>
<feature name="HS"/>
<feature name="XY"/>
<feature name="CT"/>
</orTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {
"HS": MockFeature("HueSaturation", "HS"),
"XY": MockFeature("XY", "XY"),
"CT": MockFeature("ColorTemperature", "CT"),
}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIn("or", result.condition)
self.assertEqual(len(result.condition["or"]), 3)
def test_optional_with_not_feature(self):
"""Test: <optionalConform><notTerm><feature name="OFFONLY"/></notTerm></optionalConform>
From OnOff cluster - LT feature"""
xml = """<optionalConform>
<notTerm>
<feature name="OFFONLY"/>
</notTerm>
</optionalConform>"""
elem = ET.fromstring(xml)
feature_map = {"OFFONLY": MockFeature("OffOnly", "OFFONLY")}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OPTIONAL)
self.assertIn("not", result.condition)
self.assertEqual(result.condition["not"]["feature"], "off_only")
def test_nested_not_or_features(self):
"""Test: <optionalConform><notTerm><orTerm><feature/><feature/></orTerm></notTerm></optionalConform>
From OnOff cluster - OFFONLY feature"""
xml = """<optionalConform>
<notTerm>
<orTerm>
<feature name="LT"/>
<feature name="DF"/>
</orTerm>
</notTerm>
</optionalConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"DF": MockFeature("DeadFrontBehavior", "DF"),
}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OPTIONAL)
self.assertIn("not", result.condition)
self.assertIn("or", result.condition["not"])
def test_mandatory_with_not_feature(self):
"""Test: <mandatoryConform><notTerm><feature name="OFFONLY"/></notTerm></mandatoryConform>
From OnOff cluster - On/Toggle commands"""
xml = """<mandatoryConform>
<notTerm>
<feature name="OFFONLY"/>
</notTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {"OFFONLY": MockFeature("OffOnly", "OFFONLY")}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIn("not", result.condition)
class TestAttributeCommandConformance(unittest.TestCase):
"""Test conformance based on attributes and commands"""
def test_attribute_reference(self):
"""Test: <mandatoryConform><attribute name="SomeAttribute"/></mandatoryConform>"""
xml = """<mandatoryConform>
<attribute name="OnOff"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertEqual(result.condition, {"attribute": "OnOff"})
def test_command_reference(self):
"""Test: <mandatoryConform><command name="SomeCommand"/></mandatoryConform>"""
xml = """<mandatoryConform>
<command name="Off"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertEqual(result.condition, {"command": "Off"})
def test_attribute_or_command(self):
"""Test OR of attributes/commands"""
xml = """<mandatoryConform>
<orTerm>
<attribute name="OnOff"/>
<command name="Toggle"/>
</orTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIn("or", result.condition)
class TestOtherwiseConformance(unittest.TestCase):
"""Test otherwise conformance patterns"""
def test_otherwise_with_mandatory_and_deprecated(self):
"""Test: <otherwiseConform><mandatoryConform/><deprecateConform/></otherwiseConform>
From OccupancySensing cluster"""
xml = """<otherwiseConform>
<mandatoryConform/>
<deprecateConform/>
</otherwiseConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OTHERWISE)
self.assertIn("mandatory", result.condition)
self.assertIn("deprecated", result.condition)
def test_otherwise_with_provisional_and_mandatory(self):
"""Test: <otherwiseConform><provisionalConform/><mandatoryConform/></otherwiseConform>
From BasicInformation cluster"""
xml = """<otherwiseConform>
<provisionalConform/>
<mandatoryConform/>
</otherwiseConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OTHERWISE)
self.assertIn("provisional", result.condition)
self.assertIn("mandatory", result.condition)
def test_otherwise_with_conditional_mandatory(self):
"""Test otherwise with mandatory that has conditions"""
xml = """<otherwiseConform>
<mandatoryConform>
<greaterTerm>
<attribute name="NumberOfPrimaries"/>
<literal value="0"/>
</greaterTerm>
</mandatoryConform>
<optionalConform/>
</otherwiseConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OTHERWISE)
self.assertIn("mandatory", result.condition)
self.assertIn("optional", result.condition)
def test_otherwise_with_optional_choice(self):
"""Test otherwise with optional that has choice attributes"""
xml = """<otherwiseConform>
<mandatoryConform>
<feature name="AUTO"/>
</mandatoryConform>
<optionalConform choice="a" more="true" min="1"/>
</otherwiseConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"AUTO": MockFeature("Auto", "AUTO"),
}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.OTHERWISE)
self.assertIn("optional", result.condition)
self.assertEqual(result.condition["optional"]["choice"], "a")
self.assertTrue(result.condition["optional"]["more"])
self.assertEqual(result.condition["optional"]["min"], 1)
class TestComplexNestedConformance(unittest.TestCase):
"""Test complex nested conformance structures"""
def test_and_of_features(self):
"""Test AND operation with multiple features"""
xml = """<mandatoryConform>
<andTerm>
<feature name="LT"/>
<feature name="DF"/>
</andTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"DF": MockFeature("DeadFrontBehavior", "DF"),
}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIn("and", result.condition)
self.assertEqual(len(result.condition["and"]), 2)
def test_complex_nested_boolean_operations(self):
"""Test complex nested: AND(OR(...), NOT(...))"""
xml = """<mandatoryConform>
<andTerm>
<orTerm>
<feature name="HS"/>
<feature name="XY"/>
</orTerm>
<notTerm>
<feature name="OFFONLY"/>
</notTerm>
</andTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {
"HS": MockFeature("HueSaturation", "HS"),
"XY": MockFeature("XY", "XY"),
"OFFONLY": MockFeature("OffOnly", "OFFONLY"),
}
result = Conformance(feature_map).parse(elem)
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
self.assertIn("and", result.condition)
# Check that we have both OR and NOT in the AND
and_elements = result.condition["and"]
has_or = any(
"or" in elem if isinstance(elem, dict) else False for elem in and_elements
)
has_not = any(
"not" in elem if isinstance(elem, dict) else False for elem in and_elements
)
self.assertTrue(has_or and has_not)
def test_implicit_and_of_multiple_features(self):
"""Test implicit AND when multiple features at same level"""
xml = """<mandatoryConform>
<feature name="LT"/>
<feature name="DF"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"DF": MockFeature("DeadFrontBehavior", "DF"),
}
result = Conformance(feature_map).parse(elem)
# Multiple features at same level should be implicitly AND-ed
self.assertEqual(result.type, ConformanceDecision.MANDATORY)
if result.condition:
self.assertTrue("and" in result.condition or "feature" in result.condition)
class TestConformanceRestrictions(unittest.TestCase):
"""Test is_restricted_by_conformance function"""
def test_skip_disallowed_conformance(self):
"""Test that disallowed elements are flagged for skipping"""
xml = """<attribute id="0x0001" name="TestAttr">
<disallowConform/>
</attribute>"""
elem = ET.fromstring(xml)
should_skip = is_restricted_by_conformance({}, elem)
self.assertTrue(should_skip)
def test_skip_deprecated_conformance(self):
"""Test that deprecated elements are flagged for skipping"""
xml = """<attribute id="0x0001" name="TestAttr">
<deprecateConform/>
</attribute>"""
elem = ET.fromstring(xml)
should_skip = is_restricted_by_conformance({}, elem)
self.assertTrue(should_skip)
def test_skip_provisional_conformance(self):
"""Test that provisional elements are flagged for skipping"""
xml = """<attribute id="0x0001" name="TestAttr">
<provisionalConform/>
</attribute>"""
elem = ET.fromstring(xml)
should_skip = is_restricted_by_conformance({}, elem)
self.assertTrue(should_skip)
def test_skip_otherwise_with_provisional_first(self):
"""Test that otherwise conformance with provisional first is skipped"""
xml = """<attribute id="0x0001" name="TestAttr">
<otherwiseConform>
<provisionalConform/>
<mandatoryConform/>
</otherwiseConform>
</attribute>"""
elem = ET.fromstring(xml)
should_skip = is_restricted_by_conformance({}, elem)
self.assertTrue(should_skip)
def test_skip_missing_feature_in_mandatory(self):
"""Test skipping when mandatory conformance references non-existent feature"""
xml = """<attribute id="0x0001" name="TestAttr">
<otherwiseConform>
<mandatoryConform>
<feature name="NONEXISTENT"/>
</mandatoryConform>
<optionalConform/>
</otherwiseConform>
</attribute>"""
elem = ET.fromstring(xml)
feature_map = {"LT": MockFeature("Lighting", "LT")} # NONEXISTENT not in map
should_skip = is_restricted_by_conformance(feature_map, elem)
self.assertTrue(should_skip)
def test_dont_skip_valid_mandatory(self):
"""Test that valid mandatory conformance is not skipped"""
xml = """<attribute id="0x0001" name="TestAttr">
<mandatoryConform/>
</attribute>"""
elem = ET.fromstring(xml)
should_skip = is_restricted_by_conformance({}, elem)
self.assertFalse(should_skip)
def test_skip_zigbee_specific(self):
"""Test that Zigbee-specific optional conformance is skipped"""
xml = """<attribute id="0x0001" name="TestAttr">
<optionalConform>
<condition name="Zigbee"/>
</optionalConform>
</attribute>"""
elem = ET.fromstring(xml)
should_skip = is_restricted_by_conformance({}, elem)
self.assertTrue(should_skip)
class TestConformanceSerialization(unittest.TestCase):
"""Test JSON serialization of conformance objects"""
def test_to_dict_basic(self):
"""Test basic to_dict conversion"""
xml = "<mandatoryConform/>"
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
dict_result = result.to_dict()
self.assertEqual(dict_result["type"], "mandatory")
def test_to_dict_with_condition(self):
"""Test to_dict with conditions"""
xml = """<mandatoryConform>
<feature name="LT"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {"LT": MockFeature("Lighting", "LT")}
result = Conformance(feature_map).parse(elem)
dict_result = result.to_dict()
self.assertIn("condition", dict_result)
self.assertIsNotNone(dict_result["condition"])
def test_to_dict_with_attribute_map(self):
"""Test to_dict with attribute name to ID mapping"""
xml = """<mandatoryConform>
<orTerm>
<attribute name="OnOff"/>
<attribute name="LevelControl"/>
</orTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
# Without attribute map
dict_without_map = result.to_dict()
self.assertEqual(dict_without_map["condition"]["or"][0]["attribute"], "OnOff")
# With attribute map - should replace names with IDs
attribute_map = {"OnOff": "OnOff", "LevelControl": "LevelControl"}
dict_with_map = result.to_dict(attribute_map)
self.assertEqual(dict_with_map["condition"]["or"][0]["attribute"], "OnOff")
self.assertEqual(
dict_with_map["condition"]["or"][1]["attribute"], "LevelControl"
)
def test_to_dict_with_command_map(self):
"""Test to_dict with command name to ID/flag mapping"""
xml = """<mandatoryConform>
<command name="Off"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
result = Conformance({}).parse(elem)
# With command map (stored as tuple with flag)
command_map = {"Off": ("Off", "COMMAND_FLAG_ACCEPTED")}
dict_with_map = result.to_dict(command_map)
self.assertEqual(dict_with_map["condition"]["command"], "Off")
self.assertEqual(dict_with_map["condition"]["flag"], "COMMAND_FLAG_ACCEPTED")
def test_to_dict_json_serializable(self):
"""Test that to_dict output is JSON serializable"""
xml = """<optionalConform choice="a" more="true" min="1">
<andTerm>
<feature name="LT"/>
<feature name="DF"/>
</andTerm>
</optionalConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"DF": MockFeature("DeadFrontBehavior", "DF"),
}
result = Conformance(feature_map).parse(elem)
dict_result = result.to_dict()
# Should not raise exception
json_str = json.dumps(dict_result)
self.assertIsNotNone(json_str)
class TestMatchConformanceItems(unittest.TestCase):
"""Test matching items with feature conformance"""
def test_match_mandatory_feature(self):
"""Test matching items with mandatory feature conformance"""
# Create a feature
feature = MockFeature("Lighting", "LT")
# Create conformance that requires this feature
xml = """<mandatoryConform>
<feature name="LT"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {"LT": feature}
conformance = Conformance(feature_map).parse(elem)
# Create items with this conformance
item1 = MockItem("OnCommand", conformance)
item2 = MockItem("OffCommand", None)
items = [item1, item2]
matched = match_conformance_items(feature, items)
self.assertEqual(len(matched), 1)
self.assertEqual(matched[0].name, "OnCommand")
def test_match_otherwise_mandatory_feature(self):
"""Test matching items with otherwise conformance containing mandatory feature"""
feature = MockFeature("Lighting", "LT")
# Create otherwise conformance
xml = """<otherwiseConform>
<mandatoryConform>
<feature name="LT"/>
</mandatoryConform>
<optionalConform/>
</otherwiseConform>"""
elem = ET.fromstring(xml)
feature_map = {"LT": feature}
conformance = Conformance(feature_map).parse(elem)
item = MockItem("LightingCommand", conformance)
items = [item]
matched = match_conformance_items(feature, items)
# Should match if otherwise has mandatory with this feature
# Note: Current implementation checks for condition.get("mandatory")
self.assertGreaterEqual(len(matched), 0)
class TestConformanceHasFeature(unittest.TestCase):
"""Test has_feature method on Conformance objects"""
def test_has_feature_simple(self):
"""Test has_feature with simple feature conformance"""
xml = """<mandatoryConform>
<feature name="LT"/>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature = MockFeature("Lighting", "LT")
feature_map = {"LT": feature}
conformance = Conformance(feature_map).parse(elem)
self.assertTrue(conformance.has_feature("LT"))
self.assertFalse(conformance.has_feature("DF"))
def test_has_feature_in_or(self):
"""Test has_feature when feature is in OR term"""
xml = """<mandatoryConform>
<orTerm>
<feature name="LT"/>
<feature name="DF"/>
</orTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"DF": MockFeature("DeadFrontBehavior", "DF"),
}
conformance = Conformance(feature_map).parse(elem)
self.assertTrue(conformance.has_feature("LT"))
self.assertTrue(conformance.has_feature("DF"))
def test_has_feature_in_and(self):
"""Test has_feature when feature is in AND term"""
xml = """<mandatoryConform>
<andTerm>
<feature name="LT"/>
<feature name="DF"/>
</andTerm>
</mandatoryConform>"""
elem = ET.fromstring(xml)
feature_map = {
"LT": MockFeature("Lighting", "LT"),
"DF": MockFeature("DeadFrontBehavior", "DF"),
}
conformance = Conformance(feature_map).parse(elem)
self.assertTrue(conformance.has_feature("LT"))
self.assertTrue(conformance.has_feature("DF"))
class TestConformanceParsing(unittest.TestCase):
"""Test parsing of conformance elements"""
def test_parse_mandatory_conformance(self):
"""Test parsing of mandatory conformance"""
xml = """<attribute id="0x0002" name="Occupancy" type="OccupancyBitmap">
<access read="true" readPrivilege="view"/>
<mandatoryConform>
<feature name="OCC"/>
</mandatoryConform>
</attribute>"""
elem = ET.fromstring(xml)
feature_map = {"OCC": MockFeature("Occupancy", "OCC")}
conformance = parse_conformance(elem, feature_map)
self.assertEqual(conformance.type, ConformanceDecision.MANDATORY)
self.assertEqual(conformance.condition, {"feature": "occupancy"})
def run_tests():
"""Run all tests with verbose output"""
loader = unittest.TestLoader()
suite = loader.loadTestsFromModule(__import__(__name__))
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
return result.wasSuccessful()
if __name__ == "__main__":
import sys
sys.exit(0 if run_tests() else 1)