blob: 1405e5c8c791cc848493f942de270dc0c677a646 [file] [log] [blame]
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# 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 xml.etree.ElementTree as ElementTree
from typing import Callable
from conformance_support import (ConformanceDecision, ConformanceException, ConformanceParseParameters, deprecated, disallowed,
mandatory, optional, parse_basic_callable_from_xml, parse_callable_from_xml,
parse_device_type_callable_from_xml, provisional, zigbee)
from matter_testing_support import MatterBaseTest, default_matter_test_main
from mobly import asserts
def basic_test(xml: str, cls: Callable) -> None:
et = ElementTree.fromstring(xml)
xml_callable = parse_basic_callable_from_xml(et)
asserts.assert_true(isinstance(xml_callable, cls), "Unexpected class parsed from basic conformance")
class TestConformanceSupport(MatterBaseTest):
def setup_class(self):
super().setup_class()
# a small feature map
self.feature_names_to_bits = {'AB': 0x01, 'CD': 0x02}
# none, AB, CD, AB&CD
self.feature_maps = [0x00, 0x01, 0x02, 0x03]
self.has_ab = [False, True, False, True]
self.has_cd = [False, False, True, True]
self.attribute_names_to_values = {'attr1': 0x00, 'attr2': 0x01}
self.attribute_lists = [[], [0x00], [0x01], [0x00, 0x01]]
self.has_attr1 = [False, True, False, True]
self.has_attr2 = [False, False, True, True]
self.command_names_to_values = {'cmd1': 0x00, 'cmd2': 0x01}
self.cmd_lists = [[], [0x00], [0x01], [0x00, 0x01]]
self.has_cmd1 = [False, True, False, True]
self.has_cmd2 = [False, False, True, True]
self.params = ConformanceParseParameters(
feature_map=self.feature_names_to_bits, attribute_map=self.attribute_names_to_values, command_map=self.command_names_to_values)
def test_conformance_mandatory(self):
xml = '<mandatoryConform />'
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for f in self.feature_maps:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
asserts.assert_equal(str(xml_callable), 'M')
def test_conformance_optional(self):
xml = '<optionalConform />'
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for f in self.feature_maps:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
asserts.assert_equal(str(xml_callable), 'O')
def test_conformance_disallowed(self):
xml = '<disallowConform />'
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for f in self.feature_maps:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.DISALLOWED)
asserts.assert_equal(str(xml_callable), 'X')
xml = '<deprecateConform />'
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for f in self.feature_maps:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.DISALLOWED)
asserts.assert_equal(str(xml_callable), 'D')
def test_conformance_provisional(self):
xml = '<provisionalConform />'
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for f in self.feature_maps:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.PROVISIONAL)
asserts.assert_equal(str(xml_callable), 'P')
def test_conformance_zigbee(self):
xml = '<condition name="Zigbee"/>'
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for f in self.feature_maps:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'Zigbee')
def test_conformance_mandatory_on_condition(self):
xml = ('<mandatoryConform>'
'<feature name="AB" />'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB')
xml = ('<mandatoryConform>'
'<feature name="CD" />'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'CD')
# single attribute mandatory
xml = ('<mandatoryConform>'
'<attribute name="attr1" />'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if self.has_attr1[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'attr1')
xml = ('<mandatoryConform>'
'<attribute name="attr2" />'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if self.has_attr2[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'attr2')
# test command in optional and in boolean - this is the same as attribute essentially, so testing every permutation is overkill
def test_conformance_optional_on_condition(self):
# single feature optional
xml = ('<optionalConform>'
'<feature name="AB" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[AB]')
xml = ('<optionalConform>'
'<feature name="CD" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[CD]')
# single attribute optional
xml = ('<optionalConform>'
'<attribute name="attr1" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if self.has_attr1[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[attr1]')
xml = ('<optionalConform>'
'<attribute name="attr2" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if self.has_attr2[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[attr2]')
# single command optional
xml = ('<optionalConform>'
'<command name="cmd1" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, c in enumerate(self.cmd_lists):
if self.has_cmd1[i]:
asserts.assert_equal(xml_callable(0x00, [], c), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(0x00, [], c), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[cmd1]')
xml = ('<optionalConform>'
'<command name="cmd2" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, c in enumerate(self.cmd_lists):
if self.has_cmd2[i]:
asserts.assert_equal(xml_callable(0x00, [], c), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(0x00, [], c), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[cmd2]')
def test_conformance_not_term_mandatory(self):
# single feature not mandatory
xml = ('<mandatoryConform>'
'<notTerm>'
'<feature name="AB" />'
'</notTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if not self.has_ab[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '!AB')
xml = ('<mandatoryConform>'
'<notTerm>'
'<feature name="CD" />'
'</notTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if not self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '!CD')
# single attribute not mandatory
xml = ('<mandatoryConform>'
'<notTerm>'
'<attribute name="attr1" />'
'</notTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if not self.has_attr1[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '!attr1')
xml = ('<mandatoryConform>'
'<notTerm>'
'<attribute name="attr2" />'
'</notTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if not self.has_attr2[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '!attr2')
def test_conformance_not_term_optional(self):
# single feature not optional
xml = ('<optionalConform>'
'<notTerm>'
'<feature name="AB" />'
'</notTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if not self.has_ab[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[!AB]')
xml = ('<optionalConform>'
'<notTerm>'
'<feature name="CD" />'
'</notTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if not self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[!CD]')
def test_conformance_and_term(self):
# and term for features only
xml = ('<mandatoryConform>'
'<andTerm>'
'<feature name="AB" />'
'<feature name="CD" />'
'</andTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i] and self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB & CD')
# and term for attributes only
xml = ('<mandatoryConform>'
'<andTerm>'
'<attribute name="attr1" />'
'<attribute name="attr2" />'
'</andTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if self.has_attr1[i] and self.has_attr2[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'attr1 & attr2')
# and term for feature and attribute
xml = ('<mandatoryConform>'
'<andTerm>'
'<feature name="AB" />'
'<attribute name="attr2" />'
'</andTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
for j, a in enumerate(self.attribute_lists):
if self.has_ab[i] and self.has_attr2[j]:
asserts.assert_equal(xml_callable(f, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB & attr2')
def test_conformance_or_term(self):
# or term feature only
xml = ('<mandatoryConform>'
'<orTerm>'
'<feature name="AB" />'
'<feature name="CD" />'
'</orTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i] or self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB | CD')
# or term attribute only
xml = ('<mandatoryConform>'
'<orTerm>'
'<attribute name="attr1" />'
'<attribute name="attr2" />'
'</orTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, a in enumerate(self.attribute_lists):
if self.has_attr1[i] or self.has_attr2[i]:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'attr1 | attr2')
# or term feature and attribute
xml = ('<mandatoryConform>'
'<orTerm>'
'<feature name="AB" />'
'<attribute name="attr2" />'
'</orTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
for j, a in enumerate(self.attribute_lists):
if self.has_ab[i] or self.has_attr2[j]:
asserts.assert_equal(xml_callable(f, a, []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, a, []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB | attr2')
def test_conformance_and_term_with_not(self):
# and term with not
xml = ('<optionalConform>'
'<andTerm>'
'<notTerm>'
'<feature name="AB" />'
'</notTerm>'
'<feature name="CD" />'
'</andTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if not self.has_ab[i] and self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[!AB & CD]')
def test_conformance_or_term_with_not(self):
# or term with not on second feature
xml = ('<mandatoryConform>'
'<orTerm>'
'<feature name="AB" />'
'<notTerm>'
'<feature name="CD" />'
'</notTerm>'
'</orTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i] or not self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB | !CD')
# not around or term with
xml = ('<optionalConform>'
'<notTerm>'
'<orTerm>'
'<feature name="AB" />'
'<feature name="CD" />'
'</orTerm>'
'</notTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if not (self.has_ab[i] or self.has_cd[i]):
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[!(AB | CD)]')
def test_conformance_and_term_with_three_terms(self):
# and term with three features
xml = ('<optionalConform>'
'<andTerm>'
'<feature name="AB" />'
'<feature name="CD" />'
'<feature name="EF" />'
'</andTerm>'
'</optionalConform>')
self.feature_names_to_bits['EF'] = 0x04
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
# no features
asserts.assert_equal(xml_callable(0x00, [], []), ConformanceDecision.NOT_APPLICABLE)
# one feature
asserts.assert_equal(xml_callable(0x01, [], []), ConformanceDecision.NOT_APPLICABLE)
# all features
asserts.assert_equal(xml_callable(0x07, [], []), ConformanceDecision.OPTIONAL)
asserts.assert_equal(str(xml_callable), '[AB & CD & EF]')
# and term with one of each
xml = ('<optionalConform>'
'<andTerm>'
'<feature name="AB" />'
'<attribute name="attr1" />'
'<command name="cmd1" />'
'</andTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
for j, a in enumerate(self.attribute_lists):
for k, c in enumerate(self.cmd_lists):
if self.has_ab[i] and self.has_attr1[j] and self.has_cmd1[k]:
asserts.assert_equal(xml_callable(f, a, c), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, a, c), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[AB & attr1 & cmd1]')
def test_conformance_or_term_with_three_terms(self):
# or term with three features
xml = ('<optionalConform>'
'<orTerm>'
'<feature name="AB" />'
'<feature name="CD" />'
'<feature name="EF" />'
'</orTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
# no features
asserts.assert_equal(xml_callable(0x00, [], []), ConformanceDecision.NOT_APPLICABLE)
# one feature
asserts.assert_equal(xml_callable(0x01, [], []), ConformanceDecision.OPTIONAL)
# all features
asserts.assert_equal(xml_callable(0x07, [], []), ConformanceDecision.OPTIONAL)
asserts.assert_equal(str(xml_callable), '[AB | CD | EF]')
# or term with one of each
xml = ('<optionalConform>'
'<orTerm>'
'<feature name="AB" />'
'<attribute name="attr1" />'
'<command name="cmd1" />'
'</orTerm>'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
for j, a in enumerate(self.attribute_lists):
for k, c in enumerate(self.cmd_lists):
if self.has_ab[i] or self.has_attr1[j] or self.has_cmd1[k]:
asserts.assert_equal(xml_callable(f, a, c), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, a, c), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), '[AB | attr1 | cmd1]')
def test_conformance_otherwise(self):
# AB, O
xml = ('<otherwiseConform>'
'<mandatoryConform>'
'<feature name="AB" />'
'</mandatoryConform>'
'<optionalConform />'
'</otherwiseConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
asserts.assert_equal(str(xml_callable), 'AB, O')
# AB, [CD]
xml = ('<otherwiseConform>'
'<mandatoryConform>'
'<feature name="AB" />'
'</mandatoryConform>'
'<optionalConform>'
'<feature name="CD" />'
'</optionalConform>'
'</otherwiseConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
elif self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
asserts.assert_equal(str(xml_callable), 'AB, [CD]')
# AB & !CD, P
xml = ('<otherwiseConform>'
'<mandatoryConform>'
'<andTerm>'
'<feature name="AB" />'
'<notTerm>'
'<feature name="CD" />'
'</notTerm>'
'</andTerm>'
'</mandatoryConform>'
'<provisionalConform />'
'</otherwiseConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
for i, f in enumerate(self.feature_maps):
if self.has_ab[i] and not self.has_cd[i]:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
else:
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.PROVISIONAL)
asserts.assert_equal(str(xml_callable), 'AB & !CD, P')
def test_conformance_greater(self):
# AB, [CD]
xml = ('<mandatoryConform>'
'<greaterTerm>'
'<attribute name="attr1" />'
'<literal value="1" />'
'</greaterTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_callable_from_xml(et, self.params)
# TODO: switch this to check greater than once the update to the base is done (#33422)
asserts.assert_equal(xml_callable(0x00, [], []), ConformanceDecision.OPTIONAL)
asserts.assert_equal(str(xml_callable), 'attr1 > 1')
# Ensure that we can only have greater terms with exactly 2 value
xml = ('<mandatoryConform>'
'<greaterTerm>'
'<attribute name="attr1" />'
'<attribute name="attr2" />'
'<literal value="1" />'
'</greaterTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
try:
xml_callable = parse_callable_from_xml(et, self.params)
asserts.fail("Incorrectly parsed bad greaterTerm XML with > 2 values")
except ConformanceException:
pass
xml = ('<mandatoryConform>'
'<greaterTerm>'
'<attribute name="attr1" />'
'</greaterTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
try:
xml_callable = parse_callable_from_xml(et, self.params)
asserts.fail("Incorrectly parsed bad greaterTerm XML with < 2 values")
except ConformanceException:
pass
# Only attributes and literals allowed because arithmetic operations require values
xml = ('<mandatoryConform>'
'<greaterTerm>'
'<feature name="AB" />'
'<literal value="1" />'
'</greaterTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
try:
xml_callable = parse_callable_from_xml(et, self.params)
asserts.fail("Incorrectly parsed greater term with feature value")
except ConformanceException:
pass
def test_basic_conformance(self):
basic_test('<mandatoryConform />', mandatory)
basic_test('<optionalConform />', optional)
basic_test('<disallowConform />', disallowed)
basic_test('<deprecateConform />', deprecated)
basic_test('<provisionalConform />', provisional)
basic_test('<condition name="zigbee" />', zigbee)
# feature is not basic so we should get an exception
xml = '<feature name="CD" />'
et = ElementTree.fromstring(xml)
try:
parse_basic_callable_from_xml(et)
asserts.fail("Unexpected success parsing non-basic conformance")
except ConformanceException:
pass
# mandatory tag is basic, but this one is a wrapper, so we should get a TypeError
xml = ('<mandatoryConform>'
'<andTerm>'
'<feature name="AB" />'
'<notTerm>'
'<feature name="CD" />'
'</notTerm>'
'</andTerm>'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
try:
parse_basic_callable_from_xml(et)
asserts.fail("Unexpected success parsing mandatory wrapper")
except ConformanceException:
pass
def test_device_type_conformance(self):
msg = "Unexpected conformance returned for device type"
xml = ('<mandatoryConform>'
'<condition name="zigbee" />'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_device_type_callable_from_xml(et)
asserts.assert_equal(str(xml_callable), 'Zigbee', msg)
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.NOT_APPLICABLE, msg)
xml = ('<optionalConform>'
'<condition name="zigbee" />'
'</optionalConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_device_type_callable_from_xml(et)
# expect no exception here
asserts.assert_equal(str(xml_callable), '[Zigbee]', msg)
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.NOT_APPLICABLE, msg)
# otherwise conforms are allowed
xml = ('<otherwiseConform>'
'<condition name="zigbee" />'
'<provisionalConform />'
'</otherwiseConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_device_type_callable_from_xml(et)
# expect no exception here
asserts.assert_equal(str(xml_callable), 'Zigbee, P', msg)
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.PROVISIONAL, msg)
# Device type conditions or features don't correspond to anything in the spec, so the XML takes a best
# guess as to what they are. We should be able to parse features, conditions, attributes as the same
# thing.
# TODO: allow querying conformance for conditional device features
# TODO: adjust conformance call function to accept a list of features and evaluate based on that
xml = ('<mandatoryConform>'
'<feature name="CD" />'
'</mandatoryConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_device_type_callable_from_xml(et)
asserts.assert_equal(str(xml_callable), 'CD', msg)
# Device features are always optional (at least for now), even though we didn't pass this feature in
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.OPTIONAL)
xml = ('<otherwiseConform>'
'<feature name="CD" />'
'<condition name="testy" />'
'</otherwiseConform>')
et = ElementTree.fromstring(xml)
xml_callable = parse_device_type_callable_from_xml(et)
asserts.assert_equal(str(xml_callable), 'CD, testy', msg)
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.OPTIONAL)
if __name__ == "__main__":
default_matter_test_main()