Skip to content

Commit d84f13e

Browse files
cecillerestyled-commits
andauthoredJun 6, 2024
Python testing: Conformance support for device type conditions (#33622)
* Python testing: Conformance support for device type conditions * fix type * strings * simplify exception * Restyled by isort * address review comments --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent 2b6969e commit d84f13e

File tree

2 files changed

+206
-73
lines changed

2 files changed

+206
-73
lines changed
 

‎src/python_testing/TestConformanceSupport.py

+113-34
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@
1616
#
1717

1818
import xml.etree.ElementTree as ElementTree
19+
from typing import Callable
1920

20-
from conformance_support import ConformanceDecision, ConformanceException, ConformanceParseParameters, parse_callable_from_xml
21-
from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
21+
from conformance_support import (ConformanceDecision, ConformanceException, ConformanceParseParameters, deprecated, disallowed,
22+
mandatory, optional, parse_basic_callable_from_xml, parse_callable_from_xml,
23+
parse_device_type_callable_from_xml, provisional, zigbee)
24+
from matter_testing_support import MatterBaseTest, default_matter_test_main
2225
from mobly import asserts
2326

2427

28+
def basic_test(xml: str, cls: Callable) -> None:
29+
et = ElementTree.fromstring(xml)
30+
xml_callable = parse_basic_callable_from_xml(et)
31+
asserts.assert_true(isinstance(xml_callable, cls), "Unexpected class parsed from basic conformance")
32+
33+
2534
class TestConformanceSupport(MatterBaseTest):
26-
@async_test_body
27-
async def setup_class(self):
35+
def setup_class(self):
2836
super().setup_class()
2937
# a small feature map
3038
self.feature_names_to_bits = {'AB': 0x01, 'CD': 0x02}
@@ -46,26 +54,23 @@ async def setup_class(self):
4654
self.params = ConformanceParseParameters(
4755
feature_map=self.feature_names_to_bits, attribute_map=self.attribute_names_to_values, command_map=self.command_names_to_values)
4856

49-
@async_test_body
50-
async def test_conformance_mandatory(self):
57+
def test_conformance_mandatory(self):
5158
xml = '<mandatoryConform />'
5259
et = ElementTree.fromstring(xml)
5360
xml_callable = parse_callable_from_xml(et, self.params)
5461
for f in self.feature_maps:
5562
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.MANDATORY)
5663
asserts.assert_equal(str(xml_callable), 'M')
5764

58-
@async_test_body
59-
async def test_conformance_optional(self):
65+
def test_conformance_optional(self):
6066
xml = '<optionalConform />'
6167
et = ElementTree.fromstring(xml)
6268
xml_callable = parse_callable_from_xml(et, self.params)
6369
for f in self.feature_maps:
6470
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.OPTIONAL)
6571
asserts.assert_equal(str(xml_callable), 'O')
6672

67-
@async_test_body
68-
async def test_conformance_disallowed(self):
73+
def test_conformance_disallowed(self):
6974
xml = '<disallowConform />'
7075
et = ElementTree.fromstring(xml)
7176
xml_callable = parse_callable_from_xml(et, self.params)
@@ -80,26 +85,23 @@ async def test_conformance_disallowed(self):
8085
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.DISALLOWED)
8186
asserts.assert_equal(str(xml_callable), 'D')
8287

83-
@async_test_body
84-
async def test_conformance_provisional(self):
88+
def test_conformance_provisional(self):
8589
xml = '<provisionalConform />'
8690
et = ElementTree.fromstring(xml)
8791
xml_callable = parse_callable_from_xml(et, self.params)
8892
for f in self.feature_maps:
8993
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.PROVISIONAL)
9094
asserts.assert_equal(str(xml_callable), 'P')
9195

92-
@async_test_body
93-
async def test_conformance_zigbee(self):
96+
def test_conformance_zigbee(self):
9497
xml = '<condition name="Zigbee"/>'
9598
et = ElementTree.fromstring(xml)
9699
xml_callable = parse_callable_from_xml(et, self.params)
97100
for f in self.feature_maps:
98101
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
99102
asserts.assert_equal(str(xml_callable), 'Zigbee')
100103

