Skip to content

Commit c503967

Browse files
committed
Updating TestSpecParsingNamespace.py and spec_parsing.py:
- Updating to change method to using 0 instead of 16 bit enums - Updating to use PrebuiltDatamodelDirectory instead of os to set filepaths - Updated to utilizing the build_xml_namespaces function in spec_parsing.
1 parent 02c6d80 commit c503967

File tree

2 files changed

+162
-122
lines changed

2 files changed

+162
-122
lines changed

src/python_testing/TestSpecParsingNamespace.py

+105-91
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,20 @@
1515
# limitations under the License.
1616
#
1717

18-
import xml.etree.ElementTree as ElementTree
18+
from importlib.abc import Traversable
1919
from jinja2 import Template
2020
import os
21+
import xml.etree.ElementTree as ElementTree
22+
import zipfile
2123

2224
from chip.testing.matter_testing import (MatterBaseTest, default_matter_test_main,
2325
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)
2628
from mobly import asserts
2729

2830
class TestSpecParsingNamespace(MatterBaseTest):
2931
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-
3632
# Test data setup
3733
self.namespace_id = 0x0001
3834
self.namespace_name = "Test Namespace"
@@ -110,14 +106,14 @@ def test_no_tags(self):
110106

111107
def test_spec_files(self):
112108
"""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)
117113

118114
asserts.assert_equal(len(one_four_problems), 0, "Problems found when parsing 1.4 spec")
119115
asserts.assert_equal(len(one_four_one_problems), 0, "Problems found when parsing 1.4.1 spec")
120-
116+
121117
# Check version relationships
122118
asserts.assert_greater(len(set(tot.keys()) - set(one_three.keys())),
123119
0, "Master dir does not contain any namespaces not in 1.3")
@@ -132,50 +128,49 @@ def test_spec_files(self):
132128
asserts.assert_equal(set(one_four.keys()) - set(tot.keys()),
133129
set(), "There are some 1.4 namespaces that are unexpectedly not included in the TOT files")
134130

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] = []
138134
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:
173141
problems.append(ProblemNotice(
174142
test_name="Validate Namespace XML",
175143
location=NamespacePathLocation(),
176144
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}"
178146
))
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+
))
179174

180175
# Check for namespace name
181176
namespace_name = root.get('name', '').strip()
@@ -184,7 +179,7 @@ def validate_namespace_xml(self, xml_file: str) -> list[ProblemNotice]:
184179
test_name="Validate Namespace XML",
185180
location=NamespacePathLocation(),
186181
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}"
188183
))
189184

190185
# Check tags structure
@@ -198,34 +193,33 @@ def validate_namespace_xml(self, xml_file: str) -> list[ProblemNotice]:
198193
test_name="Validate Namespace XML",
199194
location=NamespacePathLocation(),
200195
severity=ProblemSeverity.WARNING,
201-
problem=f"Missing tag ID in {xml_file}"
196+
problem=f"Missing tag ID in {xml_file.name}"
202197
))
203198
else:
204-
# Validate 16-bit hex format for tags (0xNNNN)
205199
try:
206200
# 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)
208202
if id_value < 0 or id_value > 0xFFFF:
209203
problems.append(ProblemNotice(
210204
test_name="Validate Namespace XML",
211205
location=NamespacePathLocation(),
212206
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}"
214208
))
215209
# Check format is exactly 0xNNNN where N is hex digit
216210
if not tag_id.lower().startswith('0x') or len(tag_id) != 6:
217211
problems.append(ProblemNotice(
218212
test_name="Validate Namespace XML",
219213
location=NamespacePathLocation(),
220214
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}"
222216
))
223217
except ValueError:
224218
problems.append(ProblemNotice(
225219
test_name="Validate Namespace XML",
226220
location=NamespacePathLocation(),
227221
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}"
229223
))
230224

231225
# Check tag name
@@ -235,56 +229,76 @@ def validate_namespace_xml(self, xml_file: str) -> list[ProblemNotice]:
235229
test_name="Validate Namespace XML",
236230
location=NamespacePathLocation(),
237231
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}"
239233
))
240234

241235
except Exception as e:
242236
problems.append(ProblemNotice(
243237
test_name="Validate Namespace XML",
244-
location=NamespacePathLocation(),
238+
location=UnknownProblemLocation(),
245239
severity=ProblemSeverity.WARNING,
246-
problem=f"Failed to parse {xml_file}: {str(e)}"
240+
problem=f"Failed to parse {xml_file.name}: {str(e)}"
247241
))
248242

249243
return problems
250244

251245
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"""
253247
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,
256250
}
257251

258252
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"\nSkipping {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)
271256
if problems:
272257
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)
283262

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
288302

289303
if __name__ == "__main__":
290304
default_matter_test_main()

0 commit comments

Comments
 (0)