From 640286bb844fbc1280d3492793fec8212520fd35 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Wed, 15 May 2024 13:20:26 -0700 Subject: [PATCH 01/20] Initial commit --- src/python_testing/TC_IDM_4_3.py | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/python_testing/TC_IDM_4_3.py diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py new file mode 100644 index 00000000000000..82273937de0917 --- /dev/null +++ b/src/python_testing/TC_IDM_4_3.py @@ -0,0 +1,54 @@ +# +# Copyright (c) 2023 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 copy +import logging +import queue +import time + +import chip.clusters as Clusters +from chip.ChipDeviceCtrl import ChipDeviceController +from chip.clusters import ClusterObjects as ClusterObjects +from chip.clusters.Attribute import AttributePath, TypedAttributePath +from chip.exceptions import ChipStackError +from chip.interaction_model import Status +from matter_testing_support import (AttributeChangeCallback, MatterBaseTest, async_test_body, default_matter_test_main, + wait_for_attribute_report) +from mobly import asserts + +''' +Category: +Functional + +Description: +This test case will verify the report data messages sent from the DUT after activating +subscription are according to specification. + +Full test plan link for details: +https://github.com/CHIP-Specifications/chip-test-plans/blob/master/src/interactiondatamodel.adoc#443-tc-idm-43-report-data-messages-post-subscription-activation-from-dut-test-cases-dut_server +''' + + +class TC_IDM_4_3(MatterBaseTest): + + @async_test_body + async def test_TC_IDM_4_3(self): + print() + + +if __name__ == "__main__": + default_matter_test_main() From 68f3b6a3f077a48f049789b1243de1c5ff84372e Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Thu, 16 May 2024 12:17:29 -0700 Subject: [PATCH 02/20] progress --- src/python_testing/TC_IDM_4_3.py | 49 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 82273937de0917..4af02ff5229d53 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -26,8 +26,9 @@ from chip.clusters.Attribute import AttributePath, TypedAttributePath from chip.exceptions import ChipStackError from chip.interaction_model import Status -from matter_testing_support import (AttributeChangeCallback, MatterBaseTest, async_test_body, default_matter_test_main, - wait_for_attribute_report) +from matter_testing_support import (MatterBaseTest, async_test_body, default_matter_test_main) +# from matter_testing_support import (AttributeChangeCallback, MatterBaseTest, async_test_body, default_matter_test_main, +# wait_for_attribute_report) from mobly import asserts ''' @@ -47,7 +48,49 @@ class TC_IDM_4_3(MatterBaseTest): @async_test_body async def test_TC_IDM_4_3(self): - print() + + # Test setup + node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel + node_label_attr_path = [(0, node_label_attr)] + TH: ChipDeviceController = self.default_controller + + # *** Step 1a *** + self.print_step("1a", "DUT and TH activate the subscription.") + + # Subscribe to attribute + sub_th_step1a = await TH.ReadAttribute( + nodeid=self.dut_node_id, + attributes=node_label_attr_path, + reportInterval=(3, 10), + keepSubscriptions=False + ) + + # Verify that the subscription is activated between TH and DUT + # Verify on the TH, a report data message is received. + asserts.assert_true(sub_th_step1a.subscriptionId, "Subscription not activated") + + # Verify subscriptionId field is present + asserts.assert_is_not_none(sub_th_step1a.subscriptionId, "SubscriptionId field not present") + + # Verify MaxInterval field is present + sub_th_step1a_intervals = sub_th_step1a.GetReportingIntervalsSeconds() + sub_th_step1a_min_interval_sec, sub_th_step1a_max_interval_sec = sub_th_step1a_intervals + asserts.assert_is_not_none(sub_th_step1a_max_interval_sec, "MaxInterval field not present") + + sub_th_step1a.Shutdown() + + # *** Step 1b *** + self.print_step("1b", "Change the value of the attribute which has been subscribed on the DUT by manually changing some settings on the device.") + + # Modify attribute value + new_node_label_write = "NewNodeLabel_11001100" + await TH.WriteAttribute( + self.dut_node_id, + [(0, node_label_attr(value=new_node_label_write))] + ) + + + if __name__ == "__main__": From 8356a270280d902a14ee70cdbe518ca6f910590f Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Tue, 21 May 2024 09:47:47 -0700 Subject: [PATCH 03/20] Updates steps to step array --- src/python_testing/TC_IDM_4_3.py | 62 ++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 4af02ff5229d53..e717cf41be66e0 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -26,7 +26,7 @@ from chip.clusters.Attribute import AttributePath, TypedAttributePath from chip.exceptions import ChipStackError from chip.interaction_model import Status -from matter_testing_support import (MatterBaseTest, async_test_body, default_matter_test_main) +from matter_testing_support import (MatterBaseTest, TestStep, async_test_body, default_matter_test_main) # from matter_testing_support import (AttributeChangeCallback, MatterBaseTest, async_test_body, default_matter_test_main, # wait_for_attribute_report) from mobly import asserts @@ -46,6 +46,53 @@ class TC_IDM_4_3(MatterBaseTest): + def steps_TC_IDM_4_3(self): + return [TestStep("1a", "DUT and TH activate the subscription.", + "Verify on the TH, a report data message is received. Verify on the TH the Subscribe Response has the following fields: SubscriptionId and MaxInterval In the following Steps 2, 3, 5-10, 13, and 15, the MaxInterval time reference in each step is the MaxInterval presented in the Subscribe Response of the subscription."), + TestStep("1b", "Change the value of the attribute which has been subscribed on the DUT by manually changing some settings on the device. Example: Temperature sensor may update the value of the room temperature. Turning on/off on a light bulb.", + "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), + # TestStep(2, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT by sending an IMWrite or Invoke message to the DUT from the TH.", + # "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), + # TestStep(3, "DUT and TH activate the subscription for an attribute. Do not change the value of the attribute which has been subscribed.", + # "Verify that there is an empty report data message sent from the DUT to the TH after MaxInterval time."), + # TestStep(4, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT. TH force sends a status response with an \"invalid subscription\". Change the value of the attribute which has been subscribed on the DUT.", + # "Verify that DUT does not send report data for the second time after the subscription has been terminated."), + # TestStep(5, "Activate the subscription between the DUT and the TH for an attribute of data type bool. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", + # "Verify on the TH that the DUT sends the correct value of the attribute."), + # TestStep(6, "Activate the subscription between the DUT and the TH for an attribute of data type string. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", + # "Verify on the TH that the DUT sends the correct value of the attribute."), + # TestStep(7, "Activate the subscription between the DUT and the TH for an attribute of data type \"unsigned integer\". Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", + # "Verify on the TH that the DUT sends the correct value of the attribute."), + # TestStep(8, "Activate the subscription between the DUT and the TH for an attribute of data type \"signed integer\". Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times)before the MaxInterval time specified during the subscription activation.", + # "Verify on the TH that the DUT sends the correct value of the attribute."), + # TestStep(9, "Activate the subscription between the DUT and the TH for an attribute of data type \"floating point\". Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", + # "Verify on the TH that the DUT sends the correct value of the attribute."), + # TestStep(10, "Activate the subscription between the DUT and the TH for an attribute of data type list. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", + # "Verify on the TH that the DUT sends the correct value of the attribute."), + # TestStep(11, "Activate the subscription between the DUT and the TH for any attribute. KeepSubscriptions flag should be set to False After the Maximum interval time is elapsed, TH should send another subscription request message with different parameters than before. KeepSubscriptions flag should be set to False Change the value of the attribute requested on the DUT.", + # "Verify that the DUT sends the changed value of the attribute with the newest subscription id sent with the second request."), + # TestStep(12, "Activate the subscription between the DUT and the TH for any attribute After the Maximum interval time is elapsed, change the value of the attribute requested on the DUT.", + # "Verify that the DUT sends the changed value of the attribute to the TH after the next MinIntervalFloor time has passed."), + # TestStep(13, "Activate the subscription between the DUT and the TH for an attribute There are no attribute value changes before MaxInterval elapses.", + # "Verify that the DUT sends a Report Data action with no data to keep the subscription alive."), + # TestStep(14, "TH sends a subscription request action for an attribute to the DUT with the KeepSubscriptions flag set to True. Activate the subscription between DUT and the TH. Initiate another subscription request action to the DUT for another attribute with the KeepSubscriptions flag set to True. Change both the attribute values on the DUT.", + # "Verify that both the subscriptions are active and the TH receives reports for both these attributes on both subscriptions."), + # TestStep(15, "TH sends a subscription request action for an attribute to the DUT with the KeepSubscriptions flag set to True. Activate the subscription between DUT and the TH. Initiate another subscription request action to the DUT for another attribute with the KeepSubscriptions flag set to False. Change both the attribute values on the DUT.", + # "Verify that both the subscriptions are active and the TH receives notifications for both these attributes. Verify that the first subscription is terminated after the MaxInterval of the first subscription is reached."), + # TestStep(16, "TH sends a subscription request action for an attribute and all events. Set the MinIntervalFloor to some value say \"N\"(seconds). Change the value of the attribute and trigger an action on the DUT to trigger any event.", + # "Verify on TH that DUT sends a report action data for both the attribute and the event after N seconds."), + # TestStep(17, "TH sends a subscription request action for attribute wildcard - AttributePath = [[Endpoint = EndpointID, Cluster = ClusterID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), + # TestStep(18, "TH sends a subscription request to subscribe to an attribute on a specific cluster from all endpoints AttributePath = [[Attribute = Attribute, Cluster = ClusterID ]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change the attribute on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), + # TestStep(19, "TH sends a subscription request to subscribe to all attributes from all clusters from all endpoints. AttributePath = [[]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), + # TestStep(20, "TH sends a subscription request to subscribe to all attributes from all clusters on an endpoint. AttributePath = [[Endpoint = EndpointID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), + # TestStep(21, "TH sends a subscription request to subscribe to all attributes from a specific cluster on all endpoints. AttributePath = [[Cluster = ClusterID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed after N seconds.") + ] + @async_test_body async def test_TC_IDM_4_3(self): @@ -55,7 +102,8 @@ async def test_TC_IDM_4_3(self): TH: ChipDeviceController = self.default_controller # *** Step 1a *** - self.print_step("1a", "DUT and TH activate the subscription.") + # DUT and TH activate the subscription. + self.step("1a") # Subscribe to attribute sub_th_step1a = await TH.ReadAttribute( @@ -64,7 +112,7 @@ async def test_TC_IDM_4_3(self): reportInterval=(3, 10), keepSubscriptions=False ) - + # Verify that the subscription is activated between TH and DUT # Verify on the TH, a report data message is received. asserts.assert_true(sub_th_step1a.subscriptionId, "Subscription not activated") @@ -73,14 +121,16 @@ async def test_TC_IDM_4_3(self): asserts.assert_is_not_none(sub_th_step1a.subscriptionId, "SubscriptionId field not present") # Verify MaxInterval field is present - sub_th_step1a_intervals = sub_th_step1a.GetReportingIntervalsSeconds() - sub_th_step1a_min_interval_sec, sub_th_step1a_max_interval_sec = sub_th_step1a_intervals + sub_th_step1a_min_interval_sec, sub_th_step1a_max_interval_sec = sub_th_step1a.GetReportingIntervalsSeconds() asserts.assert_is_not_none(sub_th_step1a_max_interval_sec, "MaxInterval field not present") sub_th_step1a.Shutdown() # *** Step 1b *** - self.print_step("1b", "Change the value of the attribute which has been subscribed on the DUT by manually changing some settings on the device.") + # Change the value of the attribute which has been subscribed on the DUT by manually changing some + # settings on the device. Example: Temperature sensor may update the value of the room temperature. + # Turning on/off on a light bulb. + self.step("1b") # Modify attribute value new_node_label_write = "NewNodeLabel_11001100" From 34b7667a7b46b3329b9f5f955ded16e5035b3fd3 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Tue, 21 May 2024 10:32:10 -0700 Subject: [PATCH 04/20] Fix restyle --- src/python_testing/TC_IDM_4_3.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index e717cf41be66e0..4a3c8105a87a63 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -26,9 +26,7 @@ from chip.clusters.Attribute import AttributePath, TypedAttributePath from chip.exceptions import ChipStackError from chip.interaction_model import Status -from matter_testing_support import (MatterBaseTest, TestStep, async_test_body, default_matter_test_main) -# from matter_testing_support import (AttributeChangeCallback, MatterBaseTest, async_test_body, default_matter_test_main, -# wait_for_attribute_report) +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts ''' @@ -138,9 +136,6 @@ async def test_TC_IDM_4_3(self): self.dut_node_id, [(0, node_label_attr(value=new_node_label_write))] ) - - - if __name__ == "__main__": From 87ce2a427c7252e91cd391441469be513bcb8d33 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Tue, 21 May 2024 16:31:48 -0700 Subject: [PATCH 05/20] progress --- src/python_testing/TC_IDM_4_3.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 4a3c8105a87a63..e4bf490d8e992e 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -18,6 +18,7 @@ import copy import logging import queue +import threading import time import chip.clusters as Clusters @@ -90,12 +91,13 @@ def steps_TC_IDM_4_3(self): # TestStep(21, "TH sends a subscription request to subscribe to all attributes from a specific cluster on all endpoints. AttributePath = [[Cluster = ClusterID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", # "Verify that the DUT sends reports for all the attributes that have changed after N seconds.") ] - + @async_test_body async def test_TC_IDM_4_3(self): # Test setup node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel + # node_label_attr = Clusters.OnOff node_label_attr_path = [(0, node_label_attr)] TH: ChipDeviceController = self.default_controller @@ -107,9 +109,20 @@ async def test_TC_IDM_4_3(self): sub_th_step1a = await TH.ReadAttribute( nodeid=self.dut_node_id, attributes=node_label_attr_path, - reportInterval=(3, 10), + reportInterval=(3, 5), keepSubscriptions=False ) + + + + + + + + + + + # Verify that the subscription is activated between TH and DUT # Verify on the TH, a report data message is received. @@ -130,12 +143,12 @@ async def test_TC_IDM_4_3(self): # Turning on/off on a light bulb. self.step("1b") - # Modify attribute value - new_node_label_write = "NewNodeLabel_11001100" - await TH.WriteAttribute( - self.dut_node_id, - [(0, node_label_attr(value=new_node_label_write))] - ) + # # Modify attribute value + # new_node_label_write = "NewNodeLabel_11001100" + # await TH.WriteAttribute( + # self.dut_node_id, + # [(0, node_label_attr(value=new_node_label_write))] + # ) if __name__ == "__main__": From 17f9d2b1b705cc3b47077381228a33d02c63f641 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Wed, 22 May 2024 10:36:14 -0700 Subject: [PATCH 06/20] Step 2 progress --- src/python_testing/TC_IDM_4_3.py | 32 +++++++++++++------- src/python_testing/matter_testing_support.py | 31 ++++++++++++++++++- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index e4bf490d8e992e..873755716da0e1 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -27,7 +27,7 @@ from chip.clusters.Attribute import AttributePath, TypedAttributePath from chip.exceptions import ChipStackError from chip.interaction_model import Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback from mobly import asserts ''' @@ -97,7 +97,6 @@ async def test_TC_IDM_4_3(self): # Test setup node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel - # node_label_attr = Clusters.OnOff node_label_attr_path = [(0, node_label_attr)] TH: ChipDeviceController = self.default_controller @@ -112,12 +111,17 @@ async def test_TC_IDM_4_3(self): reportInterval=(3, 5), keepSubscriptions=False ) - - + # secs = 60 + # print(f"\n\n\n\n\nTime to sleep {secs} second(s)") + # time.sleep(secs) + # print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") + + + @@ -135,7 +139,7 @@ async def test_TC_IDM_4_3(self): sub_th_step1a_min_interval_sec, sub_th_step1a_max_interval_sec = sub_th_step1a.GetReportingIntervalsSeconds() asserts.assert_is_not_none(sub_th_step1a_max_interval_sec, "MaxInterval field not present") - sub_th_step1a.Shutdown() + # sub_th_step1a.Shutdown() # *** Step 1b *** # Change the value of the attribute which has been subscribed on the DUT by manually changing some @@ -143,12 +147,18 @@ async def test_TC_IDM_4_3(self): # Turning on/off on a light bulb. self.step("1b") - # # Modify attribute value - # new_node_label_write = "NewNodeLabel_11001100" - # await TH.WriteAttribute( - # self.dut_node_id, - # [(0, node_label_attr(value=new_node_label_write))] - # ) + # Set Attribute Update Callback + node_label_update_cb = AttributeChangeCallback(node_label_attr) + sub_th_step1a.SetAttributeUpdateCallback(node_label_update_cb) + + # Modify attribute value + new_node_label_write = "NewNodeLabel_11001100" + await TH.WriteAttribute( + self.dut_node_id, + [(0, node_label_attr(value=new_node_label_write))] + ) + + node_label_update_cb.wait_for_report() if __name__ == "__main__": diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 4861cae7ea8c61..6a6684499e9f1a 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -54,7 +54,7 @@ from chip import discovery from chip.ChipStack import ChipStack from chip.clusters import ClusterObjects as ClusterObjects -from chip.clusters.Attribute import EventReadResult, SubscriptionTransaction +from chip.clusters.Attribute import EventReadResult, SubscriptionTransaction, TypedAttributePath from chip.exceptions import ChipStackError from chip.interaction_model import InteractionModelError, Status from chip.setup_payload import SetupPayload @@ -310,6 +310,35 @@ def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, tim asserts.assert_equal(res.Header.EventId, expected_event.event_id, "Expected event ID not found in event report") return res.Data +class AttributeChangeCallback: + def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): + self._output = queue.Queue() + self._expected_attribute = expected_attribute + + def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): + """This is the subscription callback when an attribute is updated. + It checks the passed in attribute is the same as the subscribed to attribute and + then posts it into the queue for later processing.""" + + asserts.assert_equal(path.AttributeType, self._expected_attribute, + f"[AttributeChangeCallback] Attribute mismatch. Expected: {self._expected_attribute}, received: {path.AttributeType}") + logging.info(f"[AttributeChangeCallback] Attribute update callback for {path.AttributeType}") + q = (path, transaction) + self._output.put(q) + + def wait_for_report(self): + try: + path, transaction = self._output.get(block=True, timeout=10) + except queue.Empty: + asserts.fail(f"[AttributeChangeCallback] Failed to receive a report for the {self._expected_attribute} attribute change") + + asserts.assert_equal(path.AttributeType, self._expected_attribute, + f"[AttributeChangeCallback] Received incorrect report. Expected: {self._expected_attribute}, received: {path.AttributeType}") + try: + attribute_value = transaction.GetAttribute(path) + logging.info(f"[AttributeChangeCallback] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") + except KeyError: + asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") class InternalTestRunnerHooks(TestRunnerHooks): From cba575c18189d740bc110c18796c2c33d38a0637 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Wed, 22 May 2024 10:40:19 -0700 Subject: [PATCH 07/20] Lint --- src/python_testing/matter_testing_support.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 6a6684499e9f1a..5416204a25afeb 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -310,6 +310,7 @@ def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, tim asserts.assert_equal(res.Header.EventId, expected_event.event_id, "Expected event ID not found in event report") return res.Data + class AttributeChangeCallback: def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): self._output = queue.Queue() @@ -340,6 +341,7 @@ def wait_for_report(self): except KeyError: asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") + class InternalTestRunnerHooks(TestRunnerHooks): def start(self, count: int): From 0201dd7f6e355dd6b3b5d1321ee67d5bb42af296 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Wed, 22 May 2024 10:42:23 -0700 Subject: [PATCH 08/20] Fix restyle --- src/python_testing/matter_testing_support.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 5416204a25afeb..a50effa95ecd65 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -320,7 +320,7 @@ def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransactio """This is the subscription callback when an attribute is updated. It checks the passed in attribute is the same as the subscribed to attribute and then posts it into the queue for later processing.""" - + asserts.assert_equal(path.AttributeType, self._expected_attribute, f"[AttributeChangeCallback] Attribute mismatch. Expected: {self._expected_attribute}, received: {path.AttributeType}") logging.info(f"[AttributeChangeCallback] Attribute update callback for {path.AttributeType}") @@ -331,13 +331,15 @@ def wait_for_report(self): try: path, transaction = self._output.get(block=True, timeout=10) except queue.Empty: - asserts.fail(f"[AttributeChangeCallback] Failed to receive a report for the {self._expected_attribute} attribute change") + asserts.fail( + f"[AttributeChangeCallback] Failed to receive a report for the {self._expected_attribute} attribute change") asserts.assert_equal(path.AttributeType, self._expected_attribute, f"[AttributeChangeCallback] Received incorrect report. Expected: {self._expected_attribute}, received: {path.AttributeType}") try: attribute_value = transaction.GetAttribute(path) - logging.info(f"[AttributeChangeCallback] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") + logging.info( + f"[AttributeChangeCallback] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") except KeyError: asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") From 3900053f423ddad44dbb0ce93e20610ca1b9a705 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Thu, 23 May 2024 15:06:41 -0700 Subject: [PATCH 09/20] Adds handleNotifySubscriptionStillActive to AsyncReadTransaction, temporary debug logging --- src/app/BufferedReadCallback.h | 4 + .../python/chip/clusters/Attribute.py | 193 +++++++++++++++++- .../python/chip/clusters/attribute.cpp | 27 ++- src/python_testing/TC_IDM_4_3.py | 8 +- 4 files changed, 211 insertions(+), 21 deletions(-) diff --git a/src/app/BufferedReadCallback.h b/src/app/BufferedReadCallback.h index b24257884e1a77..5dc158fbbd6f3b 100644 --- a/src/app/BufferedReadCallback.h +++ b/src/app/BufferedReadCallback.h @@ -70,6 +70,10 @@ class BufferedReadCallback : public ReadClient::Callback // void OnReportBegin() override; void OnReportEnd() override; + void NotifySubscriptionStillActive(const ReadClient & apReadClient) override + { + mCallback.NotifySubscriptionStillActive(apReadClient); + } void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override; void OnError(CHIP_ERROR aError) override { diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py index 9e46eed469d39f..3cb7eb3311095c 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py @@ -680,12 +680,52 @@ def __init__(self, future: Future, eventLoop, devCtrl, returnClusterObject: bool self._pReadClient = None self._pReadCallback = None self._resultError = None + + print("\n\n\n") + print(f"AsyncReadTransaction - __init__") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") def SetClientObjPointers(self, pReadClient, pReadCallback): self._pReadClient = pReadClient self._pReadCallback = pReadCallback + print("\n\n\n") + print(f"AsyncReadTransaction - SetClientObjPointers") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") def GetAllEventValues(self): + print("\n\n\n") + print(f"AsyncReadTransaction - GetAllEventValues") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") return self._events def handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: int, status: int, data: bytes): @@ -702,6 +742,20 @@ def handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: int self._cache.UpdateTLV(path, dataVersion, attributeValue) self._changedPathSet.add(path) + print("\n\n\n") + print(f"AsyncReadTransaction - handleAttributeData") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") + except Exception as ex: logging.exception(ex) @@ -742,11 +796,39 @@ def handleEventData(self, header: EventHeader, path: EventPath, data: bytes, sta self._subscription_handler.OnEventChangeCb( eventResult, self._subscription_handler) + print("\n\n\n") + print(f"AsyncReadTransaction - handleEventData") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") + except Exception as ex: logging.exception(ex) def handleError(self, chipError: PyChipError): self._resultError = chipError.code + + print("\n\n\n") + print(f"AsyncReadTransaction - handleError") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") def _handleSubscriptionEstablished(self, subscriptionId): if not self._future.done(): @@ -762,6 +844,20 @@ def _handleSubscriptionEstablished(self, subscriptionId): else: self._subscription_handler._onResubscriptionSucceededCb(self._subscription_handler) + print("\n\n\n") + print(f"AsyncReadTransaction - _handleSubscriptionEstablished") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") + def handleSubscriptionEstablished(self, subscriptionId): self._event_loop.call_soon_threadsafe( self._handleSubscriptionEstablished, subscriptionId) @@ -777,11 +873,52 @@ def handleResubscriptionAttempted(self, terminationCause: PyChipError, nextResub self._subscription_handler._onResubscriptionAttemptedCb, self._subscription_handler, terminationCause.code, nextResubscribeIntervalMsec) + print("\n\n\n") + print(f"AsyncReadTransaction - handleResubscriptionAttempted") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") + def _handleReportBegin(self): + print("\n\n\n") + print(f"AsyncReadTransaction - _handleReportBegin") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") pass - def _handleReportEnd(self): + def _handleReportEnd(self): self._cache.UpdateCachedData(self._changedPathSet) + + print("\n\n\n") + print(f"AsyncReadTransaction - _handleReportEnd") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") if (self._subscription_handler is not None): for change in self._changedPathSet: @@ -813,6 +950,20 @@ def _handleDone(self): else: self._future.set_result(AsyncReadTransaction.ReadResponse( attributes=self._cache.attributeCache, events=self._events, tlvAttributes=self._cache.attributeTLVCache)) + + print("\n\n\n") + print(f"AsyncReadTransaction - _handleDone") + print(f"self._event_loop: {self._event_loop}") + print(f"self._future: {self._future}") + print(f"self._subscription_handler: {self._subscription_handler}") + print(f"self._events: {self._events}") + print(f"self._devCtrl: {self._devCtrl}") + print(f"self._cache: {self._cache}") + print(f"self._changedPathSet: {self._changedPathSet}") + print(f"self._pReadClient: {self._pReadClient}") + print(f"self._pReadCallback: {self._pReadCallback}") + print(f"self._resultError: {self._resultError}") + print("\n\n\n") # # Decrement the ref on ourselves to match the increment that happened at allocation. @@ -823,14 +974,33 @@ def _handleDone(self): def handleDone(self): self._event_loop.call_soon_threadsafe(self._handleDone) - + def handleReportBegin(self): - pass - + self._handleReportBegin() + def handleReportEnd(self): - # self._event_loop.call_soon_threadsafe(self._handleReportEnd) self._handleReportEnd() + def _handleNotifySubscriptionStillActive(self): + pass + + def handleNotifySubscriptionStillActive(self): + print("\n\n\n\n\n\n\n\n\n\n\n") + print(f"closure.handleNotifySubscriptionStillActive") + print(f"\t\tAsyncReadTransaction - _handleReportBegin") + print(f"\t\tself._event_loop: {self._event_loop}") + print(f"\t\tself._future: {self._future}") + print(f"\t\tself._subscription_handler: {self._subscription_handler}") + print(f"\t\tself._events: {self._events}") + print(f"\t\tself._devCtrl: {self._devCtrl}") + print(f"\t\tself._cache: {self._cache}") + print(f"\t\tself._changedPathSet: {self._changedPathSet}") + print(f"\t\tself._pReadClient: {self._pReadClient}") + print(f"\t\tself._pReadCallback: {self._pReadCallback}") + print(f"\t\tself._resultError: {self._resultError}") + print("\n\n\n\n\n\n\n\n\n\n\n") + self._handleNotifySubscriptionStillActive() + class AsyncWriteTransaction: def __init__(self, future: Future, eventLoop): @@ -889,7 +1059,8 @@ def handleDone(self): None, py_object) _OnReportEndCallbackFunct = CFUNCTYPE( None, py_object) - +_OnNotifySubscriptionStillActiveCallbackFunct = CFUNCTYPE( + None, py_object) @_OnReadAttributeDataCallbackFunct def _OnReadAttributeDataCallback(closure, dataVersion: int, endpoint: int, cluster: int, attribute: int, status, data, len): @@ -937,6 +1108,11 @@ def _OnReportEndCallback(closure): closure.handleReportEnd() +@_OnNotifySubscriptionStillActiveCallbackFunct +def _OnNotifySubscriptionStillActiveCallback(closure): + closure.handleNotifySubscriptionStillActive() + + @_OnReadDoneCallbackFunct def _OnReadDoneCallback(closure): closure.handleDone() @@ -1208,14 +1384,15 @@ def Init(): _OnReadAttributeDataCallbackFunct, _OnReadEventDataCallbackFunct, _OnSubscriptionEstablishedCallbackFunct, _OnResubscriptionAttemptedCallbackFunct, _OnReadErrorCallbackFunct, _OnReadDoneCallbackFunct, - _OnReportBeginCallbackFunct, _OnReportEndCallbackFunct]) + _OnReportBeginCallbackFunct, _OnReportEndCallbackFunct, + _OnNotifySubscriptionStillActiveCallbackFunct]) handle.pychip_WriteClient_InitCallbacks( _OnWriteResponseCallback, _OnWriteErrorCallback, _OnWriteDoneCallback) handle.pychip_ReadClient_InitCallbacks( _OnReadAttributeDataCallback, _OnReadEventDataCallback, _OnSubscriptionEstablishedCallback, _OnResubscriptionAttemptedCallback, _OnReadErrorCallback, _OnReadDoneCallback, - _OnReportBeginCallback, _OnReportEndCallback) + _OnReportBeginCallback, _OnReportEndCallback, _OnNotifySubscriptionStillActiveCallback) _BuildAttributeIndex() _BuildClusterIndex() diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp index b73b4a49b44d1f..0406dbb830f27c 100644 --- a/src/controller/python/chip/clusters/attribute.cpp +++ b/src/controller/python/chip/clusters/attribute.cpp @@ -83,6 +83,7 @@ using OnReadErrorCallback = void (*)(PyObject * appContext, PyChip using OnReadDoneCallback = void (*)(PyObject * appContext); using OnReportBeginCallback = void (*)(PyObject * appContext); using OnReportEndCallback = void (*)(PyObject * appContext); +using OnNotifySubscriptionStillActiveCallback = void (*)(PyObject * appContext); OnReadAttributeDataCallback gOnReadAttributeDataCallback = nullptr; OnReadEventDataCallback gOnReadEventDataCallback = nullptr; @@ -92,6 +93,7 @@ OnReadErrorCallback gOnReadErrorCallback = nullptr; OnReadDoneCallback gOnReadDoneCallback = nullptr; OnReportBeginCallback gOnReportBeginCallback = nullptr; OnReportBeginCallback gOnReportEndCallback = nullptr; +OnNotifySubscriptionStillActiveCallback gOnNotifySubscriptionStillActiveCallback = nullptr; void PythonResubscribePolicy(uint32_t aNumCumulativeRetries, uint32_t & aNextSubscriptionIntervalMsec, bool & aShouldResubscribe) { @@ -225,6 +227,11 @@ class ReadClientCallback : public ReadClient::Callback void OnReportEnd() override { gOnReportEndCallback(mAppContext); } + void NotifySubscriptionStillActive(const ReadClient & apReadClient) override + { + gOnNotifySubscriptionStillActiveCallback(mAppContext); + } + void OnDone(ReadClient *) override { gOnReadDoneCallback(mAppContext); @@ -326,16 +333,18 @@ void pychip_ReadClient_InitCallbacks(OnReadAttributeDataCallback onReadAttribute OnSubscriptionEstablishedCallback onSubscriptionEstablishedCallback, OnResubscriptionAttemptedCallback onResubscriptionAttemptedCallback, OnReadErrorCallback onReadErrorCallback, OnReadDoneCallback onReadDoneCallback, - OnReportBeginCallback onReportBeginCallback, OnReportEndCallback onReportEndCallback) + OnReportBeginCallback onReportBeginCallback, OnReportEndCallback onReportEndCallback, + OnNotifySubscriptionStillActiveCallback onNotifySubscriptionStillActiveCallback) { - gOnReadAttributeDataCallback = onReadAttributeDataCallback; - gOnReadEventDataCallback = onReadEventDataCallback; - gOnSubscriptionEstablishedCallback = onSubscriptionEstablishedCallback; - gOnResubscriptionAttemptedCallback = onResubscriptionAttemptedCallback; - gOnReadErrorCallback = onReadErrorCallback; - gOnReadDoneCallback = onReadDoneCallback; - gOnReportBeginCallback = onReportBeginCallback; - gOnReportEndCallback = onReportEndCallback; + gOnReadAttributeDataCallback = onReadAttributeDataCallback; + gOnReadEventDataCallback = onReadEventDataCallback; + gOnSubscriptionEstablishedCallback = onSubscriptionEstablishedCallback; + gOnResubscriptionAttemptedCallback = onResubscriptionAttemptedCallback; + gOnReadErrorCallback = onReadErrorCallback; + gOnReadDoneCallback = onReadDoneCallback; + gOnReportBeginCallback = onReportBeginCallback; + gOnReportEndCallback = onReportEndCallback; + gOnNotifySubscriptionStillActiveCallback = onNotifySubscriptionStillActiveCallback; } PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, size_t timedWriteTimeoutMsSizeT, diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 873755716da0e1..675d81a186b616 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -115,10 +115,10 @@ async def test_TC_IDM_4_3(self): - # secs = 60 - # print(f"\n\n\n\n\nTime to sleep {secs} second(s)") - # time.sleep(secs) - # print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") + secs = 60 + print(f"\n\n\n\n\nTime to sleep {secs} second(s)") + time.sleep(secs) + print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") From 56742f84781c5631a46e45325f4acd081885c2b0 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Fri, 24 May 2024 00:16:44 -0700 Subject: [PATCH 10/20] Adds SetNotifySubscriptionStillActiveCallback --- .../python/chip/clusters/Attribute.py | 182 ++---------------- src/python_testing/TC_IDM_4_3.py | 37 ++-- 2 files changed, 37 insertions(+), 182 deletions(-) diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py index 3cb7eb3311095c..0b4601f9daa3bf 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py @@ -579,6 +579,15 @@ def SetErrorCallback(self, callback: Callable[[int, SubscriptionTransaction], No if callback is not None: self._onErrorCb = callback + def SetNotifySubscriptionStillActiveCallback(self, callback: Callable): + ''' + Sets the callback function that gets invoked when a report data message is sent. The callback + is expected to have the following signature: + def Callback() + ''' + if callback is not None: + self._readTransaction.register_notify_subscription_still_active_callback(callback) + @property def OnAttributeChangeCb(self) -> Callable[[TypedAttributePath, SubscriptionTransaction], None]: return self._onAttributeChangeCb @@ -680,52 +689,13 @@ def __init__(self, future: Future, eventLoop, devCtrl, returnClusterObject: bool self._pReadClient = None self._pReadCallback = None self._resultError = None - - print("\n\n\n") - print(f"AsyncReadTransaction - __init__") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") + self._notify_subscription_still_active_callback = None def SetClientObjPointers(self, pReadClient, pReadCallback): self._pReadClient = pReadClient self._pReadCallback = pReadCallback - print("\n\n\n") - print(f"AsyncReadTransaction - SetClientObjPointers") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") def GetAllEventValues(self): - print("\n\n\n") - print(f"AsyncReadTransaction - GetAllEventValues") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") return self._events def handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: int, status: int, data: bytes): @@ -742,20 +712,6 @@ def handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: int self._cache.UpdateTLV(path, dataVersion, attributeValue) self._changedPathSet.add(path) - print("\n\n\n") - print(f"AsyncReadTransaction - handleAttributeData") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") - except Exception as ex: logging.exception(ex) @@ -796,39 +752,11 @@ def handleEventData(self, header: EventHeader, path: EventPath, data: bytes, sta self._subscription_handler.OnEventChangeCb( eventResult, self._subscription_handler) - print("\n\n\n") - print(f"AsyncReadTransaction - handleEventData") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") - except Exception as ex: logging.exception(ex) def handleError(self, chipError: PyChipError): self._resultError = chipError.code - - print("\n\n\n") - print(f"AsyncReadTransaction - handleError") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") def _handleSubscriptionEstablished(self, subscriptionId): if not self._future.done(): @@ -844,20 +772,6 @@ def _handleSubscriptionEstablished(self, subscriptionId): else: self._subscription_handler._onResubscriptionSucceededCb(self._subscription_handler) - print("\n\n\n") - print(f"AsyncReadTransaction - _handleSubscriptionEstablished") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") - def handleSubscriptionEstablished(self, subscriptionId): self._event_loop.call_soon_threadsafe( self._handleSubscriptionEstablished, subscriptionId) @@ -873,53 +787,12 @@ def handleResubscriptionAttempted(self, terminationCause: PyChipError, nextResub self._subscription_handler._onResubscriptionAttemptedCb, self._subscription_handler, terminationCause.code, nextResubscribeIntervalMsec) - print("\n\n\n") - print(f"AsyncReadTransaction - handleResubscriptionAttempted") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") - def _handleReportBegin(self): - print("\n\n\n") - print(f"AsyncReadTransaction - _handleReportBegin") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") pass def _handleReportEnd(self): self._cache.UpdateCachedData(self._changedPathSet) - print("\n\n\n") - print(f"AsyncReadTransaction - _handleReportEnd") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") - if (self._subscription_handler is not None): for change in self._changedPathSet: try: @@ -950,21 +823,6 @@ def _handleDone(self): else: self._future.set_result(AsyncReadTransaction.ReadResponse( attributes=self._cache.attributeCache, events=self._events, tlvAttributes=self._cache.attributeTLVCache)) - - print("\n\n\n") - print(f"AsyncReadTransaction - _handleDone") - print(f"self._event_loop: {self._event_loop}") - print(f"self._future: {self._future}") - print(f"self._subscription_handler: {self._subscription_handler}") - print(f"self._events: {self._events}") - print(f"self._devCtrl: {self._devCtrl}") - print(f"self._cache: {self._cache}") - print(f"self._changedPathSet: {self._changedPathSet}") - print(f"self._pReadClient: {self._pReadClient}") - print(f"self._pReadCallback: {self._pReadCallback}") - print(f"self._resultError: {self._resultError}") - print("\n\n\n") - # # Decrement the ref on ourselves to match the increment that happened at allocation. # This happens synchronously as part of handling done to ensure the object remains valid @@ -982,24 +840,14 @@ def handleReportEnd(self): self._handleReportEnd() def _handleNotifySubscriptionStillActive(self): - pass + if self._notify_subscription_still_active_callback: + self._notify_subscription_still_active_callback() def handleNotifySubscriptionStillActive(self): - print("\n\n\n\n\n\n\n\n\n\n\n") - print(f"closure.handleNotifySubscriptionStillActive") - print(f"\t\tAsyncReadTransaction - _handleReportBegin") - print(f"\t\tself._event_loop: {self._event_loop}") - print(f"\t\tself._future: {self._future}") - print(f"\t\tself._subscription_handler: {self._subscription_handler}") - print(f"\t\tself._events: {self._events}") - print(f"\t\tself._devCtrl: {self._devCtrl}") - print(f"\t\tself._cache: {self._cache}") - print(f"\t\tself._changedPathSet: {self._changedPathSet}") - print(f"\t\tself._pReadClient: {self._pReadClient}") - print(f"\t\tself._pReadCallback: {self._pReadCallback}") - print(f"\t\tself._resultError: {self._resultError}") - print("\n\n\n\n\n\n\n\n\n\n\n") self._handleNotifySubscriptionStillActive() + + def register_notify_subscription_still_active_callback(self, callback): + self._notify_subscription_still_active_callback = callback class AsyncWriteTransaction: diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 675d81a186b616..3bd8595c0b0105 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -24,7 +24,7 @@ import chip.clusters as Clusters from chip.ChipDeviceCtrl import ChipDeviceController from chip.clusters import ClusterObjects as ClusterObjects -from chip.clusters.Attribute import AttributePath, TypedAttributePath +from chip.clusters.Attribute import AttributePath, TypedAttributePath, AsyncReadTransaction from chip.exceptions import ChipStackError from chip.interaction_model import Status from matter_testing_support import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback @@ -91,9 +91,13 @@ def steps_TC_IDM_4_3(self): # TestStep(21, "TH sends a subscription request to subscribe to all attributes from a specific cluster on all endpoints. AttributePath = [[Cluster = ClusterID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", # "Verify that the DUT sends reports for all the attributes that have changed after N seconds.") ] - + + def on_notify_subscription_still_active(self): + print("NotifyLogic") + @async_test_body async def test_TC_IDM_4_3(self): + print("Hey there") # Test setup node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel @@ -111,22 +115,25 @@ async def test_TC_IDM_4_3(self): reportInterval=(3, 5), keepSubscriptions=False ) - - - - + + + + + + + + sub_th_step1a.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active) + secs = 60 print(f"\n\n\n\n\nTime to sleep {secs} second(s)") time.sleep(secs) print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") - - - - - - - + + + + + # Verify that the subscription is activated between TH and DUT # Verify on the TH, a report data message is received. @@ -150,14 +157,14 @@ async def test_TC_IDM_4_3(self): # Set Attribute Update Callback node_label_update_cb = AttributeChangeCallback(node_label_attr) sub_th_step1a.SetAttributeUpdateCallback(node_label_update_cb) - + # Modify attribute value new_node_label_write = "NewNodeLabel_11001100" await TH.WriteAttribute( self.dut_node_id, [(0, node_label_attr(value=new_node_label_write))] ) - + node_label_update_cb.wait_for_report() From e5665928f947d6988cffeacedcdbaabe4784480a Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Sat, 25 May 2024 00:03:52 -0700 Subject: [PATCH 11/20] Measuring time --- src/python_testing/TC_IDM_4_3.py | 104 +++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 3bd8595c0b0105..3c72a4a07b8e1a 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -45,6 +45,23 @@ class TC_IDM_4_3(MatterBaseTest): + # ANSI escape codes for background colors + BACKGROUND_COLORS = { + 'black': '\033[40m', + 'red': '\033[41m', + 'green': '\033[42m', + 'yellow': '\033[43m', + 'blue': '\033[44m', + 'magenta': '\033[45m', + 'cyan': '\033[46m', + 'white': '\033[47m', + 'reset': '\033[0m' + } + + # Function to print text with a specific background color + def fprint(self, text: str, background_color: str): + print(f"{self.BACKGROUND_COLORS.get(background_color, self.BACKGROUND_COLORS['reset'])}{text}{self.BACKGROUND_COLORS['reset']}") + def steps_TC_IDM_4_3(self): return [TestStep("1a", "DUT and TH activate the subscription.", "Verify on the TH, a report data message is received. Verify on the TH the Subscribe Response has the following fields: SubscriptionId and MaxInterval In the following Steps 2, 3, 5-10, 13, and 15, the MaxInterval time reference in each step is the MaxInterval presented in the Subscribe Response of the subscription."), @@ -93,11 +110,45 @@ def steps_TC_IDM_4_3(self): ] def on_notify_subscription_still_active(self): - print("NotifyLogic") + + self.previous_report_data_time = self.current_report_data_time + self.current_report_data_time = time.time() + + if self.previous_report_data_time > 0: + diff = self.current_report_data_time - self.previous_report_data_time + else: + diff = 0 + + self.fprint(f"NotifyLogic {time.time()} >>> t: {diff}", "red") + + def wait_for_attribute_update_report(self, expected_attribute, output): + try: + path, transaction = output.get(block=True, timeout=10) + except queue.Empty: + asserts.fail( + f"[AttributeChangeCallback | Local] Failed to receive a report for the {expected_attribute} attribute change") + + asserts.assert_equal(path.AttributeType, expected_attribute, + f"[AttributeChangeCallback | Local] Received incorrect report. Expected: {expected_attribute}, received: {path.AttributeType}") + try: + attribute_value = transaction.GetAttribute(path) + + self.attr_update_report_data_time = time.time() + + logging.info( + f"[AttributeChangeCallback | Local] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") + except KeyError: + asserts.fail("[AttributeChangeCallback | Local] Attribute {expected_attribute} not found in returned report") + + current_report_data_time: time = 0 + previous_report_data_time: time = 0 + attr_update_report_data_time: time = 0 + + min_interval_floor_sec: int = 2 + max_interval_ceiling_sec: int = 3 @async_test_body async def test_TC_IDM_4_3(self): - print("Hey there") # Test setup node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel @@ -109,44 +160,30 @@ async def test_TC_IDM_4_3(self): self.step("1a") # Subscribe to attribute - sub_th_step1a = await TH.ReadAttribute( + sub_th_step1ab = await TH.ReadAttribute( nodeid=self.dut_node_id, attributes=node_label_attr_path, - reportInterval=(3, 5), + reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), keepSubscriptions=False ) + sub_th_step1ab.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active) - - - - - - sub_th_step1a.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active) - - secs = 60 + secs = 45 print(f"\n\n\n\n\nTime to sleep {secs} second(s)") time.sleep(secs) print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") - - - - - - # Verify that the subscription is activated between TH and DUT # Verify on the TH, a report data message is received. - asserts.assert_true(sub_th_step1a.subscriptionId, "Subscription not activated") + asserts.assert_true(sub_th_step1ab.subscriptionId, "Subscription not activated") # Verify subscriptionId field is present - asserts.assert_is_not_none(sub_th_step1a.subscriptionId, "SubscriptionId field not present") + asserts.assert_is_not_none(sub_th_step1ab.subscriptionId, "SubscriptionId field not present") # Verify MaxInterval field is present - sub_th_step1a_min_interval_sec, sub_th_step1a_max_interval_sec = sub_th_step1a.GetReportingIntervalsSeconds() - asserts.assert_is_not_none(sub_th_step1a_max_interval_sec, "MaxInterval field not present") - - # sub_th_step1a.Shutdown() + sub_th_step1ab_min_interval_sec, sub_th_step1ab_max_interval_sec = sub_th_step1ab.GetReportingIntervalsSeconds() + asserts.assert_is_not_none(sub_th_step1ab_max_interval_sec, "MaxInterval field not present") # *** Step 1b *** # Change the value of the attribute which has been subscribed on the DUT by manually changing some @@ -156,16 +193,29 @@ async def test_TC_IDM_4_3(self): # Set Attribute Update Callback node_label_update_cb = AttributeChangeCallback(node_label_attr) - sub_th_step1a.SetAttributeUpdateCallback(node_label_update_cb) + sub_th_step1ab.SetAttributeUpdateCallback(node_label_update_cb) - # Modify attribute value + # Update attribute value new_node_label_write = "NewNodeLabel_11001100" await TH.WriteAttribute( self.dut_node_id, [(0, node_label_attr(value=new_node_label_write))] ) - node_label_update_cb.wait_for_report() + self.wait_for_attribute_update_report(node_label_attr, node_label_update_cb._output) + + # Number of seconds elapsed between the last report data event + # and the arrival of the attribute update report data + elapsed_time_since_report = self.attr_update_report_data_time - self.previous_report_data_time + + # Verify that the attribute update report data is sent + # after MinInterval time and before MaxInterval time + asserts.assert_greater(elapsed_time_since_report, self.min_interval_floor_sec, + f"Attribute update report data must be sent after the MinInterval") + asserts.assert_less(elapsed_time_since_report, self.max_interval_ceiling_sec, + f"Attribute update report data must be sent before the MaxInterval") + + sub_th_step1ab.Shutdown() if __name__ == "__main__": From 5a5b92ad2a1e7884b6069d5d621c720aebf10b57 Mon Sep 17 00:00:00 2001 From: raul-marquez-csa Date: Mon, 10 Jun 2024 08:49:20 -0700 Subject: [PATCH 12/20] progress --- src/python_testing/TC_IDM_4_3.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 3c72a4a07b8e1a..ad571fb4b5517c 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -16,6 +16,7 @@ # import copy +from datetime import datetime import logging import queue import threading @@ -144,7 +145,7 @@ def wait_for_attribute_update_report(self, expected_attribute, output): previous_report_data_time: time = 0 attr_update_report_data_time: time = 0 - min_interval_floor_sec: int = 2 + min_interval_floor_sec: int = 1 max_interval_ceiling_sec: int = 3 @async_test_body @@ -169,7 +170,7 @@ async def test_TC_IDM_4_3(self): sub_th_step1ab.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active) - secs = 45 + secs = 15 print(f"\n\n\n\n\nTime to sleep {secs} second(s)") time.sleep(secs) print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") @@ -208,6 +209,17 @@ async def test_TC_IDM_4_3(self): # and the arrival of the attribute update report data elapsed_time_since_report = self.attr_update_report_data_time - self.previous_report_data_time + + # Convert the current time to a datetime object + update_time = datetime.fromtimestamp(self.attr_update_report_data_time) + previous_time = datetime.fromtimestamp(self.previous_report_data_time) + + # Format the datetime object into the desired string format + update_time_f = update_time.strftime("%H:%M:%S.%f") + previous_time_f = previous_time.strftime("%H:%M:%S.%f") + + self.fprint(f"\n\n\t\elapsed_time_since_report: {elapsed_time_since_report}s\n\t\tattr_update_report_data_time: {update_time_f}s\n\t\tprevious_report_data_time: {previous_time_f}s\n\n", "green") + # Verify that the attribute update report data is sent # after MinInterval time and before MaxInterval time asserts.assert_greater(elapsed_time_since_report, self.min_interval_floor_sec, From b04d1a91ee3f6786a816698ff1b98f6b8adc6070 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 1 Aug 2024 20:50:23 -0700 Subject: [PATCH 13/20] Removes duplicate AttributeChangeCallback class --- src/python_testing/matter_testing_support.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 612175024f77f6..948b897870d87c 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -391,23 +391,6 @@ def attribute_report_counts(self) -> dict[ClusterObjects.ClusterAttributeDescrip def attribute_reports(self) -> dict[ClusterObjects.ClusterAttributeDescriptor, AttributeValue]: return self._attribute_reports - -class AttributeChangeCallback: - def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): - self._output = queue.Queue() - self._expected_attribute = expected_attribute - - def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): - """This is the subscription callback when an attribute is updated. - It checks the passed in attribute is the same as the subscribed to attribute and - then posts it into the queue for later processing.""" - - asserts.assert_equal(path.AttributeType, self._expected_attribute, - f"[AttributeChangeCallback] Attribute mismatch. Expected: {self._expected_attribute}, received: {path.AttributeType}") - logging.info(f"[AttributeChangeCallback] Attribute update callback for {path.AttributeType}") - q = (path, transaction) - self._output.put(q) - def wait_for_report(self): try: path, transaction = self._output.get(block=True, timeout=10) From 06dd24e442cce75664c130e2cc0c2ad74470839a Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 12 Aug 2024 08:12:16 -0700 Subject: [PATCH 14/20] progress --- src/python_testing/TC_IDM_4_3.py | 42 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index ad571fb4b5517c..99cb79bf8ea36f 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -68,10 +68,10 @@ def steps_TC_IDM_4_3(self): "Verify on the TH, a report data message is received. Verify on the TH the Subscribe Response has the following fields: SubscriptionId and MaxInterval In the following Steps 2, 3, 5-10, 13, and 15, the MaxInterval time reference in each step is the MaxInterval presented in the Subscribe Response of the subscription."), TestStep("1b", "Change the value of the attribute which has been subscribed on the DUT by manually changing some settings on the device. Example: Temperature sensor may update the value of the room temperature. Turning on/off on a light bulb.", "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), - # TestStep(2, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT by sending an IMWrite or Invoke message to the DUT from the TH.", - # "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), - # TestStep(3, "DUT and TH activate the subscription for an attribute. Do not change the value of the attribute which has been subscribed.", - # "Verify that there is an empty report data message sent from the DUT to the TH after MaxInterval time."), + TestStep(2, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT by sending an IMWrite or Invoke message to the DUT from the TH.", + "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), + TestStep(3, "DUT and TH activate the subscription for an attribute. Do not change the value of the attribute which has been subscribed.", + "Verify that there is an empty report data message sent from the DUT to the TH after the MinInterval time and no later than the MaxInterval time plus an additional duration equal to the total retransmission time according to negotiated MRP parameters."), # TestStep(4, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT. TH force sends a status response with an \"invalid subscription\". Change the value of the attribute which has been subscribed on the DUT.", # "Verify that DUT does not send report data for the second time after the subscription has been terminated."), # TestStep(5, "Activate the subscription between the DUT and the TH for an attribute of data type bool. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", @@ -152,7 +152,13 @@ def wait_for_attribute_update_report(self, expected_attribute, output): async def test_TC_IDM_4_3(self): # Test setup + # Mandatory writable attributes node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel + # bc = Clusters.GeneralCommissioning.Attributes.Breadcrumb + + # Event + # acl = Clusters.AccessControl.Events. + node_label_attr_path = [(0, node_label_attr)] TH: ChipDeviceController = self.default_controller @@ -228,6 +234,34 @@ async def test_TC_IDM_4_3(self): f"Attribute update report data must be sent before the MaxInterval") sub_th_step1ab.Shutdown() + + # DUT and TH activate the subscription. Change the value of the attribute which has been + # subscribed on the DUT by sending an IMWrite or Invoke message to the DUT from the TH. + # Verify that there is a report data message sent from the DUT for the changed value of + # the attribute. Verify that the Report Data is sent when the minimum interval time is + # reached and before the MaxInterval time. + self.step(2) + + # DUT and TH activate the subscription for an attribute. Do not change the value of the + # attribute which has been subscribed. Verify that there is an empty report data message + # sent from the DUT to the TH after the MinInterval time and no later than the + # MaxInterval time plus an additional duration equal to the total retransmission time + # according to negotiated MRP parameters. + self.step(3) + + # Subscribe to attribute + sub_th_step3 = await TH.ReadAttribute( + nodeid=self.dut_node_id, + attributes=node_label_attr_path, + reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), + keepSubscriptions=False + ) + + + sub_th_step3.Shutdown() + + + if __name__ == "__main__": From 892d5858fc0a99c37cda310d79ad8d5d91a0c83e Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 16 Aug 2024 20:21:45 -0700 Subject: [PATCH 15/20] Step 1 --- src/python_testing/TC_IDM_4_3.py | 163 +++++++++++++++++++------------ 1 file changed, 99 insertions(+), 64 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 99cb79bf8ea36f..299ab8bbb3d890 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -21,11 +21,12 @@ import queue import threading import time +# from time import time import chip.clusters as Clusters from chip.ChipDeviceCtrl import ChipDeviceController from chip.clusters import ClusterObjects as ClusterObjects -from chip.clusters.Attribute import AttributePath, TypedAttributePath, AsyncReadTransaction +from chip.clusters.Attribute import AttributePath, TypedAttributePath, AsyncReadTransaction, SubscriptionTransaction from chip.exceptions import ChipStackError from chip.interaction_model import Status from matter_testing_support import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback @@ -122,6 +123,10 @@ def on_notify_subscription_still_active(self): self.fprint(f"NotifyLogic {time.time()} >>> t: {diff}", "red") + def on_notify_subscription_still_active_empty_report(self): + self.report_data_received = True + self.empty_report_time = time.time() + def wait_for_attribute_update_report(self, expected_attribute, output): try: path, transaction = output.get(block=True, timeout=10) @@ -141,13 +146,16 @@ def wait_for_attribute_update_report(self, expected_attribute, output): except KeyError: asserts.fail("[AttributeChangeCallback | Local] Attribute {expected_attribute} not found in returned report") - current_report_data_time: time = 0 - previous_report_data_time: time = 0 - attr_update_report_data_time: time = 0 + current_report_data_time = 0 + previous_report_data_time = 0 + attr_update_report_data_time = 0 min_interval_floor_sec: int = 1 max_interval_ceiling_sec: int = 3 + empty_report_time = None + report_data_received = False + @async_test_body async def test_TC_IDM_4_3(self): @@ -155,42 +163,42 @@ async def test_TC_IDM_4_3(self): # Mandatory writable attributes node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel # bc = Clusters.GeneralCommissioning.Attributes.Breadcrumb - + # Event # acl = Clusters.AccessControl.Events. - + node_label_attr_path = [(0, node_label_attr)] TH: ChipDeviceController = self.default_controller - # *** Step 1a *** - # DUT and TH activate the subscription. + # # *** Step 1a *** + # # DUT and TH activate the subscription. self.step("1a") - # Subscribe to attribute - sub_th_step1ab = await TH.ReadAttribute( - nodeid=self.dut_node_id, - attributes=node_label_attr_path, - reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), - keepSubscriptions=False - ) + # # Subscribe to attribute + # sub_th_step1ab = await TH.ReadAttribute( + # nodeid=self.dut_node_id, + # attributes=node_label_attr_path, + # reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), + # keepSubscriptions=False + # ) - sub_th_step1ab.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active) + # sub_th_step1ab.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active) - secs = 15 - print(f"\n\n\n\n\nTime to sleep {secs} second(s)") - time.sleep(secs) - print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") + # secs = 3 + # print(f"\n\n\n\n\nTime to sleep {secs} second(s)") + # time.sleep(secs) + # print(f"Rise and shine after {secs} second(s)\n\n\n\n\n") - # Verify that the subscription is activated between TH and DUT - # Verify on the TH, a report data message is received. - asserts.assert_true(sub_th_step1ab.subscriptionId, "Subscription not activated") + # # Verify that the subscription is activated between TH and DUT + # # Verify on the TH, a report data message is received. + # asserts.assert_true(sub_th_step1ab.subscriptionId, "Subscription not activated") - # Verify subscriptionId field is present - asserts.assert_is_not_none(sub_th_step1ab.subscriptionId, "SubscriptionId field not present") + # # Verify subscriptionId field is present + # asserts.assert_is_not_none(sub_th_step1ab.subscriptionId, "SubscriptionId field not present") - # Verify MaxInterval field is present - sub_th_step1ab_min_interval_sec, sub_th_step1ab_max_interval_sec = sub_th_step1ab.GetReportingIntervalsSeconds() - asserts.assert_is_not_none(sub_th_step1ab_max_interval_sec, "MaxInterval field not present") + # # Verify MaxInterval field is present + # sub_th_step1ab_min_interval_sec, sub_th_step1ab_max_interval_sec = sub_th_step1ab.GetReportingIntervalsSeconds() + # asserts.assert_is_not_none(sub_th_step1ab_max_interval_sec, "MaxInterval field not present") # *** Step 1b *** # Change the value of the attribute which has been subscribed on the DUT by manually changing some @@ -198,70 +206,97 @@ async def test_TC_IDM_4_3(self): # Turning on/off on a light bulb. self.step("1b") - # Set Attribute Update Callback - node_label_update_cb = AttributeChangeCallback(node_label_attr) - sub_th_step1ab.SetAttributeUpdateCallback(node_label_update_cb) + # # Set Attribute Update Callback + # node_label_update_cb = AttributeChangeCallback(node_label_attr) + # sub_th_step1ab.SetAttributeUpdateCallback(node_label_update_cb) - # Update attribute value - new_node_label_write = "NewNodeLabel_11001100" - await TH.WriteAttribute( - self.dut_node_id, - [(0, node_label_attr(value=new_node_label_write))] - ) + # # Update attribute value + # new_node_label_write = "NewNodeLabel_11001100" + # await TH.WriteAttribute( + # self.dut_node_id, + # [(0, node_label_attr(value=new_node_label_write))] + # ) - self.wait_for_attribute_update_report(node_label_attr, node_label_update_cb._output) + # self.wait_for_attribute_update_report(node_label_attr, node_label_update_cb._output) - # Number of seconds elapsed between the last report data event - # and the arrival of the attribute update report data - elapsed_time_since_report = self.attr_update_report_data_time - self.previous_report_data_time + # # Number of seconds elapsed between the last report data event + # # and the arrival of the attribute update report data + # elapsed_time_since_report = self.attr_update_report_data_time - self.previous_report_data_time + # # Convert the current time to a datetime object + # update_time = datetime.fromtimestamp(self.attr_update_report_data_time) + # previous_time = datetime.fromtimestamp(self.previous_report_data_time) - # Convert the current time to a datetime object - update_time = datetime.fromtimestamp(self.attr_update_report_data_time) - previous_time = datetime.fromtimestamp(self.previous_report_data_time) + # # Format the datetime object into the desired string format + # update_time_f = update_time.strftime("%H:%M:%S.%f") + # previous_time_f = previous_time.strftime("%H:%M:%S.%f") - # Format the datetime object into the desired string format - update_time_f = update_time.strftime("%H:%M:%S.%f") - previous_time_f = previous_time.strftime("%H:%M:%S.%f") + # self.fprint(f"\n\n\t\elapsed_time_since_report: {elapsed_time_since_report}s\n\t\tattr_update_report_data_time: {update_time_f}s\n\t\tprevious_report_data_time: {previous_time_f}s\n\n", "green") - self.fprint(f"\n\n\t\elapsed_time_since_report: {elapsed_time_since_report}s\n\t\tattr_update_report_data_time: {update_time_f}s\n\t\tprevious_report_data_time: {previous_time_f}s\n\n", "green") + # # Verify that the attribute update report data is sent + # # after MinInterval time and before MaxInterval time + # asserts.assert_greater(elapsed_time_since_report, self.min_interval_floor_sec, + # f"Attribute update report data must be sent after the MinInterval") + # asserts.assert_less(elapsed_time_since_report, self.max_interval_ceiling_sec, + # f"Attribute update report data must be sent before the MaxInterval") - # Verify that the attribute update report data is sent - # after MinInterval time and before MaxInterval time - asserts.assert_greater(elapsed_time_since_report, self.min_interval_floor_sec, - f"Attribute update report data must be sent after the MinInterval") - asserts.assert_less(elapsed_time_since_report, self.max_interval_ceiling_sec, - f"Attribute update report data must be sent before the MaxInterval") + # sub_th_step1ab.Shutdown() - sub_th_step1ab.Shutdown() - # DUT and TH activate the subscription. Change the value of the attribute which has been # subscribed on the DUT by sending an IMWrite or Invoke message to the DUT from the TH. # Verify that there is a report data message sent from the DUT for the changed value of # the attribute. Verify that the Report Data is sent when the minimum interval time is # reached and before the MaxInterval time. self.step(2) - + # DUT and TH activate the subscription for an attribute. Do not change the value of the # attribute which has been subscribed. Verify that there is an empty report data message # sent from the DUT to the TH after the MinInterval time and no later than the # MaxInterval time plus an additional duration equal to the total retransmission time # according to negotiated MRP parameters. self.step(3) - + # Subscribe to attribute - sub_th_step3 = await TH.ReadAttribute( + sub_th_step3: SubscriptionTransaction = await TH.ReadAttribute( nodeid=self.dut_node_id, attributes=node_label_attr_path, reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), keepSubscriptions=False ) - - + + # Record time after subscription + sub_time = time.time() + + # Get subscription timeout + sub_timeout_sec = sub_th_step3.GetSubscriptionTimeoutMs() / 1000 + + # Records the time the first empty report after subscription arrives + sub_th_step3.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active_empty_report) + + # Waint for empty report data + wait_increments = self.min_interval_floor_sec / 10 + while not self.report_data_received: + time.sleep(wait_increments) + self.fprint(f"Time: {time.time()}", "blue") + self.fprint(f"Empty Rport time: {self.empty_report_time}", "green") + if self.report_data_received: + break + + # Elapsed time between subscription established and first report data + sub_report_data_elapsed_time = self.empty_report_time - sub_time + + self.fprint(f"min_interval_floor_sec: {self.min_interval_floor_sec}", "red") + self.fprint(f"sub_report_data_elapsed_time: {sub_report_data_elapsed_time}", "red") + self.fprint(f"sub_timeout_sec: {sub_timeout_sec}", "red") + + # Verify that the empty report data message from the DUT to the TH was sent + # after the MinInterval time and no later than the MaxInterval time plus an + # additional duration equal to the total retransmission time according to + # negotiated MRP parameters + asserts.assert_greater(sub_report_data_elapsed_time, self.min_interval_floor_sec, "Empty report not received after the MinInterval time") + asserts.assert_less(sub_report_data_elapsed_time, sub_timeout_sec, "Empty report not received before the MaxInterval time") + sub_th_step3.Shutdown() - - - if __name__ == "__main__": From 02519028d1e9a86a251484633be9899c84650834 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 20 Aug 2024 10:43:14 -0700 Subject: [PATCH 16/20] Step2 progress --- src/python_testing/TC_IDM_4_3.py | 247 ++++++++++++++++++++++++------- 1 file changed, 190 insertions(+), 57 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 299ab8bbb3d890..5c92070fc33fdc 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -15,6 +15,7 @@ # limitations under the License. # +import inspect import copy from datetime import datetime import logging @@ -30,7 +31,10 @@ from chip.exceptions import ChipStackError from chip.interaction_model import Status from matter_testing_support import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback -from mobly import asserts +from chip.clusters.enum import MatterIntEnum +from basic_composition_support import BasicCompositionTests +from enum import IntFlag +from mobly import asserts, signals ''' Category: @@ -45,7 +49,16 @@ ''' -class TC_IDM_4_3(MatterBaseTest): +class TC_IDM_4_3(MatterBaseTest, BasicCompositionTests): + + + + + + + + + # ANSI escape codes for background colors BACKGROUND_COLORS = { @@ -61,54 +74,46 @@ class TC_IDM_4_3(MatterBaseTest): } # Function to print text with a specific background color - def fprint(self, text: str, background_color: str): - print(f"{self.BACKGROUND_COLORS.get(background_color, self.BACKGROUND_COLORS['reset'])}{text}{self.BACKGROUND_COLORS['reset']}") + def fprint(self, text: str, background_color: str, padding: int = 0): + double_space = " " * padding + padding_space = "\n" * (padding - 1) + print(f"{padding_space}{double_space}{self.BACKGROUND_COLORS.get(background_color, self.BACKGROUND_COLORS['reset'])}{text}{self.BACKGROUND_COLORS['reset']}{padding_space}") def steps_TC_IDM_4_3(self): - return [TestStep("1a", "DUT and TH activate the subscription.", - "Verify on the TH, a report data message is received. Verify on the TH the Subscribe Response has the following fields: SubscriptionId and MaxInterval In the following Steps 2, 3, 5-10, 13, and 15, the MaxInterval time reference in each step is the MaxInterval presented in the Subscribe Response of the subscription."), - TestStep("1b", "Change the value of the attribute which has been subscribed on the DUT by manually changing some settings on the device. Example: Temperature sensor may update the value of the room temperature. Turning on/off on a light bulb.", - "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), - TestStep(2, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT by sending an IMWrite or Invoke message to the DUT from the TH.", - "Verify that there is a report data message sent from the DUT for the changed value of the attribute. Verify that the Report Data is sent when the minimum interval time is reached and before the MaxInterval time."), - TestStep(3, "DUT and TH activate the subscription for an attribute. Do not change the value of the attribute which has been subscribed.", + return [TestStep(1, "DUT and TH activate the subscription for an attribute. Do not change the value of the attribute which has been subscribed.", "Verify that there is an empty report data message sent from the DUT to the TH after the MinInterval time and no later than the MaxInterval time plus an additional duration equal to the total retransmission time according to negotiated MRP parameters."), - # TestStep(4, "DUT and TH activate the subscription. Change the value of the attribute which has been subscribed on the DUT. TH force sends a status response with an \"invalid subscription\". Change the value of the attribute which has been subscribed on the DUT.", - # "Verify that DUT does not send report data for the second time after the subscription has been terminated."), - # TestStep(5, "Activate the subscription between the DUT and the TH for an attribute of data type bool. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", - # "Verify on the TH that the DUT sends the correct value of the attribute."), - # TestStep(6, "Activate the subscription between the DUT and the TH for an attribute of data type string. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", - # "Verify on the TH that the DUT sends the correct value of the attribute."), - # TestStep(7, "Activate the subscription between the DUT and the TH for an attribute of data type \"unsigned integer\". Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", - # "Verify on the TH that the DUT sends the correct value of the attribute."), - # TestStep(8, "Activate the subscription between the DUT and the TH for an attribute of data type \"signed integer\". Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times)before the MaxInterval time specified during the subscription activation.", - # "Verify on the TH that the DUT sends the correct value of the attribute."), - # TestStep(9, "Activate the subscription between the DUT and the TH for an attribute of data type \"floating point\". Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", - # "Verify on the TH that the DUT sends the correct value of the attribute."), - # TestStep(10, "Activate the subscription between the DUT and the TH for an attribute of data type list. Modify that attribute on the DUT. DUT should send the report data with the modified attribute value. Modify the attribute multiple times (3 times) before the MaxInterval time specified during the subscription activation.", - # "Verify on the TH that the DUT sends the correct value of the attribute."), - # TestStep(11, "Activate the subscription between the DUT and the TH for any attribute. KeepSubscriptions flag should be set to False After the Maximum interval time is elapsed, TH should send another subscription request message with different parameters than before. KeepSubscriptions flag should be set to False Change the value of the attribute requested on the DUT.", + TestStep(2, "Activate the subscription between the DUT and the TH for an attribute of data type bool. If no such attribute exists, skip this step.", + "Verify the subscription was successfully activated and a priming data report was sent"), + # TestStep(3, "Activate the subscription between the DUT and the TH for an attribute of data type string. If no such attribute exists, skip this step.", + # "Verify the subscription was successfully activated and a priming data report was sent"), + # TestStep(4, "Activate the subscription between the DUT and the TH for an attribute of data type unsigned integer. If no such attribute exists, skip this step.", + # "Verify the subscription was successfully activated and a priming data report was sent"), + # TestStep(5, "Activate the subscription between the DUT and the TH for an attribute of data type signed integer. If no such attribute exists, skip this step.", + # "Verify the subscription was successfully activated and a priming data report was sent"), + # TestStep(6, "Activate the subscription between the DUT and the TH for an attribute of data type floating point. If no such attribute exists, skip this step.", + # "Verify the subscription was successfully activated and a priming data report was sent"), + # TestStep(7, "Activate the subscription between the DUT and the TH for an attribute of data type list. If no such attribute exists, skip this step.", + # "Verify the subscription was successfully activated and a priming data report was sent"), + # TestStep(8, "Activate the subscription between the DUT and the TH for any attribute. KeepSubscriptions flag should be set to False Save the returned MaxInterval value as original_max_interval TH then sends another subscription request message for the same attribute with different parameters than before. KeepSubscriptions flag should be set to False Wait for original_max_interval. Change the value of the attribute requested on the DUT.", # "Verify that the DUT sends the changed value of the attribute with the newest subscription id sent with the second request."), - # TestStep(12, "Activate the subscription between the DUT and the TH for any attribute After the Maximum interval time is elapsed, change the value of the attribute requested on the DUT.", - # "Verify that the DUT sends the changed value of the attribute to the TH after the next MinIntervalFloor time has passed."), - # TestStep(13, "Activate the subscription between the DUT and the TH for an attribute There are no attribute value changes before MaxInterval elapses.", - # "Verify that the DUT sends a Report Data action with no data to keep the subscription alive."), - # TestStep(14, "TH sends a subscription request action for an attribute to the DUT with the KeepSubscriptions flag set to True. Activate the subscription between DUT and the TH. Initiate another subscription request action to the DUT for another attribute with the KeepSubscriptions flag set to True. Change both the attribute values on the DUT.", - # "Verify that both the subscriptions are active and the TH receives reports for both these attributes on both subscriptions."), - # TestStep(15, "TH sends a subscription request action for an attribute to the DUT with the KeepSubscriptions flag set to True. Activate the subscription between DUT and the TH. Initiate another subscription request action to the DUT for another attribute with the KeepSubscriptions flag set to False. Change both the attribute values on the DUT.", - # "Verify that both the subscriptions are active and the TH receives notifications for both these attributes. Verify that the first subscription is terminated after the MaxInterval of the first subscription is reached."), - # TestStep(16, "TH sends a subscription request action for an attribute and all events. Set the MinIntervalFloor to some value say \"N\"(seconds). Change the value of the attribute and trigger an action on the DUT to trigger any event.", - # "Verify on TH that DUT sends a report action data for both the attribute and the event after N seconds."), - # TestStep(17, "TH sends a subscription request action for attribute wildcard - AttributePath = [[Endpoint = EndpointID, Cluster = ClusterID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", - # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), - # TestStep(18, "TH sends a subscription request to subscribe to an attribute on a specific cluster from all endpoints AttributePath = [[Attribute = Attribute, Cluster = ClusterID ]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change the attribute on the DUT", - # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), - # TestStep(19, "TH sends a subscription request to subscribe to all attributes from all clusters from all endpoints. AttributePath = [[]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", - # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), - # TestStep(20, "TH sends a subscription request to subscribe to all attributes from all clusters on an endpoint. AttributePath = [[Endpoint = EndpointID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", - # "Verify that the DUT sends reports for all the attributes that have changed after N seconds."), - # TestStep(21, "TH sends a subscription request to subscribe to all attributes from a specific cluster on all endpoints. AttributePath = [[Cluster = ClusterID]]. Set the MinIntervalFloor to some value say \"N\"(seconds). Change all or few of the attributes on the DUT", - # "Verify that the DUT sends reports for all the attributes that have changed after N seconds.") + # TestStep(9, "Activate the subscription between the DUT and the TH for any attribute with MinIntervalFloor set to 5 seconds and MaxIntervalCeiling set to 10. Save the returned MaxInterval as max_interval. Wait to receive the empty report on the subscription and save the time the report was received as time_empty. TH then changes the attribute and waits for a data report. Save the time the report was received as time_data. TH then waits for a second empty report on the subscription and saves the time the report was received as time_empty_2", + # "Verify that time_data - time_empty is larger than the MinIntervalFloor and smaller than max_interval plus an additional duration equal to the total retransmission time according to negotiated MRP parameters. Verify that time_empty_2 - time_data is larger than the MinIntervalFloor and smaller than max_interval plus an additional duration equal to the total retransmission time according to negotiated MRP parameters."), + # TestStep(10, "TH sends a subscription request action for an attribute to the DUT with the KeepSubscriptions flag set to False. Activate the subscription between DUT and the TH. Initiate another subscription request action to the DUT for another attribute with the KeepSubscriptions flag set to True. Change both the attribute values on the DUT.", + # "Verify that the TH receives reports for both these attributes on their respective subscriptions."), + # TestStep(11, "TH sends a subscription request action for an attribute to the DUT with the KeepSubscriptions flag set to False. Activate the subscription between DUT and the TH. Initiate another subscription request action to the DUT for another attribute with the KeepSubscriptions flag set to False. Change both the attribute values on the DUT.", + # "Verify that the TH receives a report for the second attribute on the second subscription. Verify that that the TH does not receive a report on the first subscription."), + # TestStep(12, "TH sends a subscription request action for an attribute and all events. Change the value of the attribute and trigger an action on the DUT to trigger any event.", + # "Verify on TH that DUT sends a report action data for both the attribute and the event."), + # TestStep(13, "TH sends a subscription request action for attribute wildcard - AttributePath = [[Endpoint = EndpointID, Cluster = ClusterID]] for a cluster where more than 1 attribute can be changed by the TH. Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed."), + # TestStep(14, "TH sends a subscription request to subscribe to an attribute on a specific cluster from all endpoints AttributePath = [[Attribute = Attribute, Cluster = ClusterID ]]. Change the attribute on the DUT", + # "Verify that the DUT sends a priming reports for all the attributes."), + # TestStep(15, "TH sends a subscription request to subscribe to all attributes from all clusters from all endpoints. AttributePath = [[]]. Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed."), + # TestStep(16, "TH sends a sub scription request to subscribe to all attributes from all clusters on an endpoint. AttributePath = [[Endpoint = EndpointID]]. Change all or few of the attributes on the DUT", + # "Verify that the DUT sends reports for all the attributes that have changed."), + # TestStep(17, "TH sends a subscription request to subscribe to all attributes from a specific cluster on all endpoints. AttributePath = [[Cluster = ClusterID]].", + # "Verify that the DUT sends a priming reports for all the attributes."), ] def on_notify_subscription_still_active(self): @@ -146,6 +151,68 @@ def wait_for_attribute_update_report(self, expected_attribute, output): except KeyError: asserts.fail("[AttributeChangeCallback | Local] Attribute {expected_attribute} not found in returned report") + + + def all_device_clusters(self) -> set: + device_clusters = set() + for endpoint in self.endpoints: + device_clusters |= set(self.endpoints[endpoint].keys()) + return device_clusters + + async def all_type_attributes_for_cluster(self, cluster: ClusterObjects.Cluster, desired_type: type) -> list[ClusterObjects.ClusterAttributeDescriptor]: + all_attributes = [attribute for attribute in cluster.Attributes.__dict__.values() if inspect.isclass( + attribute) and issubclass(attribute, ClusterObjects.ClusterAttributeDescriptor)] + + # Hackish way to get enums to return properly -- the default behavior (under else block) returns a BLANK LIST without this workaround + # If type(attribute.attribute_type.Type) or type(ClusterObjects.ClusterObjectFieldDescriptor(Type=desired_type).Type are enums, they return , which are equal! + if desired_type == MatterIntEnum: + all_attributes_of_type = [attribute for attribute in all_attributes if type( + attribute.attribute_type.Type) == type(ClusterObjects.ClusterObjectFieldDescriptor(Type=desired_type).Type)] + elif desired_type == IntFlag: + try: + feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) + except signals.TestFailure: + print(f"{cluster} does not support Attributes.FeatureMap") + return [] + if feature_map >= 1: + return [cluster.Attributes.FeatureMap] + else: + all_attributes_of_type = [attribute for attribute in all_attributes if attribute.attribute_type == + ClusterObjects.ClusterObjectFieldDescriptor(Type=desired_type)] + return all_attributes_of_type + + async def check_attribute_read_for_type(self, attribute_type: type, return_objects: bool = False) -> None: + # Get all clusters from device + for cluster in self.device_clusters: + all_types = await self.all_type_attributes_for_cluster(cluster, attribute_type) + self.fprint(f"all_types: {all_types}", "green", 2) + + if all_types: + chosen_attribute = all_types[0] + chosen_cluster = Clusters.ClusterObjects.ALL_CLUSTERS[chosen_attribute.cluster_id] + break + else: + print(f"Attribute type not found on device: {attribute_type}") + chosen_cluster = None + + endpoint = None + for endpoint in self.endpoints: + if (chosen_cluster in self.endpoints[endpoint]) and (chosen_attribute in self.endpoints[endpoint][chosen_cluster]): + break + + if chosen_cluster and (endpoint is not None): + output = await self.read_single_attribute_check_success( + endpoint=endpoint, + dev_ctrl=self.default_controller, + cluster=chosen_cluster, + attribute=chosen_attribute) + return chosen_cluster, chosen_attribute, output if return_objects else output + return + + + + + current_report_data_time = 0 previous_report_data_time = 0 attr_update_report_data_time = 0 @@ -155,24 +222,48 @@ def wait_for_attribute_update_report(self, expected_attribute, output): empty_report_time = None report_data_received = False + + root_node_endpoint = 0 @async_test_body async def test_TC_IDM_4_3(self): + + await self.setup_class_helper(default_to_pase=False) + + # all_clusters = [cluster for cluster in Clusters.ClusterObjects.ALL_ATTRIBUTES] + # server_list_attr = Clusters.Objects.Descriptor.Attributes.ServerList + # attribute_list = Clusters.Objects.Descriptor.Attributes.AttributeList + # descriptor_obj = Clusters.Objects.Descriptor + # server_list_attr_path = [(0, server_list_attr)] + # descriptor_obj_path = [(0, descriptor_obj)] + # attribute_list_path = [0, attribute_list] + self.device_clusters = self.all_device_clusters() + self.all_supported_clusters = [cluster for cluster in Clusters.__dict__.values( + ) if inspect.isclass(cluster) and issubclass(cluster, ClusterObjects.Cluster)] + + + + + + + + + # Test setup # Mandatory writable attributes - node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel - # bc = Clusters.GeneralCommissioning.Attributes.Breadcrumb + node_label_attr = Clusters.Objects.BasicInformation.Attributes.NodeLabel + # bc = Clusters.GeneralCommissioning.Attributes.Breadcrumb # Event # acl = Clusters.AccessControl.Events. - node_label_attr_path = [(0, node_label_attr)] + node_label_attr_path = [(self.root_node_endpoint, node_label_attr)] TH: ChipDeviceController = self.default_controller # # *** Step 1a *** # # DUT and TH activate the subscription. - self.step("1a") + # self.step("1a") # # Subscribe to attribute # sub_th_step1ab = await TH.ReadAttribute( @@ -204,7 +295,7 @@ async def test_TC_IDM_4_3(self): # Change the value of the attribute which has been subscribed on the DUT by manually changing some # settings on the device. Example: Temperature sensor may update the value of the room temperature. # Turning on/off on a light bulb. - self.step("1b") + # self.step("1b") # # Set Attribute Update Callback # node_label_update_cb = AttributeChangeCallback(node_label_attr) @@ -247,17 +338,28 @@ async def test_TC_IDM_4_3(self): # Verify that there is a report data message sent from the DUT for the changed value of # the attribute. Verify that the Report Data is sent when the minimum interval time is # reached and before the MaxInterval time. - self.step(2) + # self.step(2) # DUT and TH activate the subscription for an attribute. Do not change the value of the # attribute which has been subscribed. Verify that there is an empty report data message # sent from the DUT to the TH after the MinInterval time and no later than the # MaxInterval time plus an additional duration equal to the total retransmission time # according to negotiated MRP parameters. - self.step(3) + # self.step(3) + + + + + # *** Step 1 *** + # DUT and TH activate the subscription for an attribute. Do not change the value of the + # attribute which has been subscribed. Verify that there is an empty report data message + # sent from the DUT to the TH after the MinInterval time and no later than the MaxInterval + # time plus an additional duration equal to the total retransmission time according to + # negotiated MRP parameters. + self.step(1) # Subscribe to attribute - sub_th_step3: SubscriptionTransaction = await TH.ReadAttribute( + sub_th_step1: SubscriptionTransaction = await TH.ReadAttribute( nodeid=self.dut_node_id, attributes=node_label_attr_path, reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), @@ -268,10 +370,10 @@ async def test_TC_IDM_4_3(self): sub_time = time.time() # Get subscription timeout - sub_timeout_sec = sub_th_step3.GetSubscriptionTimeoutMs() / 1000 + sub_timeout_sec = sub_th_step1.GetSubscriptionTimeoutMs() / 1000 # Records the time the first empty report after subscription arrives - sub_th_step3.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active_empty_report) + sub_th_step1.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active_empty_report) # Waint for empty report data wait_increments = self.min_interval_floor_sec / 10 @@ -296,7 +398,38 @@ async def test_TC_IDM_4_3(self): asserts.assert_greater(sub_report_data_elapsed_time, self.min_interval_floor_sec, "Empty report not received after the MinInterval time") asserts.assert_less(sub_report_data_elapsed_time, sub_timeout_sec, "Empty report not received before the MaxInterval time") - sub_th_step3.Shutdown() + sub_th_step1.Shutdown() + + # Activate the subscription between the DUT and the TH for an attribute of + # data type bool. If no such attribute exists, skip this step. Verify the + # subscription was successfully activated and a priming data report was sent + self.step(2) + + # Check for attribute of type bool + out = await self.check_attribute_read_for_type( + attribute_type=bool, + return_objects=True + ) + + # If found subscribe to attribute + if out: + self.fprint(f"out: {out}", "green", 5) + cluster, attribute, value = out + attr_path = [(self.root_node_endpoint, attribute)] + + # Subscribe to attribute + sub_th_step2: SubscriptionTransaction = await TH.ReadAttribute( + nodeid=self.dut_node_id, + attributes=attr_path, + reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), + keepSubscriptions=False + ) + + self.fprint(f"sub_th_step2: {sub_th_step2.subscriptionId}", "yellow", 3) + + sub_th_step2.Shutdown() + else: + logging.info("No attribute of type bool was found, skipping step") if __name__ == "__main__": From 9f262a397c52ed4c5a7cb1d68a6db196d1acf215 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 20 Aug 2024 16:03:16 -0700 Subject: [PATCH 17/20] Step2 progress --- src/python_testing/TC_IDM_4_3.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 5c92070fc33fdc..057c576e15f9a4 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -183,7 +183,14 @@ async def all_type_attributes_for_cluster(self, cluster: ClusterObjects.Cluster, async def check_attribute_read_for_type(self, attribute_type: type, return_objects: bool = False) -> None: # Get all clusters from device + self.fprint(f"self.device_clusters: {self.device_clusters}", "red", 2) for cluster in self.device_clusters: + + # TEMPORARY: if cluster is SmokeCoAlarm skip, as it returns INVALID_ACTION when trying + # to subscribe to its TestInProgress attribute + # if cluster.id == 92: + # continue + all_types = await self.all_type_attributes_for_cluster(cluster, attribute_type) self.fprint(f"all_types: {all_types}", "green", 2) @@ -231,9 +238,9 @@ async def test_TC_IDM_4_3(self): await self.setup_class_helper(default_to_pase=False) # all_clusters = [cluster for cluster in Clusters.ClusterObjects.ALL_ATTRIBUTES] - # server_list_attr = Clusters.Objects.Descriptor.Attributes.ServerList - # attribute_list = Clusters.Objects.Descriptor.Attributes.AttributeList - # descriptor_obj = Clusters.Objects.Descriptor + # server_list_attr = Clusters.Descriptor.Attributes.ServerList + # attribute_list = Clusters.Descriptor.Attributes.AttributeList + # descriptor_obj = Clusters.Descriptor # server_list_attr_path = [(0, server_list_attr)] # descriptor_obj_path = [(0, descriptor_obj)] # attribute_list_path = [0, attribute_list] @@ -252,7 +259,7 @@ async def test_TC_IDM_4_3(self): # Test setup # Mandatory writable attributes - node_label_attr = Clusters.Objects.BasicInformation.Attributes.NodeLabel + node_label_attr = Clusters.BasicInformation.Attributes.NodeLabel # bc = Clusters.GeneralCommissioning.Attributes.Breadcrumb # Event @@ -363,7 +370,8 @@ async def test_TC_IDM_4_3(self): nodeid=self.dut_node_id, attributes=node_label_attr_path, reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), - keepSubscriptions=False + keepSubscriptions=False, + fabricFiltered=False ) # Record time after subscription @@ -375,7 +383,7 @@ async def test_TC_IDM_4_3(self): # Records the time the first empty report after subscription arrives sub_th_step1.SetNotifySubscriptionStillActiveCallback(self.on_notify_subscription_still_active_empty_report) - # Waint for empty report data + # Wait for empty report data wait_increments = self.min_interval_floor_sec / 10 while not self.report_data_received: time.sleep(wait_increments) @@ -418,18 +426,24 @@ async def test_TC_IDM_4_3(self): attr_path = [(self.root_node_endpoint, attribute)] # Subscribe to attribute + self.fprint(f"Will sub TO: {attr_path}", "black", 7) + logging.info(f"Attribute of type 'bool' was found") sub_th_step2: SubscriptionTransaction = await TH.ReadAttribute( nodeid=self.dut_node_id, attributes=attr_path, reportInterval=(self.min_interval_floor_sec, self.max_interval_ceiling_sec), - keepSubscriptions=False + keepSubscriptions=False, + fabricFiltered=False ) - self.fprint(f"sub_th_step2: {sub_th_step2.subscriptionId}", "yellow", 3) + # Verify the subscription was successfully activated and a priming + # data report was sent + asserts.assert_is_not_none(sub_th_step2.subscriptionId, "Subscription activation to attribute of type 'bool' was unsuccessful") + logging.info("Subscription activation to attribute of type 'bool' successful") sub_th_step2.Shutdown() else: - logging.info("No attribute of type bool was found, skipping step") + logging.info("No attribute of type 'bool' was found, skipping step") if __name__ == "__main__": From 42a7bfecfade8179294696f8bbbae2cc6658d772 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 21 Nov 2024 13:48:20 -0800 Subject: [PATCH 18/20] Update to latest master merge and refactors --- .../python/chip/clusters/Attribute.py | 17 +++-- src/python_testing/TC_IDM_4_3.py | 73 ++++++++++--------- .../chip/testing/matter_testing.py | 2 +- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py index 877b9875b6557e..b66cbf03e27ffe 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py @@ -824,22 +824,24 @@ def _handleDone(self): def handleDone(self): self._event_loop.call_soon_threadsafe(self._handleDone) - + def handleReportBegin(self): self._handleReportBegin() - + def handleReportEnd(self): self._handleReportEnd() - def _handleNotifySubscriptionStillActive(self): + # def _handleNotifySubscriptionStillActive(self): + # if self._notify_subscription_still_active_callback: + # self._notify_subscription_still_active_callback() + + def handleNotifySubscriptionStillActive(self): + # self._handleNotifySubscriptionStillActive() if self._notify_subscription_still_active_callback: self._notify_subscription_still_active_callback() - def handleNotifySubscriptionStillActive(self): - self._handleNotifySubscriptionStillActive() - def register_notify_subscription_still_active_callback(self, callback): - self._notify_subscription_still_active_callback = callback + self._notify_subscription_still_active_callback = callback class AsyncWriteTransaction: @@ -906,6 +908,7 @@ def handleDone(self): _OnNotifySubscriptionStillActiveCallbackFunct = CFUNCTYPE( None, py_object) + @_OnReadAttributeDataCallbackFunct def _OnReadAttributeDataCallback(closure, dataVersion: int, endpoint: int, cluster: int, attribute: int, status, data, len): dataBytes = ctypes.string_at(data, len) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 057c576e15f9a4..3177f38c8f8211 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -15,6 +15,25 @@ # limitations under the License. # +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. + +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: +# run1: +# app: ${ALL_CLUSTERS_APP} +# factory-reset: true +# quiet: true +# 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 +# === END CI TEST ARGUMENTS === + import inspect import copy from datetime import datetime @@ -30,9 +49,9 @@ from chip.clusters.Attribute import AttributePath, TypedAttributePath, AsyncReadTransaction, SubscriptionTransaction from chip.exceptions import ChipStackError from chip.interaction_model import Status -from matter_testing_support import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback +from chip.testing.matter_testing import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback from chip.clusters.enum import MatterIntEnum -from basic_composition_support import BasicCompositionTests +from chip.testing.basic_composition import BasicCompositionTests from enum import IntFlag from mobly import asserts, signals @@ -50,15 +69,6 @@ class TC_IDM_4_3(MatterBaseTest, BasicCompositionTests): - - - - - - - - - # ANSI escape codes for background colors BACKGROUND_COLORS = { @@ -146,7 +156,7 @@ def wait_for_attribute_update_report(self, expected_attribute, output): self.attr_update_report_data_time = time.time() - logging.info( + logging.debug( f"[AttributeChangeCallback | Local] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") except KeyError: asserts.fail("[AttributeChangeCallback | Local] Attribute {expected_attribute} not found in returned report") @@ -185,15 +195,15 @@ async def check_attribute_read_for_type(self, attribute_type: type, return_objec # Get all clusters from device self.fprint(f"self.device_clusters: {self.device_clusters}", "red", 2) for cluster in self.device_clusters: - + # TEMPORARY: if cluster is SmokeCoAlarm skip, as it returns INVALID_ACTION when trying # to subscribe to its TestInProgress attribute # if cluster.id == 92: # continue - + all_types = await self.all_type_attributes_for_cluster(cluster, attribute_type) self.fprint(f"all_types: {all_types}", "green", 2) - + if all_types: chosen_attribute = all_types[0] chosen_cluster = Clusters.ClusterObjects.ALL_CLUSTERS[chosen_attribute.cluster_id] @@ -234,9 +244,9 @@ async def check_attribute_read_for_type(self, attribute_type: type, return_objec @async_test_body async def test_TC_IDM_4_3(self): - - await self.setup_class_helper(default_to_pase=False) - + + await self.setup_class_helper(allow_pase=False) + # all_clusters = [cluster for cluster in Clusters.ClusterObjects.ALL_ATTRIBUTES] # server_list_attr = Clusters.Descriptor.Attributes.ServerList # attribute_list = Clusters.Descriptor.Attributes.AttributeList @@ -247,15 +257,10 @@ async def test_TC_IDM_4_3(self): self.device_clusters = self.all_device_clusters() self.all_supported_clusters = [cluster for cluster in Clusters.__dict__.values( ) if inspect.isclass(cluster) and issubclass(cluster, ClusterObjects.Cluster)] - - - - - - - - - + + + + # Test setup # Mandatory writable attributes @@ -353,10 +358,10 @@ async def test_TC_IDM_4_3(self): # MaxInterval time plus an additional duration equal to the total retransmission time # according to negotiated MRP parameters. # self.step(3) - - - - + + + + # *** Step 1 *** # DUT and TH activate the subscription for an attribute. Do not change the value of the # attribute which has been subscribed. Verify that there is an empty report data message @@ -407,18 +412,18 @@ async def test_TC_IDM_4_3(self): asserts.assert_less(sub_report_data_elapsed_time, sub_timeout_sec, "Empty report not received before the MaxInterval time") sub_th_step1.Shutdown() - + # Activate the subscription between the DUT and the TH for an attribute of # data type bool. If no such attribute exists, skip this step. Verify the # subscription was successfully activated and a priming data report was sent self.step(2) - + # Check for attribute of type bool out = await self.check_attribute_read_for_type( attribute_type=bool, return_objects=True ) - + # If found subscribe to attribute if out: self.fprint(f"out: {out}", "green", 5) diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py index c8503639a374fc..f24a0cf711543a 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py @@ -577,7 +577,7 @@ def wait_for_report(self): f"[AttributeChangeCallback] Received incorrect report. Expected: {self._expected_attribute}, received: {path.AttributeType}") try: attribute_value = transaction.GetAttribute(path) - logging.info( + logging.debug( f"[AttributeChangeCallback] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") except KeyError: asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") From c42d4629e4bdd978d53a0a8dc983d20de5c920a8 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 21 Nov 2024 13:59:01 -0800 Subject: [PATCH 19/20] Fix restyle --- src/python_testing/TC_IDM_4_3.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 3177f38c8f8211..68642b8450d4b2 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -34,27 +34,28 @@ # --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto # === END CI TEST ARGUMENTS === -import inspect import copy -from datetime import datetime +import inspect import logging import queue import threading import time -# from time import time +from datetime import datetime +from enum import IntFlag import chip.clusters as Clusters from chip.ChipDeviceCtrl import ChipDeviceController from chip.clusters import ClusterObjects as ClusterObjects -from chip.clusters.Attribute import AttributePath, TypedAttributePath, AsyncReadTransaction, SubscriptionTransaction +from chip.clusters.Attribute import AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath +from chip.clusters.enum import MatterIntEnum from chip.exceptions import ChipStackError from chip.interaction_model import Status -from chip.testing.matter_testing import AttributeChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main, EventChangeCallback -from chip.clusters.enum import MatterIntEnum from chip.testing.basic_composition import BasicCompositionTests -from enum import IntFlag +from chip.testing.matter_testing import (AttributeChangeCallback, EventChangeCallback, MatterBaseTest, TestStep, async_test_body, + default_matter_test_main) from mobly import asserts, signals + ''' Category: Functional From cccf2d73932ad57a634f1cad11d05d12f0d3913f Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 21 Nov 2024 14:00:59 -0800 Subject: [PATCH 20/20] Fix restyle --- src/python_testing/TC_IDM_4_3.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/python_testing/TC_IDM_4_3.py b/src/python_testing/TC_IDM_4_3.py index 68642b8450d4b2..d9db5865a87f87 100644 --- a/src/python_testing/TC_IDM_4_3.py +++ b/src/python_testing/TC_IDM_4_3.py @@ -55,7 +55,6 @@ default_matter_test_main) from mobly import asserts, signals - ''' Category: Functional