101-
@async_test_body
102-
async def test_conformance_mandatory_on_condition(self):
104+
def test_conformance_mandatory_on_condition(self):
103105
xml = ('<mandatoryConform>'
104106
'<feature name="AB" />'
105107
'</mandatoryConform>')
@@ -151,8 +153,7 @@ async def test_conformance_mandatory_on_condition(self):
151153

152154
# test command in optional and in boolean - this is the same as attribute essentially, so testing every permutation is overkill
153155

154-
@async_test_body
155-
async def test_conformance_optional_on_condition(self):
156+
def test_conformance_optional_on_condition(self):
156157
# single feature optional
157158
xml = ('<optionalConform>'
158159
'<feature name="AB" />'
@@ -228,8 +229,7 @@ async def test_conformance_optional_on_condition(self):
228229
asserts.assert_equal(xml_callable(0x00, [], c), ConformanceDecision.NOT_APPLICABLE)
229230
asserts.assert_equal(str(xml_callable), '[cmd2]')
230231

231-
@async_test_body
232-
async def test_conformance_not_term_mandatory(self):
232+
def test_conformance_not_term_mandatory(self):
233233
# single feature not mandatory
234234
xml = ('<mandatoryConform>'
235235
'<notTerm>'
@@ -288,8 +288,7 @@ async def test_conformance_not_term_mandatory(self):
288288
asserts.assert_equal(xml_callable(0x00, a, []), ConformanceDecision.NOT_APPLICABLE)
289289
asserts.assert_equal(str(xml_callable), '!attr2')
290290

291-
@async_test_body
292-
async def test_conformance_not_term_optional(self):
291+
def test_conformance_not_term_optional(self):
293292
# single feature not optional
294293
xml = ('<optionalConform>'
295294
'<notTerm>'
@@ -319,8 +318,7 @@ async def test_conformance_not_term_optional(self):
319318
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
320319
asserts.assert_equal(str(xml_callable), '[!CD]')
321320

322-
@async_test_body
323-
async def test_conformance_and_term(self):
321+
def test_conformance_and_term(self):
324322
# and term for features only
325323
xml = ('<mandatoryConform>'
326324
'<andTerm>'
@@ -370,8 +368,7 @@ async def test_conformance_and_term(self):
370368
asserts.assert_equal(xml_callable(f, a, []), ConformanceDecision.NOT_APPLICABLE)
371369
asserts.assert_equal(str(xml_callable), 'AB & attr2')
372370

373-
@async_test_body
374-
async def test_conformance_or_term(self):
371+
def test_conformance_or_term(self):
375372
# or term feature only
376373
xml = ('<mandatoryConform>'
377374
'<orTerm>'
@@ -421,8 +418,7 @@ async def test_conformance_or_term(self):
421418
asserts.assert_equal(xml_callable(f, a, []), ConformanceDecision.NOT_APPLICABLE)
422419
asserts.assert_equal(str(xml_callable), 'AB | attr2')
423420

424-
@async_test_body
425-
async def test_conformance_and_term_with_not(self):
421+
def test_conformance_and_term_with_not(self):
426422
# and term with not
427423
xml = ('<optionalConform>'
428424
'<andTerm>'
@@ -441,8 +437,7 @@ async def test_conformance_and_term_with_not(self):
441437
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
442438
asserts.assert_equal(str(xml_callable), '[!AB & CD]')
443439

444-
@async_test_body
445-
async def test_conformance_or_term_with_not(self):
440+
def test_conformance_or_term_with_not(self):
446441
# or term with not on second feature
447442
xml = ('<mandatoryConform>'
448443
'<orTerm>'
@@ -479,8 +474,7 @@ async def test_conformance_or_term_with_not(self):
479474
asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.NOT_APPLICABLE)
480475
asserts.assert_equal(str(xml_callable), '[!(AB | CD)]')
481476

482-
@async_test_body
483-
async def test_conformance_and_term_with_three_terms(self):
477+
def test_conformance_and_term_with_three_terms(self):
484478
# and term with three features
485479
xml = ('<optionalConform>'
486480
'<andTerm>'
@@ -519,8 +513,7 @@ async def test_conformance_and_term_with_three_terms(self):
519513
asserts.assert_equal(xml_callable(f, a, c), ConformanceDecision.NOT_APPLICABLE)
520514
asserts.assert_equal(str(xml_callable), '[AB & attr1 & cmd1]')
521515

522-
@async_test_body
523-
async def test_conformance_or_term_with_three_terms(self):
516+
def test_conformance_or_term_with_three_terms(self):
524517
# or term with three features
525518
xml = ('<optionalConform>'
526519
'<orTerm>'
@@ -671,6 +664,92 @@ def test_conformance_greater(self):
671664
except ConformanceException:
672665
pass
673666

667+
def test_basic_conformance(self):
668+
basic_test('<mandatoryConform />', mandatory)
669+
basic_test('<optionalConform />', optional)
670+
basic_test('<disallowConform />', disallowed)
671+
basic_test('<deprecateConform />', deprecated)
672+
basic_test('<provisionalConform />', provisional)
673+
basic_test('<condition name="zigbee" />', zigbee)
674+
675+
# feature is not basic so we should get an exception
676+
xml = '<feature name="CD" />'
677+
et = ElementTree.fromstring(xml)
678+
try:
679+
parse_basic_callable_from_xml(et)
680+
asserts.fail("Unexpected success parsing non-basic conformance")
681+
except ConformanceException:
682+
pass
683+
684+
# mandatory tag is basic, but this one is a wrapper, so we should get a TypeError
685+
xml = ('<mandatoryConform>'
686+
'<andTerm>'
687+
'<feature name="AB" />'
688+
'<notTerm>'
689+
'<feature name="CD" />'
690+
'</notTerm>'
691+
'</andTerm>'
692+
'</mandatoryConform>')
693+
et = ElementTree.fromstring(xml)
694+
try:
695+
parse_basic_callable_from_xml(et)
696+
asserts.fail("Unexpected success parsing mandatory wrapper")
697+
except ConformanceException:
698+
pass
699+
700+
def test_device_type_conformance(self):
701+
msg = "Unexpected conformance returned for device type"
702+
xml = ('<mandatoryConform>'
703+
'<condition name="zigbee" />'
704+
'</mandatoryConform>')
705+
et = ElementTree.fromstring(xml)
706+
xml_callable = parse_device_type_callable_from_xml(et)
707+
asserts.assert_equal(str(xml_callable), 'Zigbee', msg)
708+
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.NOT_APPLICABLE, msg)
709+
710+
xml = ('<optionalConform>'
711+
'<condition name="zigbee" />'
712+
'</optionalConform>')
713+
et = ElementTree.fromstring(xml)
714+
xml_callable = parse_device_type_callable_from_xml(et)
715+
# expect no exception here
716+
asserts.assert_equal(str(xml_callable), '[Zigbee]', msg)
717+
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.NOT_APPLICABLE, msg)
718+
719+
# otherwise conforms are allowed
720+
xml = ('<otherwiseConform>'
721+
'<condition name="zigbee" />'
722+
'<provisionalConform />'
723+
'</otherwiseConform>')
724+
et = ElementTree.fromstring(xml)
725+
xml_callable = parse_device_type_callable_from_xml(et)
726+
# expect no exception here
727+
asserts.assert_equal(str(xml_callable), 'Zigbee, P', msg)
728+
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.PROVISIONAL, msg)
729+
730+
# Device type conditions or features don't correspond to anything in the spec, so the XML takes a best
731+
# guess as to what they are. We should be able to parse features, conditions, attributes as the same
732+
# thing.
733+
# TODO: allow querying conformance for conditional device features
734+
# TODO: adjust conformance call function to accept a list of features and evaluate based on that
735+
xml = ('<mandatoryConform>'
736+
'<feature name="CD" />'
737+
'</mandatoryConform>')
738+
et = ElementTree.fromstring(xml)
739+
xml_callable = parse_device_type_callable_from_xml(et)
740+
asserts.assert_equal(str(xml_callable), 'CD', msg)
741+
# Device features are always optional (at least for now), even though we didn't pass this feature in
742+
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.OPTIONAL)
743+
744+
xml = ('<otherwiseConform>'
745+
'<feature name="CD" />'
746+
'<condition name="testy" />'
747+
'</otherwiseConform>')
748+
et = ElementTree.fromstring(xml)
749+
xml_callable = parse_device_type_callable_from_xml(et)
750+
asserts.assert_equal(str(xml_callable), 'CD, testy', msg)
751+
asserts.assert_equal(xml_callable(0, [], []), ConformanceDecision.OPTIONAL)
752+
674753

675754
if __name__ == "__main__":
676755
default_matter_test_main()

0 commit comments

Comments
 (0)