From 85b86e4899916f90fed7b87cc6530f968d32a092 Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Mon, 29 Jul 2024 23:37:01 -0400 Subject: [PATCH] Add TC_ECOINFO_2_1.py test implementation (#34564) * Add TC_ECOINFO_2_1.py test implementation * Quick fix * Restyled by autopep8 * Fix lint and minor test plan update * add default_matter_test_main * Address PR comments * Restyled by autopep8 --------- Co-authored-by: Restyled.io --- src/python_testing/TC_ECOINFO_2_1.py | 190 +++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/python_testing/TC_ECOINFO_2_1.py diff --git a/src/python_testing/TC_ECOINFO_2_1.py b/src/python_testing/TC_ECOINFO_2_1.py new file mode 100644 index 00000000000000..ea9ee43b7f0569 --- /dev/null +++ b/src/python_testing/TC_ECOINFO_2_1.py @@ -0,0 +1,190 @@ +# +# 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 chip.clusters as Clusters +from chip.clusters.Types import NullValue +from chip.interaction_model import Status +from chip.tlv import uint +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main, type_matches +from mobly import asserts + + +class TC_ECOINFO_2_1(MatterBaseTest): + + def _validate_device_directory(self, is_removed_on_null, device_directory): + num_of_devices = len(device_directory) + if is_removed_on_null: + asserts.assert_less_equal(num_of_devices, 256, "Too many device entries") + for device in device_directory: + # TODO do fabric index check first + if device.deviceName is not None: + asserts.assert_true(type_matches(device.deviceName, str), "DeviceName should be a string") + asserts.assert_less_equal(len(device.deviceName), 64, "DeviceName should be <= 64") + asserts.assert_true(type_matches(device.deviceNameLastEdit, uint), "DeviceNameLastEdit should be a uint") + asserts.assert_greater(device.deviceNameLastEdit, 0, "DeviceNameLastEdit must be greater than 0") + else: + asserts.assert_true(device.deviceNameLastEdit is None, + "DeviceNameLastEdit should not be provided when there is no DeviceName") + + asserts.assert_true(type_matches(device.bridgedEndpoint, uint), "BridgedEndpoint should be a uint") + asserts.assert_greater_equal(device.bridgedEndpoint, 0, "BridgedEndpoint >= 0") + asserts.assert_less_equal(device.bridgedEndpoint, 0xffff_ffff, + "BridgedEndpoint less than or equal to Invalid Endpoint value") + + asserts.assert_true(type_matches(device.originalEndpoint, uint), "OriginalEndpoint should be a uint") + asserts.assert_greater_equal(device.originalEndpoint, 0, "OriginalEndpoint >= 0") + asserts.assert_less(device.originalEndpoint, 0xffff_ffff, + "OriginalEndpoint less than or equal to Invalid Endpoint value") + + asserts.assert_true(type_matches(device.deviceTypes, list), "DeviceTypes should be a list") + asserts.assert_greater_equal(len(device.deviceTypes), 1, "DeviceTypes list must contains at least one entry") + for device_type in device.deviceTypes: + asserts.assert_true(type_matches(device_type.deviceType, uint), "DeviceType should be a uint") + # TODO what other validation can we do here to device_type.deviceType + asserts.assert_true(type_matches(device_type.revision, uint), "device type's revision should be a uint") + asserts.assert_greater_equal(device_type.revision, 1, "device type's revision must >= 1") + + asserts.assert_true(type_matches(device.uniqueLocationIDs, list), "UniqueLocationIds should be a list") + num_of_unique_location_ids = len(device.uniqueLocationIDs) + asserts.assert_less_equal(num_of_unique_location_ids, 64, "UniqueLocationIds list should be <= 64") + for location_id in device.uniqueLocationIDs: + asserts.assert_true(type_matches(location_id, str), "UniqueLocationId should be a string") + location_id_string_length = len(location_id) + asserts.assert_greater_equal(location_id_string_length, 1, + "UniqueLocationId must contain at least one character") + asserts.assert_less_equal(location_id_string_length, 64, "UniqueLocationId should be <= 64") + + asserts.assert_true(type_matches(device.uniqueLocationIDsLastEdit, uint), + "UniqueLocationIdsLastEdit should be a uint") + if num_of_unique_location_ids: + asserts.assert_greater(device.uniqueLocationIDsLastEdit, 0, "UniqueLocationIdsLastEdit must be non-zero") + else: + asserts.assert_equal(num_of_devices, 0, "Device was removed, there should be no devices in DeviceDirectory") + + def _validate_location_directory(self, is_removed_on_null, location_directory): + num_of_locations = len(location_directory) + if is_removed_on_null: + asserts.assert_less_equal(num_of_locations, 64, "Too many location entries") + for location in location_directory: + asserts.assert_true(type_matches(location.uniqueLocationID, str), "UniqueLocationId should be a string") + location_id_string_length = len(location.uniqueLocationID) + asserts.assert_greater_equal(location_id_string_length, 1, + "UniqueLocationId must contain at least one character") + asserts.assert_less_equal(location_id_string_length, 64, "UniqueLocationId should be <= 64") + + asserts.assert_true(type_matches(location.locationDescriptor.locationName, str), + "LocationName should be a string") + asserts.assert_less_equal(len(location.locationDescriptor.locationName), 64, "LocationName should be <= 64") + + if location.locationDescriptor.floorNumber is not NullValue: + asserts.assert_true(type_matches(location.locationDescriptor.floorNumber, int), + "FloorNumber should be an int") + # TODO check in range of int16. + + if location.locationDescriptor.areaType is not NullValue: + # TODO check areaType is valid. + pass + + asserts.assert_true(type_matches(location.locationDescriptorLastEdit, uint), + "UniqueLocationIdsLastEdit should be a uint") + asserts.assert_greater(location.locationDescriptorLastEdit, 0, "LocationDescriptorLastEdit must be non-zero") + + else: + asserts.assert_equal(num_of_locations, 0, "Device was removed, there should be no location in LocationDirectory") + + def steps_TC_ECOINFO_2_1(self) -> list[TestStep]: + steps = [TestStep(1, "Identify endpoints with Ecosystem Information Cluster", is_commissioning=True), + TestStep(2, "Reading RemovedOn Attribute"), + TestStep(3, "Reading DeviceDirectory Attribute"), + TestStep(4, "Reading LocationDirectory Attribute"), + TestStep(5, "Try Writing to RemovedOn Attribute"), + TestStep(6, "Try Writing to DeviceDirectory Attribute"), + TestStep(7, "Try Writing to LocationDirectory Attribute"), + TestStep(8, "Repeating steps 2 to 7 for each endpoint identified in step 1")] + return steps + + @async_test_body + async def test_TC_ECOINFO_2_1(self): + dev_ctrl = self.default_controller + dut_node_id = self.dut_node_id + + self.print_step(0, "Commissioning, already done") + + self.step(1) + endpoint_wild_card_read = await dev_ctrl.ReadAttribute(dut_node_id, [(Clusters.EcosystemInformation.Attributes.ClusterRevision)]) + list_of_endpoints = list(endpoint_wild_card_read.keys()) + + for idx, cluster_endpoint in enumerate(list_of_endpoints): + if idx == 0: + self.step(2) + removed_on = await self.read_single_attribute( + dev_ctrl, + dut_node_id, + endpoint=cluster_endpoint, + attribute=Clusters.EcosystemInformation.Attributes.RemovedOn) + + is_removed_on_null = removed_on is NullValue + if not is_removed_on_null: + asserts.assert_true(type_matches(removed_on, uint)) + asserts.assert_greater(removed_on, 0, "RemovedOn must be greater than 0", "RemovedOn should be a uint") + + if idx == 0: + self.step(3) + device_directory = await self.read_single_attribute( + dev_ctrl, + dut_node_id, + endpoint=cluster_endpoint, + attribute=Clusters.EcosystemInformation.Attributes.DeviceDirectory, + fabricFiltered=False) + + self._validate_device_directory(is_removed_on_null, device_directory) + + if idx == 0: + self.step(4) + location_directory = await self.read_single_attribute( + dev_ctrl, + dut_node_id, + endpoint=cluster_endpoint, + attribute=Clusters.EcosystemInformation.Attributes.LocationDirectory, + fabricFiltered=False) + + self._validate_location_directory(is_removed_on_null, location_directory) + + if idx == 0: + self.step(5) + result = await dev_ctrl.WriteAttribute(dut_node_id, [(cluster_endpoint, Clusters.EcosystemInformation.Attributes.RemovedOn(2))]) + asserts.assert_equal(len(result), 1, "Expecting only one result from trying to write to RemovedOn Attribute") + asserts.assert_equal(result[0].Status, Status.UnsupportedWrite, "Expecting Status of UnsupportedWrite") + + if idx == 0: + self.step(6) + result = await dev_ctrl.WriteAttribute(dut_node_id, [(cluster_endpoint, Clusters.EcosystemInformation.Attributes.DeviceDirectory([]))]) + asserts.assert_equal(len(result), 1, "Expecting only one result from trying to write to DeviceDirectory Attribute") + asserts.assert_equal(result[0].Status, Status.UnsupportedWrite, "Expecting Status of UnsupportedWrite") + + if idx == 0: + self.step(7) + result = await dev_ctrl.WriteAttribute(dut_node_id, [(cluster_endpoint, Clusters.EcosystemInformation.Attributes.DeviceDirectory([]))]) + asserts.assert_equal(len(result), 1, "Expecting only one result from trying to write to LocationDirectory Attribute") + asserts.assert_equal(result[0].Status, Status.UnsupportedWrite, "Expecting Status of UnsupportedWrite") + + if idx == 0: + self.step(8) + + +if __name__ == "__main__": + default_matter_test_main()