21
21
from global_attribute_ids import GlobalAttributeIds
22
22
from matter_testing_support import MatterBaseTest , ProblemNotice , default_matter_test_main
23
23
from mobly import asserts
24
- from spec_parsing_support import (ClusterParser , XmlCluster , add_cluster_data_from_xml , check_clusters_for_unknown_commands ,
25
- combine_derived_clusters_with_base )
24
+ from spec_parsing_support import (ClusterParser , XmlCluster , add_cluster_data_from_xml , build_xml_clusters ,
25
+ check_clusters_for_unknown_commands , combine_derived_clusters_with_base )
26
26
27
27
# TODO: improve the test coverage here
28
28
# https://github.com/project-chip/connectedhomeip/issues/30958
@@ -40,6 +40,9 @@ def single_attribute_cluster_xml(read_access: str, write_access: str, write_supp
40
40
'<revision revision="2" summary="Some other revision"/>'
41
41
'<revision revision="3" summary="another revision"/>'
42
42
'</revisionHistory>' )
43
+ id_table = ('<clusterIds>'
44
+ f'<clusterId id="{ CLUSTER_ID } " name="{ CLUSTER_NAME } "/>'
45
+ '</clusterIds>' )
43
46
classification = '<classification hierarchy="base" role="utility" picsCode="TEST" scope="Node"/>'
44
47
read_access_str = f'read="true" readPrivilege="{ read_access } "' if read_access is not None else ""
45
48
write_access_str = f'write="{ write_supported } " writePrivilege="{ write_access } "' if write_access is not None else ""
@@ -53,14 +56,15 @@ def single_attribute_cluster_xml(read_access: str, write_access: str, write_supp
53
56
54
57
return (f'{ xml_cluster } '
55
58
f'{ revision_table } '
59
+ f'{ id_table } '
56
60
f'{ classification } '
57
61
f'{ attribute } '
58
62
'</cluster>' )
59
63
60
64
61
65
def parse_cluster (xml : str ) -> XmlCluster :
62
66
cluster = ElementTree .fromstring (xml )
63
- parser = ClusterParser (cluster , CLUSTER_ID , CLUSTER_NAME , False )
67
+ parser = ClusterParser (cluster , CLUSTER_ID , CLUSTER_NAME )
64
68
return parser .create_cluster ()
65
69
66
70
@@ -83,6 +87,9 @@ def get_access_enum_from_string(access_str: str) -> Clusters.AccessControl.Enums
83
87
' <revisionHistory>'
84
88
' <revision revision="1" summary="Initial version"/>'
85
89
' </revisionHistory>'
90
+ ' <clusterIds>'
91
+ ' <clusterId name="Test Base"/>'
92
+ ' </clusterIds>'
86
93
' <classification hierarchy="base" role="application" picsCode="BASE" scope="Endpoint"/>'
87
94
' <features>'
88
95
' <feature bit="0" code="DEPONOFF" name="OnOff" summary="Dependency with the OnOff cluster">'
@@ -151,6 +158,9 @@ def get_access_enum_from_string(access_str: str) -> Clusters.AccessControl.Enums
151
158
' <revisionHistory>'
152
159
' <revision revision="1" summary="Initial Release"/>'
153
160
' </revisionHistory>'
161
+ ' <clusterIds>'
162
+ ' <clusterId id="0xFFFF" name="Test Derived"/>'
163
+ ' </clusterIds>'
154
164
' <classification hierarchy="derived" baseCluster="Test Base" role="application" picsCode="MWOM" scope="Endpoint"/>'
155
165
' <attributes>'
156
166
' <attribute id="0x0000" name="SupportedModes">'
@@ -181,6 +191,9 @@ def get_access_enum_from_string(access_str: str) -> Clusters.AccessControl.Enums
181
191
' <revisionHistory>'
182
192
' <revision revision="1" summary="Initial version"/>'
183
193
' </revisionHistory>'
194
+ ' <clusterIds>'
195
+ ' <clusterId id="0xFFFE" name="Test Unknown Command"/>'
196
+ ' </clusterIds>'
184
197
' <classification hierarchy="base" role="application" picsCode="BASE" scope="Endpoint"/>'
185
198
' <commands>'
186
199
' <command id="0x00" name="ChangeToMode" direction="commandToClient">'
@@ -191,8 +204,32 @@ def get_access_enum_from_string(access_str: str) -> Clusters.AccessControl.Enums
191
204
'</cluster>'
192
205
)
193
206
207
+ ALIASED_CLUSTERS = (
208
+ '<cluster xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="types types.xsd cluster cluster.xsd" id="" name="Test Aliases" revision="1">'
209
+ ' <revisionHistory>'
210
+ ' <revision revision="1" summary="Initial version"/>'
211
+ ' </revisionHistory>'
212
+ ' <clusterIds>'
213
+ ' <clusterId id="0xFFFE" name="Test Alias1"/>'
214
+ ' <clusterId id="0xFFFD" name="Test Alias2"/>'
215
+ ' </clusterIds>'
216
+ ' <classification hierarchy="base" role="application" picsCode="BASE" scope="Endpoint"/>'
217
+ ' <commands>'
218
+ ' <command id="0x00" name="ChangeToMode" direction="commandToServer">'
219
+ ' <access invokePrivilege="operate"/>'
220
+ ' <mandatoryConform/>'
221
+ ' </command>'
222
+ ' </commands>'
223
+ '</cluster>'
224
+ )
225
+
194
226
195
227
class TestSpecParsingSupport (MatterBaseTest ):
228
+ def setup_class (self ):
229
+ super ().setup_class ()
230
+ self .spec_xml_clusters , self .spec_problems = build_xml_clusters ()
231
+ self .all_spec_clusters = set ([(id , c .name , c .pics ) for id , c in self .spec_xml_clusters .items ()])
232
+
196
233
def test_spec_parsing_access (self ):
197
234
strs = [None , 'view' , 'operate' , 'manage' , 'admin' ]
198
235
for read in strs :
@@ -296,6 +333,61 @@ def test_missing_command_direction(self):
296
333
asserts .assert_equal (problems [0 ].location .cluster_id , 0xFFFE , "Unexpected problem location (cluster id)" )
297
334
asserts .assert_equal (problems [0 ].location .command_id , 0 , "Unexpected problem location (command id)" )
298
335
336
+ def test_aliased_clusters (self ):
337
+ clusters : dict [int , XmlCluster ] = {}
338
+ pure_base_clusters : dict [str , XmlCluster ] = {}
339
+ ids_by_name : dict [str , int ] = {}
340
+ problems : list [ProblemNotice ] = []
341
+ cluster_xml = ElementTree .fromstring (ALIASED_CLUSTERS )
342
+
343
+ add_cluster_data_from_xml (cluster_xml , clusters , pure_base_clusters , ids_by_name , problems )
344
+ asserts .assert_equal (len (problems ), 0 , "Unexpected problem parsing aliased clusters" )
345
+ asserts .assert_equal (len (clusters ), 2 , "Unexpected number of clusters when parsing aliased cluster set" )
346
+ asserts .assert_equal (len (pure_base_clusters ), 0 , "Unexpected number of pure base clusters" )
347
+ asserts .assert_equal (len (ids_by_name ), 2 , "Unexpected number of ids by name" )
348
+
349
+ ids = [(id , c .name ) for id , c in clusters .items ()]
350
+ asserts .assert_true ((0xFFFE , 'Test Alias1' ) in ids , "Unable to find Test Alias1 cluster in parsed clusters" )
351
+ asserts .assert_true ((0xFFFD , 'Test Alias2' ) in ids , "Unable to find Test Alias2 cluster in parsed clusters" )
352
+
353
+ def test_known_aliased_clusters (self ):
354
+ known_aliased_clusters = set ([(0x040C , 'Carbon Monoxide Concentration Measurement' , 'CMOCONC' ),
355
+ (0x040D , 'Carbon Dioxide Concentration Measurement' , 'CDOCONC' ),
356
+ (0x0413 , 'Nitrogen Dioxide Concentration Measurement' , 'NDOCONC' ),
357
+ (0x0415 , 'Ozone Concentration Measurement' , 'OZCONC' ),
358
+ # Change to "PM2.5 Concentration Measurement" once https://github.com/csa-data-model/projects/issues/453 is fixed
359
+ (0x042A , 'PM2' , 'PMICONC' ),
360
+ (0x042B , 'Formaldehyde Concentration Measurement' , 'FLDCONC' ),
361
+ (0x042C , 'PM1 Concentration Measurement' , 'PMHCONC' ),
362
+ (0x042D , 'PM10 Concentration Measurement' , 'PMKCONC' ),
363
+ (0x042E , 'Total Volatile Organic Compounds Concentration Measurement' , 'TVOCCONC' ),
364
+ (0x042F , 'Radon Concentration Measurement' , 'RNCONC' ),
365
+ (0x0071 , 'HEPA Filter Monitoring' , 'HEPAFREMON' ),
366
+ (0x0072 , 'Activated Carbon Filter Monitoring' , 'ACFREMON' ),
367
+ (0x0405 , 'Relative Humidity Measurement' , 'RH' )])
368
+
369
+ missing_clusters = known_aliased_clusters - self .all_spec_clusters
370
+ asserts .assert_equal (len (missing_clusters ), 0 , f"Missing aliased clusters from DM XML - { missing_clusters } " )
371
+
372
+ def test_known_derived_clusters (self ):
373
+ known_derived_clusters = set ([(0x0048 , 'Oven Cavity Operational State' , 'OVENOPSTATE' ),
374
+ (0x0049 , 'Oven Mode' , 'OTCCM' ),
375
+ (0x0051 , 'Laundry Washer Mode' , 'LWM' ),
376
+ (0x0052 , 'Refrigerator And Temperature Controlled Cabinet Mode' , 'TCCM' ),
377
+ (0x0054 , 'RVC Run Mode' , 'RVCRUNM' ),
378
+ (0x0055 , 'RVC Clean Mode' , 'RVCCLEANM' ),
379
+ (0x0057 , 'Refrigerator Alarm' , 'REFALM' ),
380
+ (0x0059 , 'Dishwasher Mode' , 'DISHM' ),
381
+ (0x005c , 'Smoke CO Alarm' , 'SMOKECO' ),
382
+ (0x005d , 'Dishwasher Alarm' , 'DISHALM' ),
383
+ (0x005e , 'Microwave Oven Mode' , 'MWOM' ),
384
+ (0x0061 , 'RVC Operational State' , 'RVCOPSTATE' )])
385
+
386
+ missing_clusters = known_derived_clusters - self .all_spec_clusters
387
+ asserts .assert_equal (len (missing_clusters ), 0 , f"Missing aliased clusters from DM XML - { missing_clusters } " )
388
+ for d in known_derived_clusters :
389
+ asserts .assert_true (self .spec_xml_clusters is not None , "Derived cluster with no base cluster marker" )
390
+
299
391
300
392
if __name__ == "__main__" :
301
393
default_matter_test_main ()
0 commit comments