Python testing: move PICS functions (#33759)
* Python testing: move PICS functions
We're going to need these for some new tooling, so moving them
into their own file to avoid needing to pull in the entirety
of matter_testing_support
Only changes are moves and function renames.
test: TC_pics_checker.py
* Restyled by autopep8
* Restyled by isort
* fix imports
* Hey, I wrote a unit test
Didn't update it though because I forgot it existed. Thanks, CI.
It passes now.
* Restyled by isort
---------
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/src/python_testing/TC_pics_checker.py b/src/python_testing/TC_pics_checker.py
index 8710719..4e503ed 100644
--- a/src/python_testing/TC_pics_checker.py
+++ b/src/python_testing/TC_pics_checker.py
@@ -22,25 +22,10 @@
from matter_testing_support import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, FeaturePathLocation,
MatterBaseTest, TestStep, async_test_body, default_matter_test_main)
from mobly import asserts
+from pics_support import accepted_cmd_pics_str, attribute_pics_str, feature_pics_str, generated_cmd_pics_str
from spec_parsing_support import build_xml_clusters
-def attribute_pics(pics_base: str, id: int) -> str:
- return f'{pics_base}.S.A{id:04x}'
-
-
-def accepted_cmd_pics(pics_base: str, id: int) -> str:
- return f'{pics_base}.S.C{id:02x}.Rsp'
-
-
-def generated_cmd_pics(pics_base: str, id: int) -> str:
- return f'{pics_base}.S.C{id:02x}.Tx'
-
-
-def feature_pics(pics_base: str, bit: int) -> str:
- return f'{pics_base}.S.F{bit:02x}'
-
-
class TC_PICS_Checker(MatterBaseTest, BasicCompositionTests):
@async_test_body
async def setup_class(self):
@@ -64,14 +49,14 @@
try:
if attribute_id_of_element_list == GlobalAttributeIds.ATTRIBUTE_LIST_ID:
all_spec_elements_to_check = Clusters.ClusterObjects.ALL_ATTRIBUTES[cluster_id]
- pics_mapper = attribute_pics
+ pics_mapper = attribute_pics_str
elif attribute_id_of_element_list == GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID:
all_spec_elements_to_check = Clusters.ClusterObjects.ALL_ACCEPTED_COMMANDS[cluster_id]
- pics_mapper = accepted_cmd_pics
+ pics_mapper = accepted_cmd_pics_str
elif attribute_id_of_element_list == GlobalAttributeIds.GENERATED_COMMAND_LIST_ID:
all_spec_elements_to_check = Clusters.ClusterObjects.ALL_GENERATED_COMMANDS[cluster_id]
- pics_mapper = generated_cmd_pics
+ pics_mapper = generated_cmd_pics_str
else:
asserts.fail("add_pics_for_list function called for non-list attribute")
except KeyError:
@@ -177,7 +162,7 @@
self.record_warning("PICS check", location=location,
problem=f"Unable to parse feature mask {feature_mask} from cluster {cluster}")
continue
- pics = feature_pics(pics_base, feature_bit)
+ pics = feature_pics_str(pics_base, feature_bit)
if feature_mask & feature_map:
required = True
else:
diff --git a/src/python_testing/TestMatterTestingSupport.py b/src/python_testing/TestMatterTestingSupport.py
index 56f105b..eba9dc4 100644
--- a/src/python_testing/TestMatterTestingSupport.py
+++ b/src/python_testing/TestMatterTestingSupport.py
@@ -24,9 +24,9 @@
from chip.clusters.Types import Nullable, NullValue
from chip.tlv import uint
from matter_testing_support import (MatterBaseTest, async_test_body, compare_time, default_matter_test_main,
- get_wait_seconds_from_set_time, parse_pics, parse_pics_xml, type_matches,
- utc_time_in_matter_epoch)
+ get_wait_seconds_from_set_time, type_matches, utc_time_in_matter_epoch)
from mobly import asserts, signals
+from pics_support import parse_pics, parse_pics_xml
from taglist_and_topology_test_support import (TagProblem, create_device_type_list_for_root, create_device_type_lists,
find_tag_list_problems, find_tree_roots, flat_list_ok, get_all_children,
get_direct_children_of_root, parts_list_cycles, separate_endpoint_types)
diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py
index 4861cae..c342d87 100644
--- a/src/python_testing/matter_testing_support.py
+++ b/src/python_testing/matter_testing_support.py
@@ -18,7 +18,6 @@
import argparse
import asyncio
import builtins
-import glob
import inspect
import json
import logging
@@ -30,7 +29,6 @@
import sys
import typing
import uuid
-import xml.etree.ElementTree as ET
from binascii import hexlify, unhexlify
from dataclasses import asdict as dataclass_asdict
from dataclasses import dataclass, field
@@ -64,6 +62,7 @@
from mobly import asserts, base_test, signals, utils
from mobly.config_parser import ENV_MOBLY_LOGPATH, TestRunConfig
from mobly.test_runner import TestRunner
+from pics_support import read_pics_from_file
try:
from matter_yamltests.hooks import TestRunnerHooks
@@ -142,50 +141,6 @@
return pathlib.Path.cwd()
-def parse_pics(lines: typing.List[str]) -> dict[str, bool]:
- pics = {}
- for raw in lines:
- line, _, _ = raw.partition("#")
- line = line.strip()
-
- if not line:
- continue
-
- key, _, val = line.partition("=")
- val = val.strip()
- if val not in ["1", "0"]:
- raise ValueError('PICS {} must have a value of 0 or 1'.format(key))
-
- pics[key.strip()] = (val == "1")
- return pics
-
-
-def parse_pics_xml(contents: str) -> dict[str, bool]:
- pics = {}
- mytree = ET.fromstring(contents)
- for pi in mytree.iter('picsItem'):
- name = pi.find('itemNumber').text
- support = pi.find('support').text
- pics[name] = int(json.loads(support.lower())) == 1
- return pics
-
-
-def read_pics_from_file(path: str) -> dict[str, bool]:
- """ Reads a dictionary of PICS from a file (ci format) or directory (xml format). """
- if os.path.isdir(os.path.abspath(path)):
- pics_dict = {}
- for filename in glob.glob(f'{path}/*.xml'):
- with open(filename, 'r') as f:
- contents = f.read()
- pics_dict.update(parse_pics_xml(contents))
- return pics_dict
-
- else:
- with open(path, 'r') as f:
- lines = f.readlines()
- return parse_pics(lines)
-
-
def type_matches(received_value, desired_type):
""" Checks if the value received matches the expected type.
diff --git a/src/python_testing/pics_support.py b/src/python_testing/pics_support.py
new file mode 100644
index 0000000..62e04f7
--- /dev/null
+++ b/src/python_testing/pics_support.py
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2024 Project CHIP Authors
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import glob
+import json
+import os
+import typing
+import xml.etree.ElementTree as ET
+
+
+def attribute_pics_str(pics_base: str, id: int) -> str:
+ return f'{pics_base}.S.A{id:04x}'
+
+
+def accepted_cmd_pics_str(pics_base: str, id: int) -> str:
+ return f'{pics_base}.S.C{id:02x}.Rsp'
+
+
+def generated_cmd_pics_str(pics_base: str, id: int) -> str:
+ return f'{pics_base}.S.C{id:02x}.Tx'
+
+
+def feature_pics_str(pics_base: str, bit: int) -> str:
+ return f'{pics_base}.S.F{bit:02x}'
+
+
+def server_pics_str(pics_base: str) -> str:
+ return f'{pics_base}.S'
+
+
+def client_pics_str(pics_base: str) -> str:
+ return f'{pics_base}.C'
+
+
+def parse_pics(lines: typing.List[str]) -> dict[str, bool]:
+ pics = {}
+ for raw in lines:
+ line, _, _ = raw.partition("#")
+ line = line.strip()
+
+ if not line:
+ continue
+
+ key, _, val = line.partition("=")
+ val = val.strip()
+ if val not in ["1", "0"]:
+ raise ValueError('PICS {} must have a value of 0 or 1'.format(key))
+
+ pics[key.strip()] = (val == "1")
+ return pics
+
+
+def parse_pics_xml(contents: str) -> dict[str, bool]:
+ pics = {}
+ mytree = ET.fromstring(contents)
+ for pi in mytree.iter('picsItem'):
+ name = pi.find('itemNumber').text
+ support = pi.find('support').text
+ pics[name] = int(json.loads(support.lower())) == 1
+ return pics
+
+
+def read_pics_from_file(path: str) -> dict[str, bool]:
+ """ Reads a dictionary of PICS from a file (ci format) or directory (xml format). """
+ if os.path.isdir(os.path.abspath(path)):
+ pics_dict = {}
+ for filename in glob.glob(f'{path}/*.xml'):
+ with open(filename, 'r') as f:
+ contents = f.read()
+ pics_dict.update(parse_pics_xml(contents))
+ return pics_dict
+
+ else:
+ with open(path, 'r') as f:
+ lines = f.readlines()
+ return parse_pics(lines)