18
18
from typing import Any
19
19
20
20
import chip .clusters as Clusters
21
+ from conformance_support import ConformanceDecision
22
+ from global_attribute_ids import GlobalAttributeIds
21
23
from matter_testing_support import MatterBaseTest , async_test_body , default_matter_test_main
22
24
from mobly import asserts
23
25
from spec_parsing_support import build_xml_clusters , build_xml_device_types
@@ -109,6 +111,10 @@ def create_onoff_endpoint(endpoint: int) -> dict[int, dict[int, dict[int, Any]]]
109
111
return endpoint_tlv
110
112
111
113
114
+ def is_mandatory (conformance ):
115
+ return conformance (0 , [], []).decision == ConformanceDecision .MANDATORY
116
+
117
+
112
118
class TestConformanceSupport (MatterBaseTest , DeviceConformanceTests ):
113
119
def setup_class (self ):
114
120
self .xml_clusters , self .problems = build_xml_clusters ()
@@ -135,6 +141,96 @@ async def test_provisional_cluster(self):
135
141
success , problems = self .check_conformance (ignore_in_progress = False , is_ci = False , allow_provisional = False )
136
142
asserts .assert_true (success , "Unexpected failure parsing endpoint with no clusters marked as provisional" )
137
143
144
+ def _create_minimal_cluster (self , cluster_id : int ) -> dict [int , Any ]:
145
+ attrs = {}
146
+ attrs [GlobalAttributeIds .FEATURE_MAP_ID ] = 0
147
+
148
+ mandatory_attributes = [id for id , a in self .xml_clusters [cluster_id ].attributes .items () if is_mandatory (a .conformance )]
149
+ for m in mandatory_attributes :
150
+ # dummy versions - we're not using the values in this test
151
+ attrs [m ] = 0
152
+ attrs [GlobalAttributeIds .ATTRIBUTE_LIST_ID ] = mandatory_attributes
153
+ mandatory_accepted_commands = [id for id , a in self .xml_clusters [cluster_id ].accepted_commands .items ()
154
+ if is_mandatory (a .conformance )]
155
+ attrs [GlobalAttributeIds .ACCEPTED_COMMAND_LIST_ID ] = mandatory_accepted_commands
156
+ mandatory_generated_commands = [id for id , a in self .xml_clusters [cluster_id ].generated_commands .items ()
157
+ if is_mandatory (a .conformance )]
158
+ attrs [GlobalAttributeIds .GENERATED_COMMAND_LIST_ID ] = mandatory_generated_commands
159
+ attrs [GlobalAttributeIds .CLUSTER_REVISION_ID ] = self .xml_clusters [cluster_id ].revision
160
+ return attrs
161
+
162
+ def _create_minimal_dt (self , device_type_id : int ) -> dict [int , dict [int , Any ]]:
163
+ ''' Creates the internals of an endpoint_tlv with the minimal set of clusters, with the minimal set of attributes and commands. Global attributes only.
164
+ Does NOT take into account overrides yet.
165
+ '''
166
+ endpoint_tlv = {}
167
+ required_servers = [id for id , c in self .xml_device_types [device_type_id ].server_clusters .items ()
168
+ if is_mandatory (c .conformance )]
169
+ required_clients = [id for id , c in self .xml_device_types [device_type_id ].client_clusters .items ()
170
+ if is_mandatory (c .conformance )]
171
+ device_type_revision = self .xml_device_types [device_type_id ].revision
172
+
173
+ for s in required_servers :
174
+ endpoint_tlv [s ] = self ._create_minimal_cluster (s )
175
+
176
+ # Descriptor
177
+ attr = Clusters .Descriptor .Attributes
178
+ attrs = {}
179
+ attrs [attr .FeatureMap .attribute_id ] = 0
180
+ attrs [attr .AcceptedCommandList .attribute_id ] = []
181
+ attrs [attr .GeneratedCommandList .attribute_id ] = []
182
+ attrs [attr .ClusterRevision .attribute_id ] = self .xml_clusters [Clusters .Descriptor .id ].revision
183
+ attrs [attr .DeviceTypeList .attribute_id ] = [
184
+ Clusters .Descriptor .Structs .DeviceTypeStruct (deviceType = device_type_id , revision = device_type_revision )]
185
+ attrs [attr .ServerList .attribute_id ] = required_servers
186
+ attrs [attr .ClientList .attribute_id ] = required_clients
187
+ attrs [attr .PartsList .attribute_id ] = []
188
+ attrs [attr .AttributeList .attribute_id ] = []
189
+ attrs [attr .AttributeList .attribute_id ] = list (attrs .keys ())
190
+
191
+ endpoint_tlv [Clusters .Descriptor .id ] = attrs
192
+ return endpoint_tlv
193
+
194
+ def add_macl (self , root_endpoint : dict [int , dict [int , Any ]]):
195
+ ac = Clusters .AccessControl
196
+ root_endpoint [ac .id ][ac .Attributes .FeatureMap .attribute_id ] = ac .Bitmaps .Feature .kManagedDevice
197
+ root_endpoint [ac .id ][ac .Attributes .Arl .attribute_id ] = []
198
+ root_endpoint [ac .id ][ac .Attributes .CommissioningARL .attribute_id ] = []
199
+ root_endpoint [ac .id ][ac .Attributes .AttributeList .attribute_id ].extend ([
200
+ ac .Attributes .Arl .attribute_id , ac .Attributes .CommissioningARL .attribute_id ])
201
+ root_endpoint [ac .id ][ac .Attributes .AcceptedCommandList .attribute_id ].append (ac .Commands .ReviewFabricRestrictions .command_id )
202
+ root_endpoint [ac .id ][ac .Attributes .GeneratedCommandList .attribute_id ].append (
203
+ ac .Commands .ReviewFabricRestrictionsResponse .command_id )
204
+
205
+ @async_test_body
206
+ async def test_macl_handling (self ):
207
+ nim_id = self ._get_device_type_id ('network infrastructure manager' )
208
+ root_node_id = self ._get_device_type_id ('root node' )
209
+ on_off_id = self ._get_device_type_id ('On/Off Light' )
210
+
211
+ root = self ._create_minimal_dt (device_type_id = root_node_id )
212
+ nim = self ._create_minimal_dt (device_type_id = nim_id )
213
+ self .endpoints_tlv = {0 : root , 1 : nim }
214
+ asserts .assert_true (self ._has_device_type_supporting_macl (), "Did not find supported device in generated device" )
215
+
216
+ success , problems = self .check_conformance (ignore_in_progress = False , is_ci = False , allow_provisional = True )
217
+ self .problems .extend (problems )
218
+ asserts .assert_true (success , "Unexpected failure parsing minimal dt" )
219
+
220
+ self .add_macl (root )
221
+ # A MACL is allowed when there is a NIM, so this should succeed as well
222
+ success , problems = self .check_conformance (ignore_in_progress = False , is_ci = False , allow_provisional = True )
223
+ self .problems .extend (problems )
224
+ asserts .assert_true (success , "Unexpected failure with NIM and MACL" )
225
+
226
+ # A MACL is not allowed when there is no NIM
227
+ self .endpoints_tlv [1 ] = self ._create_minimal_dt (device_type_id = on_off_id )
228
+ success , problems = self .check_conformance (ignore_in_progress = False , is_ci = False , allow_provisional = True )
229
+ self .problems .extend (problems )
230
+ asserts .assert_false (success , "Unexpected success with On/Off and MACL" )
231
+
232
+ # TODO: what happens if there is a NIM and a non-NIM endpoint?
233
+
138
234
139
235
if __name__ == "__main__" :
140
236
default_matter_test_main ()
0 commit comments