From 6cd72e85e36d5454837bc82e1d109a685dde2a3f Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Tue, 3 Dec 2024 12:46:12 +0100 Subject: [PATCH 01/10] implement TC_TCTL_2_3 in Python --- src/python_testing/TC_TCTL_2_3.py | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/python_testing/TC_TCTL_2_3.py diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py new file mode 100644 index 00000000000000..34110ea043e1c7 --- /dev/null +++ b/src/python_testing/TC_TCTL_2_3.py @@ -0,0 +1,97 @@ +# +# 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. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: +# run1: +# app: ${ALL_CLUSTERS_APP} +# app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# script-args: > +# --storage-path admin_storage.json +# --commissioning-method on-network +# --discriminator 1234 +# --passcode 20202021 +# --trace-to json:${TRACE_TEST_JSON}.json +# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# --PICS src/app/tests/suites/certification/ci-pics-values +# factory-reset: true +# quiet: true +# === END CI TEST ARGUMENTS === + +import chip.clusters as Clusters +from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_TCTL_2_3(MatterBaseTest): + def desc_TC_TCTL_2_3(self) -> str: + return "[TC-TCTL-2.3] Optional temperature level attributes with DUT as Server" + + def pics_TC_TCTL_2_3(self): + """Return the PICS definitions associated with this test.""" + pics = [ + "TCTL.S", # Temperature Control as a Server + "TCTL.S.F01", # Does a device support temperature level feature + ] + return pics + + def steps_TC_TCTL_2_3(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "TH reads from the DUT the SelectedTemperatureLevel attribute"), + TestStep(3, "TH reads from the DUT the SupportedTemperatureLevels attribute and verifies string lengths"), + ] + return steps + + @async_test_body + async def test_TC_TCTL_2_3(self): + self.step(1) + + # Step 2: Read SelectedTemperatureLevel attribute + self.step(2) + if self.check_pics("TCTL.S.A0004"): # SelectedTemperatureLevel attribute + selected_temp = await self.default_controller.ReadAttribute( + nodeid=self.dut_node_id, + attributes=[(1, Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel)] + ) + temp_level = selected_temp[1][Clusters.TemperatureControl][Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel] + asserts.assert_true(0 <= temp_level <= 31, + f"SelectedTemperatureLevel {temp_level} is out of range [0-31]") + + # Step 3: Read SupportedTemperatureLevels attribute + self.step(3) + if self.check_pics("TCTL.S.A0004"): # SupportedTemperatureLevels attribute + supported_temps = await self.default_controller.ReadAttribute( + nodeid=self.dut_node_id, + attributes=[(1, Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels)] + ) + + temp_levels = supported_temps[1][Clusters.TemperatureControl][Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels] + asserts.assert_true(isinstance(temp_levels, list), + "SupportedTemperatureLevels should be a list") + asserts.assert_true(len(temp_levels) <= 32, + f"SupportedTemperatureLevels list length {len(temp_levels)} exceeds maximum of 32") + + # Verify string lengths + for level in temp_levels: + asserts.assert_true(isinstance(level, str), + f"Temperature level {level} is not a string") + asserts.assert_true(len(level) <= 16, + f"Temperature level string '{level}' exceeds maximum length of 16") + + +if __name__ == "__main__": + default_matter_test_main() From 4ecf4fd5e32fc0713e6e4f5e37dd26594fbf0eb3 Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Tue, 3 Dec 2024 13:14:06 +0100 Subject: [PATCH 02/10] add test to CI workflow --- .github/workflows/tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ceee20818defd8..dc1d1d05ce1ff4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -185,6 +185,7 @@ jobs: src/app/zap-templates/zcl/data-model/chip/software-diagnostics-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/switch-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/target-navigator-cluster.xml \ + src/app/zap-templates/zcl/data-model/chip/temperature-control-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/temperature-measurement-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/test-cluster.xml \ src/app/zap-templates/zcl/data-model/chip/thermostat-user-interface-configuration-cluster.xml \ @@ -534,6 +535,7 @@ jobs: scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_ICDM_2_1.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_SC_7_1.py' + scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_TCTL_2_3.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/TestDecorators.py' From 9a4b219bd2cbdc6f3008f59b78151c694bed4389 Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Tue, 3 Dec 2024 13:17:33 +0100 Subject: [PATCH 03/10] revert incorrect path addition, leave only xml parsing check --- .github/workflows/tests.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index dc1d1d05ce1ff4..78e4c55c56b965 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -535,7 +535,6 @@ jobs: scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_ICDM_2_1.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_SC_7_1.py' - scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_TCTL_2_3.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/TestDecorators.py' From 71708d1601792369c8761cb8c34396048e76317a Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Tue, 3 Dec 2024 13:27:46 +0100 Subject: [PATCH 04/10] fix typo --- src/python_testing/TC_TCTL_2_3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index 34110ea043e1c7..f9eaf6e4c65ff2 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -73,7 +73,7 @@ async def test_TC_TCTL_2_3(self): # Step 3: Read SupportedTemperatureLevels attribute self.step(3) - if self.check_pics("TCTL.S.A0004"): # SupportedTemperatureLevels attribute + if self.check_pics("TCTL.S.A0005"): # SupportedTemperatureLevels attribute supported_temps = await self.default_controller.ReadAttribute( nodeid=self.dut_node_id, attributes=[(1, Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels)] From cb1fdfcdc2de03bc63d9fbd8753d35386e618baa Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Thu, 5 Dec 2024 14:09:57 +0100 Subject: [PATCH 05/10] fix code review comment: rework reading attribute using read_single_attribute_check_success() --- src/python_testing/TC_TCTL_2_3.py | 35 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index f9eaf6e4c65ff2..42fc492255ece9 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -63,32 +63,29 @@ async def test_TC_TCTL_2_3(self): # Step 2: Read SelectedTemperatureLevel attribute self.step(2) if self.check_pics("TCTL.S.A0004"): # SelectedTemperatureLevel attribute - selected_temp = await self.default_controller.ReadAttribute( - nodeid=self.dut_node_id, - attributes=[(1, Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel)] + selected_temp = await self.read_single_attribute_check_success( + cluster=Clusters.Objects.TemperatureControl, + attribute=Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel ) - temp_level = selected_temp[1][Clusters.TemperatureControl][Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel] - asserts.assert_true(0 <= temp_level <= 31, - f"SelectedTemperatureLevel {temp_level} is out of range [0-31]") + asserts.assert_true(0 <= selected_temp <= 31, + f"SelectedTemperatureLevel {selected_temp} is out of range [0-31]") # Step 3: Read SupportedTemperatureLevels attribute self.step(3) if self.check_pics("TCTL.S.A0005"): # SupportedTemperatureLevels attribute - supported_temps = await self.default_controller.ReadAttribute( - nodeid=self.dut_node_id, - attributes=[(1, Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels)] + supported_temps = await self.read_single_attribute_check_success( + cluster=Clusters.Objects.TemperatureControl, + attribute=Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels ) + asserts.assert_true(isinstance(supported_temps, list), + "SupportedTemperatureLevels should be a list") + asserts.assert_true(len(supported_temps) <= 32, + f"SupportedTemperatureLevels list length {len(supported_temps)} exceeds maximum of 32") - temp_levels = supported_temps[1][Clusters.TemperatureControl][Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels] - asserts.assert_true(isinstance(temp_levels, list), - "SupportedTemperatureLevels should be a list") - asserts.assert_true(len(temp_levels) <= 32, - f"SupportedTemperatureLevels list length {len(temp_levels)} exceeds maximum of 32") - - # Verify string lengths - for level in temp_levels: - asserts.assert_true(isinstance(level, str), - f"Temperature level {level} is not a string") + # Verify string lengths + for level in supported_temps: + asserts.assert_true(isinstance(level, str), + f"Temperature level {level} is not a string") asserts.assert_true(len(level) <= 16, f"Temperature level string '{level}' exceeds maximum length of 16") From 8e0e7b43ac52249258c1085db4cb7905824b4f3d Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Wed, 18 Dec 2024 17:43:28 +0100 Subject: [PATCH 06/10] use run_if_endpoint_matches decorator and use attribute guards --- src/python_testing/TC_TCTL_2_3.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index 42fc492255ece9..0739f6261ab6fd 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -24,6 +24,7 @@ # --commissioning-method on-network # --discriminator 1234 # --passcode 20202021 +# --endpoint 1 # --trace-to json:${TRACE_TEST_JSON}.json # --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto # --PICS src/app/tests/suites/certification/ci-pics-values @@ -32,7 +33,7 @@ # === END CI TEST ARGUMENTS === import chip.clusters as Clusters -from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from chip.testing.matter_testing import MatterBaseTest, TestStep, default_matter_test_main, has_feature, run_if_endpoint_matches from mobly import asserts @@ -56,13 +57,16 @@ def steps_TC_TCTL_2_3(self) -> list[TestStep]: ] return steps - @async_test_body + @run_if_endpoint_matches(has_feature(Clusters.TemperatureControl, Clusters.TemperatureControl.Bitmaps.Feature.kTemperatureLevel)) async def test_TC_TCTL_2_3(self): + self.endpoint = self.get_endpoint() + cluster = Clusters.TemperatureControl + attributes = cluster.Attributes self.step(1) # Step 2: Read SelectedTemperatureLevel attribute self.step(2) - if self.check_pics("TCTL.S.A0004"): # SelectedTemperatureLevel attribute + if self.attribute_guard(endpoint=self.endpoint, attribute=attributes.SelectedTemperatureLevel): selected_temp = await self.read_single_attribute_check_success( cluster=Clusters.Objects.TemperatureControl, attribute=Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel @@ -72,7 +76,7 @@ async def test_TC_TCTL_2_3(self): # Step 3: Read SupportedTemperatureLevels attribute self.step(3) - if self.check_pics("TCTL.S.A0005"): # SupportedTemperatureLevels attribute + if self.attribute_guard(endpoint=self.endpoint, attribute=attributes.SupportedTemperatureLevels): supported_temps = await self.read_single_attribute_check_success( cluster=Clusters.Objects.TemperatureControl, attribute=Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels From 1a8d6b262105590e3da72d74d62f3599ed36360c Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Wed, 18 Dec 2024 17:52:25 +0100 Subject: [PATCH 07/10] reuse cluster variable --- src/python_testing/TC_TCTL_2_3.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index 0739f6261ab6fd..7b6ed6a33dd8fe 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -68,8 +68,8 @@ async def test_TC_TCTL_2_3(self): self.step(2) if self.attribute_guard(endpoint=self.endpoint, attribute=attributes.SelectedTemperatureLevel): selected_temp = await self.read_single_attribute_check_success( - cluster=Clusters.Objects.TemperatureControl, - attribute=Clusters.TemperatureControl.Attributes.SelectedTemperatureLevel + cluster=cluster, + attribute=cluster.Attributes.SelectedTemperatureLevel ) asserts.assert_true(0 <= selected_temp <= 31, f"SelectedTemperatureLevel {selected_temp} is out of range [0-31]") @@ -78,8 +78,8 @@ async def test_TC_TCTL_2_3(self): self.step(3) if self.attribute_guard(endpoint=self.endpoint, attribute=attributes.SupportedTemperatureLevels): supported_temps = await self.read_single_attribute_check_success( - cluster=Clusters.Objects.TemperatureControl, - attribute=Clusters.TemperatureControl.Attributes.SupportedTemperatureLevels + cluster=cluster, + attribute=cluster.Attributes.SupportedTemperatureLevels ) asserts.assert_true(isinstance(supported_temps, list), "SupportedTemperatureLevels should be a list") From f80b10d5b5ce70490b8c2e010515b6c0686dcd3a Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Thu, 19 Dec 2024 15:40:42 +0100 Subject: [PATCH 08/10] address code review comments --- src/python_testing/TC_TCTL_2_3.py | 51 ++++++++++++++++--------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index 7b6ed6a33dd8fe..bf9359498694f5 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -52,8 +52,13 @@ def pics_TC_TCTL_2_3(self): def steps_TC_TCTL_2_3(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "TH reads from the DUT the SelectedTemperatureLevel attribute"), - TestStep(3, "TH reads from the DUT the SupportedTemperatureLevels attribute and verifies string lengths"), + TestStep(2, "TH reads from the DUT the SelectedTemperatureLevel attribute", + "Verify that the DUT response contains the value of _SelectedTemperatureLevel_ with a range of 0 to 31"), + TestStep(3, "TH reads from the DUT the SupportedTemperatureLevels attribute and verifies string lengths", + ("Verify that the DUT response contains a _SupportedTemperatureLevels_ list\n\n" + "* List length hast to be equal or less than 32 \n" + "* Each temperature level should be a string\n" + "* Length of each temperature level string should be equal or less than 16\n")), ] return steps @@ -66,32 +71,30 @@ async def test_TC_TCTL_2_3(self): # Step 2: Read SelectedTemperatureLevel attribute self.step(2) - if self.attribute_guard(endpoint=self.endpoint, attribute=attributes.SelectedTemperatureLevel): - selected_temp = await self.read_single_attribute_check_success( - cluster=cluster, - attribute=cluster.Attributes.SelectedTemperatureLevel - ) - asserts.assert_true(0 <= selected_temp <= 31, - f"SelectedTemperatureLevel {selected_temp} is out of range [0-31]") + selected_temp = await self.read_single_attribute_check_success( + cluster=cluster, + attribute=attributes.SelectedTemperatureLevel + ) + asserts.assert_true(0 <= selected_temp <= 31, + f"SelectedTemperatureLevel {selected_temp} is out of range [0-31]") # Step 3: Read SupportedTemperatureLevels attribute self.step(3) - if self.attribute_guard(endpoint=self.endpoint, attribute=attributes.SupportedTemperatureLevels): - supported_temps = await self.read_single_attribute_check_success( - cluster=cluster, - attribute=cluster.Attributes.SupportedTemperatureLevels - ) - asserts.assert_true(isinstance(supported_temps, list), - "SupportedTemperatureLevels should be a list") - asserts.assert_true(len(supported_temps) <= 32, - f"SupportedTemperatureLevels list length {len(supported_temps)} exceeds maximum of 32") + supported_temps = await self.read_single_attribute_check_success( + cluster=cluster, + attribute=attributes.SupportedTemperatureLevels + ) + asserts.assert_true(isinstance(supported_temps, list), + "SupportedTemperatureLevels should be a list") + asserts.assert_true(len(supported_temps) <= 32, + f"SupportedTemperatureLevels list length {len(supported_temps)} exceeds maximum of 32") - # Verify string lengths - for level in supported_temps: - asserts.assert_true(isinstance(level, str), - f"Temperature level {level} is not a string") - asserts.assert_true(len(level) <= 16, - f"Temperature level string '{level}' exceeds maximum length of 16") + # Verify string lengths + for level in supported_temps: + asserts.assert_true(isinstance(level, str), + f"Temperature level {level} is not a string") + asserts.assert_true(len(level) <= 16, + f"Temperature level string '{level}' exceeds maximum length of 16") if __name__ == "__main__": From 0daf2a19ac534a9a29f1ed221f92d5212463fa94 Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Thu, 19 Dec 2024 15:41:41 +0100 Subject: [PATCH 09/10] fix typos --- src/python_testing/TC_TCTL_2_3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index bf9359498694f5..4a1d49c43974eb 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -56,9 +56,9 @@ def steps_TC_TCTL_2_3(self) -> list[TestStep]: "Verify that the DUT response contains the value of _SelectedTemperatureLevel_ with a range of 0 to 31"), TestStep(3, "TH reads from the DUT the SupportedTemperatureLevels attribute and verifies string lengths", ("Verify that the DUT response contains a _SupportedTemperatureLevels_ list\n\n" - "* List length hast to be equal or less than 32 \n" + "* List length has to be equal or less than 32 \n" "* Each temperature level should be a string\n" - "* Length of each temperature level string should be equal or less than 16\n")), + "* Length of each temperature level string has to be equal or less than 16\n")), ] return steps From c325a1e27cc5fbff26201aaf5e7badba7bc35f07 Mon Sep 17 00:00:00 2001 From: Oleksandr Sirko <wisenss@gmail.com> Date: Wed, 8 Jan 2025 15:21:39 +0100 Subject: [PATCH 10/10] fix loop issue found in review --- src/python_testing/TC_TCTL_2_3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_TCTL_2_3.py b/src/python_testing/TC_TCTL_2_3.py index 4a1d49c43974eb..6bc4556a483895 100644 --- a/src/python_testing/TC_TCTL_2_3.py +++ b/src/python_testing/TC_TCTL_2_3.py @@ -93,8 +93,8 @@ async def test_TC_TCTL_2_3(self): for level in supported_temps: asserts.assert_true(isinstance(level, str), f"Temperature level {level} is not a string") - asserts.assert_true(len(level) <= 16, - f"Temperature level string '{level}' exceeds maximum length of 16") + asserts.assert_true(len(level) <= 16, + f"Temperature level string '{level}' exceeds maximum length of 16") if __name__ == "__main__":