15
15
# limitations under the License.
16
16
#
17
17
18
- import xml . etree . ElementTree as ElementTree
18
+ from importlib . abc import Traversable
19
19
from jinja2 import Template
20
20
import os
21
+ import xml .etree .ElementTree as ElementTree
22
+ import zipfile
21
23
22
24
from chip .testing .matter_testing import (MatterBaseTest , default_matter_test_main ,
23
25
ProblemNotice , ProblemSeverity , NamespacePathLocation )
24
- from chip .testing .spec_parsing import (XmlNamespace , parse_namespace ,
25
- build_xml_namespaces )
26
+ from chip .testing .spec_parsing import (XmlNamespace , parse_namespace , PrebuiltDataModelDirectory ,
27
+ build_xml_namespaces , get_data_model_directory , DataModelLevel )
26
28
from mobly import asserts
27
29
28
30
class TestSpecParsingNamespace (MatterBaseTest ):
29
31
def setup_class (self ):
30
- # Get the data model paths
31
- self .dm_1_3 = os .path .join (os .path .dirname (__file__ ), ".." , ".." , "data_model" , "1.3" )
32
- self .dm_1_4 = os .path .join (os .path .dirname (__file__ ), ".." , ".." , "data_model" , "1.4" )
33
- self .dm_1_4_1 = os .path .join (os .path .dirname (__file__ ), ".." , ".." , "data_model" , "1.4.1" )
34
- self .dm_master = os .path .join (os .path .dirname (__file__ ), ".." , ".." , "data_model" , "master" )
35
-
36
32
# Test data setup
37
33
self .namespace_id = 0x0001
38
34
self .namespace_name = "Test Namespace"
@@ -110,14 +106,14 @@ def test_no_tags(self):
110
106
111
107
def test_spec_files (self ):
112
108
"""Test parsing actual spec files from different versions"""
113
- one_three , _ = build_xml_namespaces (self . dm_1_3 )
114
- one_four , one_four_problems = build_xml_namespaces (self . dm_1_4 )
115
- one_four_one , one_four_one_problems = build_xml_namespaces (self . dm_1_4_1 )
116
- tot , tot_problems = build_xml_namespaces (self . dm_master )
109
+ one_three , _ = build_xml_namespaces (PrebuiltDataModelDirectory . k1_3 )
110
+ one_four , one_four_problems = build_xml_namespaces (PrebuiltDataModelDirectory . k1_4 )
111
+ one_four_one , one_four_one_problems = build_xml_namespaces (PrebuiltDataModelDirectory . k1_4_1 )
112
+ tot , tot_problems = build_xml_namespaces (PrebuiltDataModelDirectory . kMaster )
117
113
118
114
asserts .assert_equal (len (one_four_problems ), 0 , "Problems found when parsing 1.4 spec" )
119
115
asserts .assert_equal (len (one_four_one_problems ), 0 , "Problems found when parsing 1.4.1 spec" )
120
-
116
+
121
117
# Check version relationships
122
118
asserts .assert_greater (len (set (tot .keys ()) - set (one_three .keys ())),
123
119
0 , "Master dir does not contain any namespaces not in 1.3" )
@@ -132,50 +128,49 @@ def test_spec_files(self):
132
128
asserts .assert_equal (set (one_four .keys ()) - set (tot .keys ()),
133
129
set (), "There are some 1.4 namespaces that are unexpectedly not included in the TOT files" )
134
130
135
- def validate_namespace_xml (self , xml_file : str ) -> list [ProblemNotice ]:
136
- # Validating XML namespace files
137
- problems = []
131
+ def validate_namespace_xml (self , xml_file : Traversable ) -> list [ProblemNotice ]:
132
+ """Validate namespace XML file"""
133
+ problems : list [ ProblemNotice ] = []
138
134
try :
139
- tree = ElementTree .parse (xml_file )
140
- root = tree .getroot ()
141
-
142
- # Check for namespace ID and validate format
143
- namespace_id = root .get ('id' )
144
- if not namespace_id :
145
- problems .append (ProblemNotice (
146
- test_name = "Validate Namespace XML" ,
147
- location = NamespacePathLocation (),
148
- severity = ProblemSeverity .WARNING ,
149
- problem = f"Missing namespace ID in { xml_file } "
150
- ))
151
- else :
152
- # Validate 16-bit hex format (0xNNNN)
153
- try :
154
- # Remove '0x' prefix if present and try to parse
155
- id_value = int (namespace_id .replace ('0x' , '' ), 16 )
156
- if id_value < 0 or id_value > 0xFFFF :
157
- problems .append (ProblemNotice (
158
- test_name = "Validate Namespace XML" ,
159
- location = NamespacePathLocation (),
160
- severity = ProblemSeverity .WARNING ,
161
- problem = f"Namespace ID { namespace_id } is not a valid 16-bit value in { xml_file } "
162
- ))
163
-
164
- # Check format is exactly 0xNNNN where N is a hex digit
165
- if not namespace_id .lower ().startswith ('0x' ) or len (namespace_id ) != 6 :
166
- problems .append (ProblemNotice (
167
- test_name = "Validate Namespace XML" ,
168
- location = NamespacePathLocation (),
169
- severity = ProblemSeverity .WARNING ,
170
- problem = f"Namespace ID { namespace_id } does not follow required format '0xNNNN' in { xml_file } "
171
- ))
172
- except ValueError :
135
+ with xml_file .open ('r' , encoding = "utf8" ) as f :
136
+ root = ElementTree .parse (f ).getroot ()
137
+
138
+ # Check for namespace ID and validate format
139
+ namespace_id = root .get ('id' )
140
+ if not namespace_id :
173
141
problems .append (ProblemNotice (
174
142
test_name = "Validate Namespace XML" ,
175
143
location = NamespacePathLocation (),
176
144
severity = ProblemSeverity .WARNING ,
177
- problem = f"Invalid hex format for namespace ID { namespace_id } in { xml_file } "
145
+ problem = f"Missing namespace ID in { xml_file . name } "
178
146
))
147
+ else :
148
+ # Validate 16-bit hex format (0xNNNN)
149
+ try :
150
+ # Remove '0x' prefix if present and try to parse
151
+ id_value = int (namespace_id .replace ('0x' , '' ), 16 )
152
+ if id_value < 0 or id_value > 0xFFFF :
153
+ problems .append (ProblemNotice (
154
+ test_name = "Validate Namespace XML" ,
155
+ location = NamespacePathLocation (),
156
+ severity = ProblemSeverity .WARNING ,
157
+ problem = f"Namespace ID { namespace_id } is not a valid 16-bit value in { xml_file .name } "
158
+ ))
159
+ # Check format is exactly 0xNNNN where N is hex digit
160
+ if not namespace_id .lower ().startswith ('0x' ) or len (namespace_id ) != 6 :
161
+ problems .append (ProblemNotice (
162
+ test_name = "Validate Namespace XML" ,
163
+ location = NamespacePathLocation (),
164
+ severity = ProblemSeverity .WARNING ,
165
+ problem = f"Namespace ID { namespace_id } does not follow required format '0xNNNN' in { xml_file .name } "
166
+ ))
167
+ except ValueError :
168
+ problems .append (ProblemNotice (
169
+ test_name = "Validate Namespace XML" ,
170
+ location = NamespacePathLocation (),
171
+ severity = ProblemSeverity .WARNING ,
172
+ problem = f"Invalid hex format for namespace ID { namespace_id } in { xml_file .name } "
173
+ ))
179
174
180
175
# Check for namespace name
181
176
namespace_name = root .get ('name' , '' ).strip ()
@@ -184,7 +179,7 @@ def validate_namespace_xml(self, xml_file: str) -> list[ProblemNotice]:
184
179
test_name = "Validate Namespace XML" ,
185
180
location = NamespacePathLocation (),
186
181
severity = ProblemSeverity .WARNING ,
187
- problem = f"Missing or empty namespace name in { xml_file } "
182
+ problem = f"Missing or empty namespace name in { xml_file . name } "
188
183
))
189
184
190
185
# Check tags structure
@@ -198,34 +193,33 @@ def validate_namespace_xml(self, xml_file: str) -> list[ProblemNotice]:
198
193
test_name = "Validate Namespace XML" ,
199
194
location = NamespacePathLocation (),
200
195
severity = ProblemSeverity .WARNING ,
201
- problem = f"Missing tag ID in { xml_file } "
196
+ problem = f"Missing tag ID in { xml_file . name } "
202
197
))
203
198
else :
204
- # Validate 16-bit hex format for tags (0xNNNN)
205
199
try :
206
200
# Remove '0x' prefix if present and try to parse
207
- id_value = int (tag_id .replace ('0x' , '' ), 16 )
201
+ id_value = int (tag_id .replace ('0x' , '' ), 0 )
208
202
if id_value < 0 or id_value > 0xFFFF :
209
203
problems .append (ProblemNotice (
210
204
test_name = "Validate Namespace XML" ,
211
205
location = NamespacePathLocation (),
212
206
severity = ProblemSeverity .WARNING ,
213
- problem = f"Tag ID { tag_id } is not a valid 16-bit value in { xml_file } "
207
+ problem = f"Tag ID { tag_id } is not a valid 16-bit value in { xml_file . name } "
214
208
))
215
209
# Check format is exactly 0xNNNN where N is hex digit
216
210
if not tag_id .lower ().startswith ('0x' ) or len (tag_id ) != 6 :
217
211
problems .append (ProblemNotice (
218
212
test_name = "Validate Namespace XML" ,
219
213
location = NamespacePathLocation (),
220
214
severity = ProblemSeverity .WARNING ,
221
- problem = f"Tag ID { tag_id } does not follow required format '0xNNNN' in { xml_file } "
215
+ problem = f"Tag ID { tag_id } does not follow required format '0xNNNN' in { xml_file . name } "
222
216
))
223
217
except ValueError :
224
218
problems .append (ProblemNotice (
225
219
test_name = "Validate Namespace XML" ,
226
220
location = NamespacePathLocation (),
227
221
severity = ProblemSeverity .WARNING ,
228
- problem = f"Invalid hex format for tag ID { tag_id } in { xml_file } "
222
+ problem = f"Invalid hex format for tag ID { tag_id } in { xml_file . name } "
229
223
))
230
224
231
225
# Check tag name
@@ -235,56 +229,76 @@ def validate_namespace_xml(self, xml_file: str) -> list[ProblemNotice]:
235
229
test_name = "Validate Namespace XML" ,
236
230
location = NamespacePathLocation (),
237
231
severity = ProblemSeverity .WARNING ,
238
- problem = f"Missing or empty tag name in { xml_file } "
232
+ problem = f"Missing or empty tag name in { xml_file . name } "
239
233
))
240
234
241
235
except Exception as e :
242
236
problems .append (ProblemNotice (
243
237
test_name = "Validate Namespace XML" ,
244
- location = NamespacePathLocation (),
238
+ location = UnknownProblemLocation (),
245
239
severity = ProblemSeverity .WARNING ,
246
- problem = f"Failed to parse { xml_file } : { str (e )} "
240
+ problem = f"Failed to parse { xml_file . name } : { str (e )} "
247
241
))
248
242
249
243
return problems
250
244
251
245
def test_all_namespace_files (self ):
252
- """Test all namespace XML files in the 1.4 and 1.4.1 data model directories"""
246
+ """Test all namespace XML files in the data model namespaces directories"""
253
247
data_model_versions = {
254
- "1.4" : self . dm_1_4 ,
255
- "1.4.1" : self . dm_1_4_1 ,
248
+ "1.4" : PrebuiltDataModelDirectory . k1_4 ,
249
+ "1.4.1" : PrebuiltDataModelDirectory . k1_4_1 ,
256
250
}
257
251
258
252
for version , dm_path in data_model_versions .items ():
259
- namespace_path = os .path .join (dm_path , "namespaces" )
260
- if not os .path .exists (namespace_path ):
261
- self .print_step ("Issue encountered" , f"\n Skipping { version } - namespace directory not found" )
262
- continue
263
-
264
- for filename in os .listdir (namespace_path ):
265
- if not filename .endswith ('.xml' ):
266
- continue
267
-
268
- filepath = os .path .join (namespace_path , filename )
269
- problems = self .validate_namespace_xml (filepath )
270
-
253
+ try :
254
+ # First get the namespaces
255
+ namespaces , problems = build_xml_namespaces (dm_path )
271
256
if problems :
272
257
for problem in problems :
273
- self .print_step ("problem" , problem )
274
-
275
- # Run the same validation we did for generated XML
276
- tree = ElementTree .parse (filepath )
277
- namespace , parse_problems = parse_namespace (tree .getroot ())
278
-
279
- # Verify namespace has required attributes
280
- asserts .assert_true (hasattr (namespace , 'id' ), f"Namespace in { filename } missing ID" )
281
- asserts .assert_true (hasattr (namespace , 'name' ), f"Namespace in { filename } missing name" )
282
- asserts .assert_true (hasattr (namespace , 'tags' ), f"Namespace in { filename } missing tags dictionary" )
258
+ print (f" - { problem } " )
259
+
260
+ # Get the directory for validation
261
+ top = get_data_model_directory (dm_path , DataModelLevel .kNamespace )
283
262
284
- # Verify each tag has required attributes
285
- for tag_id , tag in namespace .tags .items ():
286
- asserts .assert_true (hasattr (tag , 'id' ), f"Tag in { filename } missing ID" )
287
- asserts .assert_true (hasattr (tag , 'name' ), f"Tag in { filename } missing name" )
263
+ # Handle both zip files and directories
264
+ if isinstance (top , zipfile .Path ):
265
+ files = [f for f in top .iterdir () if str (f ).endswith ('.xml' )]
266
+ else :
267
+ files = [f for f in top .iterdir () if f .name .endswith ('.xml' )]
268
+
269
+ # Validate each XML file
270
+ for file in files :
271
+ validation_problems = self .validate_namespace_xml (file )
272
+ if validation_problems :
273
+ for problem in validation_problems :
274
+ asserts .assert_false (
275
+ problem .severity == ProblemSeverity .ERROR ,
276
+ f"Error in { file .name } : { problem } "
277
+ )
278
+
279
+ # Parse and verify the namespace
280
+ with file .open ('r' , encoding = "utf8" ) as xml :
281
+ root = ElementTree .parse (xml ).getroot ()
282
+ namespace , parse_problems = parse_namespace (root )
283
+
284
+ # Basic attribute verification
285
+ asserts .assert_true (hasattr (namespace , 'id' ),
286
+ f"Namespace in { file .name } missing ID" )
287
+ asserts .assert_true (hasattr (namespace , 'name' ),
288
+ f"Namespace in { file .name } missing name" )
289
+ asserts .assert_true (hasattr (namespace , 'tags' ),
290
+ f"Namespace in { file .name } missing tags dictionary" )
291
+
292
+ # Verify each tag
293
+ for tag_id , tag in namespace .tags .items ():
294
+ asserts .assert_true (hasattr (tag , 'id' ),
295
+ f"Tag in { file .name } missing ID" )
296
+ asserts .assert_true (hasattr (tag , 'name' ),
297
+ f"Tag in { file .name } missing name" )
298
+
299
+ except Exception as e :
300
+ print (f"Error processing { version } : { str (e )} " )
301
+ raise
288
302
289
303
if __name__ == "__main__" :
290
304
default_matter_test_main ()
0 commit comments