From c9f677981c2a5b39c74530bfa16913c9b036fa2c Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 13 May 2024 21:24:22 +0200 Subject: [PATCH 01/11] Convert TestLevelControlCluster to asyncio Remove ZCLReadAttribute and ZCLSend API use from the level control test TestLevelControlCluster and convert to asyncio. --- .../python/test/test_scripts/base.py | 36 ++++++++----------- .../test/test_scripts/mobile-device-test.py | 5 ++- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 9fc9300f4c8116..4e02b734304b03 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -1112,33 +1112,27 @@ def TestOnOffCluster(self, nodeid: int, endpoint: int, group: int): return False return True - def TestLevelControlCluster(self, nodeid: int, endpoint: int, group: int): + async def TestLevelControlCluster(self, nodeid: int, endpoint: int): self.logger.info( f"Sending MoveToLevel command to device {nodeid} endpoint {endpoint}") - try: - commonArgs = dict(transitionTime=0, optionsMask=1, optionsOverride=1) + commonArgs = dict(transitionTime=0, optionsMask=1, optionsOverride=1) + + async def _moveClusterLevel(setLevel): + await self.devCtrl.SendCommand(nodeid, + endpoint, + Clusters.LevelControl.Commands.MoveToLevel(**commonArgs, level=setLevel)) + res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, Clusters.LevelControl.Attributes.CurrentLevel)]) + readVal = res[endpoint][Clusters.LevelControl][Clusters.LevelControl.Attributes.CurrentLevel] + if readVal != setLevel: + raise Exception(f"Read attribute LevelControl.CurrentLevel: expected value {setLevel}, got {readVal}") + + try: # Move to 1 - self.devCtrl.ZCLSend("LevelControl", "MoveToLevel", nodeid, - endpoint, group, dict(**commonArgs, level=1), blocking=True) - res = self.devCtrl.ZCLReadAttribute(cluster="LevelControl", - attribute="CurrentLevel", - nodeid=nodeid, - endpoint=endpoint, - groupid=group) - TestResult("Read attribute LevelControl.CurrentLevel", - res).assertValueEqual(1) + await _moveClusterLevel(1) # Move to 254 - self.devCtrl.ZCLSend("LevelControl", "MoveToLevel", nodeid, - endpoint, group, dict(**commonArgs, level=254), blocking=True) - res = self.devCtrl.ZCLReadAttribute(cluster="LevelControl", - attribute="CurrentLevel", - nodeid=nodeid, - endpoint=endpoint, - groupid=group) - TestResult("Read attribute LevelControl.CurrentLevel", - res).assertValueEqual(254) + await _moveClusterLevel(254) return True except Exception as ex: diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py index 33ae713fe02cb2..f3f9d642151518 100755 --- a/src/controller/python/test/test_scripts/mobile-device-test.py +++ b/src/controller/python/test/test_scripts/mobile-device-test.py @@ -107,9 +107,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): group=GROUP_ID), "Failed to test on off cluster") logger.info("Testing level control cluster") - FailIfNot(test.TestLevelControlCluster(nodeid=device_nodeid, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), + FailIfNot(asyncio.run(test.TestLevelControlCluster(nodeid=device_nodeid, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test level control cluster") logger.info("Testing sending commands to non exist endpoint") From af8e2d3ceead96755602624e23866191ad016743 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 13 May 2024 21:31:45 +0200 Subject: [PATCH 02/11] Convert TestReadBasicAttributes to asyncio Remove ZCLReadAttribute API use from basic information cluster test and convert to use asyncio. --- .../python/test/test_scripts/base.py | 34 +++++++++---------- .../test/test_scripts/mobile-device-test.py | 10 +++--- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 4e02b734304b03..433e54f851ebeb 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -1165,29 +1165,27 @@ def TestResolve(self, nodeid): self.logger.exception("Failed to resolve. {}".format(ex)) return False - def TestReadBasicAttributes(self, nodeid: int, endpoint: int, group: int): + async def TestReadBasicAttributes(self, nodeid: int, endpoint: int): + attrs = Clusters.BasicInformation.Attributes basic_cluster_attrs = { - "VendorName": "TEST_VENDOR", - "VendorID": 0xFFF1, - "ProductName": "TEST_PRODUCT", - "ProductID": 0x8001, - "NodeLabel": "Test", - "Location": "XX", - "HardwareVersion": 0, - "HardwareVersionString": "TEST_VERSION", - "SoftwareVersion": 1, - "SoftwareVersionString": "1.0", + attrs.VendorName: "TEST_VENDOR", + attrs.VendorID: 0xFFF1, + attrs.ProductName: "TEST_PRODUCT", + attrs.ProductID: 0x8001, + attrs.NodeLabel: "Test", + attrs.Location: "XX", + attrs.HardwareVersion: 0, + attrs.HardwareVersionString: "TEST_VERSION", + attrs.SoftwareVersion: 1, + attrs.SoftwareVersionString: "1.0", } failed_zcl = {} for basic_attr, expected_value in basic_cluster_attrs.items(): try: - res = self.devCtrl.ZCLReadAttribute(cluster="BasicInformation", - attribute=basic_attr, - nodeid=nodeid, - endpoint=endpoint, - groupid=group) - TestResult(f"Read attribute {basic_attr}", res).assertValueEqual( - expected_value) + res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, basic_attr)]) + readVal = res[endpoint][Clusters.BasicInformation][basic_attr] + if readVal != expected_value: + raise Exception(f"Read attribute: expected value {expected_value}, got {readVal}") except Exception as ex: failed_zcl[basic_attr] = str(ex) if failed_zcl: diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py index f3f9d642151518..632c39299ea300 100755 --- a/src/controller/python/test/test_scripts/mobile-device-test.py +++ b/src/controller/python/test/test_scripts/mobile-device-test.py @@ -122,9 +122,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): "Failed when testing Python Cluster Object APIs") logger.info("Testing attribute reading") - FailIfNot(test.TestReadBasicAttributes(nodeid=device_nodeid, - endpoint=ENDPOINT_ID, - group=GROUP_ID), + FailIfNot(asyncio.run(test.TestReadBasicAttributes(nodeid=device_nodeid, + endpoint=ENDPOINT_ID)), "Failed to test Read Basic Attributes") logger.info("Testing attribute writing") @@ -133,9 +132,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): "Failed to test Write Basic Attributes") logger.info("Testing attribute reading basic again") - FailIfNot(test.TestReadBasicAttributes(nodeid=1, - endpoint=ENDPOINT_ID, - group=GROUP_ID), + FailIfNot(asyncio.run(test.TestReadBasicAttributes(nodeid=1, + endpoint=ENDPOINT_ID)), "Failed to test Read Basic Attributes") logger.info("Testing subscription") From 748dadce14f67898eb0d809ada5fcc67d1126027 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 13 May 2024 21:50:54 +0200 Subject: [PATCH 03/11] Use SendCommand directly in send_zcl_command Avoid using ZCLSend API instead use SendCommand directly in the send_zcl_command helper function. --- .../mbed/integration_tests/common/utils.py | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/test_driver/mbed/integration_tests/common/utils.py b/src/test_driver/mbed/integration_tests/common/utils.py index 036b612d7ba18c..659122ba5c0051 100644 --- a/src/test_driver/mbed/integration_tests/common/utils.py +++ b/src/test_driver/mbed/integration_tests/common/utils.py @@ -14,6 +14,7 @@ # limitations under the License. +import asyncio import logging import platform import random @@ -114,21 +115,33 @@ def send_zcl_command(devCtrl, line): if len(args) < 5: raise exceptions.InvalidArgumentCount(5, len(args)) - if args[0] not in all_commands: - raise exceptions.UnknownCluster(args[0]) - command = all_commands.get(args[0]).get(args[1], None) + cluster = args[0] + command = args[1] + if cluster not in all_commands: + raise exceptions.UnknownCluster(cluster) + commandObj = all_commands.get(cluster).get(command, None) # When command takes no arguments, (not command) is True - if command is None: - raise exceptions.UnknownCommand(args[0], args[1]) - err, res = devCtrl.ZCLSend(args[0], args[1], int( - args[2]), int(args[3]), int(args[4]), FormatZCLArguments(args[5:], command), blocking=True) - if err != 0: - log.error("Failed to send ZCL command [{}] {}.".format(err, res)) - elif res is not None: - log.info("Success, received command response:") - log.info(res) - else: - log.info("Success, no command response.") + if commandObj is None: + raise exceptions.UnknownCommand(cluster, command) + + try: + req = commandObj(**FormatZCLArguments(args[5:], command)) + except BaseException: + raise exceptions.UnknownCommand(cluster, command) + + nodeid = int(args[2]) + endpoint = int(args[3]) + try: + res = asyncio.run(devCtrl.SendCommand(nodeid, endpoint, req)) + logging.debug(f"CommandResponse {res}") + if res is not None: + log.info("Success, received command response:") + log.info(res) + else: + log.info("Success, no command response.") + except exceptions.InteractionModelError as ex: + return (int(ex.status), None) + log.error("Failed to send ZCL command [{}] {}.".format(int(ex.status), None)) except exceptions.ChipStackException as ex: log.error("An exception occurred during processing ZCL command:") log.error(str(ex)) From ce9161f5d0d814a93e866068871b522e50364f7d Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 13 May 2024 22:01:26 +0200 Subject: [PATCH 04/11] Convert TestFailsafe to use asyncio/SendCommand Remove ZCLSend API usage and call SendCommand directly. Also convert the test to a test using asyncio. --- .../python/test/test_scripts/base.py | 50 ++++++++++--------- .../test/test_scripts/failsafe_tests.py | 3 +- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 433e54f851ebeb..9f4c452f9c7807 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -368,15 +368,16 @@ def TestOnNetworkCommissioning(self, discriminator: int, setuppin: int, nodeid: def TestUsedTestCommissioner(self): return self.devCtrl.GetTestCommissionerUsed() - def TestFailsafe(self, nodeid: int): + async def TestFailsafe(self, nodeid: int): self.logger.info("Testing arm failsafe") self.logger.info("Setting failsafe on CASE connection") - err, resp = self.devCtrl.ZCLSend("GeneralCommissioning", "ArmFailSafe", nodeid, - 0, 0, dict(expiryLengthSeconds=60, breadcrumb=1), blocking=True) - if err != 0: + try: + resp = await self.devCtrl.SendCommand(nodeid, 0, + Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=60, breadcrumb=1)) + except IM.InteractionModelError as ex: self.logger.error( - "Failed to send arm failsafe command error is {} with im response{}".format(err, resp)) + "Failed to send arm failsafe command error is {}".format(ex.status)) return False if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk: @@ -387,12 +388,12 @@ def TestFailsafe(self, nodeid: int): self.logger.info( "Attempting to open basic commissioning window - this should fail since the failsafe is armed") try: - asyncio.run(self.devCtrl.SendCommand( + await self.devCtrl.SendCommand( nodeid, 0, Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), timedRequestTimeoutMs=10000 - )) + ) # we actually want the exception here because we want to see a failure, so return False here self.logger.error( 'Incorrectly succeeded in opening basic commissioning window') @@ -413,13 +414,13 @@ def TestFailsafe(self, nodeid: int): self.logger.info( "Attempting to open enhanced commissioning window - this should fail since the failsafe is armed") try: - asyncio.run(self.devCtrl.SendCommand( + await self.devCtrl.SendCommand( nodeid, 0, Clusters.AdministratorCommissioning.Commands.OpenCommissioningWindow( commissioningTimeout=180, PAKEPasscodeVerifier=verifier, discriminator=discriminator, iterations=iterations, - salt=salt), timedRequestTimeoutMs=10000)) + salt=salt), timedRequestTimeoutMs=10000) # we actually want the exception here because we want to see a failure, so return False here self.logger.error( @@ -429,23 +430,23 @@ def TestFailsafe(self, nodeid: int): pass self.logger.info("Disarming failsafe on CASE connection") - err, resp = self.devCtrl.ZCLSend("GeneralCommissioning", "ArmFailSafe", nodeid, - 0, 0, dict(expiryLengthSeconds=0, breadcrumb=1), blocking=True) - if err != 0: + try: + resp = await self.devCtrl.SendCommand(nodeid, 0, + Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=0, breadcrumb=1)) + except IM.InteractionModelError as ex: self.logger.error( - "Failed to send arm failsafe command error is {} with im response{}".format(err, resp)) + "Failed to send arm failsafe command error is {}".format(ex.status)) return False self.logger.info( "Opening Commissioning Window - this should succeed since the failsafe was just disarmed") try: - asyncio.run( - self.devCtrl.SendCommand( - nodeid, - 0, - Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), - timedRequestTimeoutMs=10000 - )) + await self.devCtrl.SendCommand( + nodeid, + 0, + Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), + timedRequestTimeoutMs=10000 + ) except Exception: self.logger.error( 'Failed to open commissioning window after disarming failsafe') @@ -453,11 +454,12 @@ def TestFailsafe(self, nodeid: int): self.logger.info( "Attempting to arm failsafe over CASE - this should fail since the commissioning window is open") - err, resp = self.devCtrl.ZCLSend("GeneralCommissioning", "ArmFailSafe", nodeid, - 0, 0, dict(expiryLengthSeconds=60, breadcrumb=1), blocking=True) - if err != 0: + try: + resp = await self.devCtrl.SendCommand(nodeid, 0, + Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=60, breadcrumb=1)) + except IM.InteractionModelError as ex: self.logger.error( - "Failed to send arm failsafe command error is {} with im response{}".format(err, resp)) + "Failed to send arm failsafe command error is {}".format(ex.status)) return False if resp.errorCode is Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kBusyWithOtherAdmin: return True diff --git a/src/controller/python/test/test_scripts/failsafe_tests.py b/src/controller/python/test/test_scripts/failsafe_tests.py index 4b3838430ca213..d1a2034e7359d5 100755 --- a/src/controller/python/test/test_scripts/failsafe_tests.py +++ b/src/controller/python/test/test_scripts/failsafe_tests.py @@ -19,6 +19,7 @@ # Commissioning test. +import asyncio import os import sys from optparse import OptionParser @@ -99,7 +100,7 @@ def main(): nodeid=1), "Failed to finish key exchange") - FailIfNot(test.TestFailsafe(nodeid=1), "Failed failsafe test") + FailIfNot(asyncio.run(test.TestFailsafe(nodeid=1)), "Failed failsafe test") timeoutTicker.stop() From eb1231d5c0b70201e551b6ef37f91167ff27498c Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 13 May 2024 22:15:12 +0200 Subject: [PATCH 05/11] Convert TestOnOffCluster to use asyncio/SendCommand Remove ZCLSend API usage and call SendCommand directly. Also convert the test to a test using asyncio. --- .../python/test/test_scripts/base.py | 22 +++++++++++-------- .../commissioning_failure_test.py | 6 ++--- .../test/test_scripts/commissioning_test.py | 6 ++--- .../test/test_scripts/mobile-device-test.py | 15 +++++-------- .../test_scripts/split_commissioning_test.py | 11 +++++----- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 9f4c452f9c7807..3221d0f16a5a14 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -1097,20 +1097,24 @@ def SetNetworkCommissioningParameters(self, dataset: str): self.devCtrl.SetThreadOperationalDataset(bytes.fromhex(dataset)) return True - def TestOnOffCluster(self, nodeid: int, endpoint: int, group: int): + async def TestOnOffCluster(self, nodeid: int, endpoint: int): self.logger.info( "Sending On/Off commands to device {} endpoint {}".format(nodeid, endpoint)) - err, resp = self.devCtrl.ZCLSend("OnOff", "On", nodeid, - endpoint, group, {}, blocking=True) - if err != 0: + + try: + await self.devCtrl.SendCommand(nodeid, endpoint, + Clusters.OnOff.Commands.On()) + except IM.InteractionModelError as ex: self.logger.error( - "failed to send OnOff.On: error is {} with im response{}".format(err, resp)) + "failed to send OnOff.On: error is {}".format(ex.status)) return False - err, resp = self.devCtrl.ZCLSend("OnOff", "Off", nodeid, - endpoint, group, {}, blocking=True) - if err != 0: + + try: + await self.devCtrl.SendCommand(nodeid, endpoint, + Clusters.OnOff.Commands.Off()) + except IM.InteractionModelError as ex: self.logger.error( - "failed to send OnOff.Off: error is {} with im response {}".format(err, resp)) + "failed to send OnOff.Off: error is {}".format(ex.status)) return False return True diff --git a/src/controller/python/test/test_scripts/commissioning_failure_test.py b/src/controller/python/test/test_scripts/commissioning_failure_test.py index eca170601c7f29..d680682d567491 100755 --- a/src/controller/python/test/test_scripts/commissioning_failure_test.py +++ b/src/controller/python/test/test_scripts/commissioning_failure_test.py @@ -19,6 +19,7 @@ # Commissioning test. +import asyncio import os import sys from optparse import OptionParser @@ -121,9 +122,8 @@ def main(): FailIfNot(test.TestCommissionFailure(1, 0), "Failed to commission device") logger.info("Testing on off cluster") - FailIfNot(test.TestOnOffCluster(nodeid=1, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), "Failed to test on off cluster") + FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=1, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") timeoutTicker.stop() diff --git a/src/controller/python/test/test_scripts/commissioning_test.py b/src/controller/python/test/test_scripts/commissioning_test.py index b6adc0f477884d..4a7f15d6c3b085 100755 --- a/src/controller/python/test/test_scripts/commissioning_test.py +++ b/src/controller/python/test/test_scripts/commissioning_test.py @@ -19,6 +19,7 @@ # Commissioning test. +import asyncio import os import sys from optparse import OptionParser @@ -146,9 +147,8 @@ def main(): TestFail("Must provide device address or setup payload to commissioning the device") logger.info("Testing on off cluster") - FailIfNot(test.TestOnOffCluster(nodeid=options.nodeid, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), "Failed to test on off cluster") + FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=options.nodeid, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") FailIfNot(test.TestUsedTestCommissioner(), "Test commissioner check failed") diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py index 632c39299ea300..8f6f534dcefb96 100755 --- a/src/controller/python/test/test_scripts/mobile-device-test.py +++ b/src/controller/python/test/test_scripts/mobile-device-test.py @@ -102,9 +102,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): logger.info("Testing datamodel functions") logger.info("Testing on off cluster") - FailIfNot(test.TestOnOffCluster(nodeid=device_nodeid, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), "Failed to test on off cluster") + FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=device_nodeid, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") logger.info("Testing level control cluster") FailIfNot(asyncio.run(test.TestLevelControlCluster(nodeid=device_nodeid, @@ -112,9 +111,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): "Failed to test level control cluster") logger.info("Testing sending commands to non exist endpoint") - FailIfNot(not test.TestOnOffCluster(nodeid=device_nodeid, - endpoint=233, - group=GROUP_ID), "Failed to test on off cluster on non-exist endpoint") + FailIfNot(not asyncio.run(test.TestOnOffCluster(nodeid=device_nodeid, + endpoint=233)), "Failed to test on off cluster on non-exist endpoint") # Test experimental Python cluster objects API logger.info("Testing cluster objects API") @@ -149,9 +147,8 @@ def TestDatamodel(test: BaseTestHelper, device_nodeid: int): "Failed to validated re-subscription") logger.info("Testing on off cluster over resolved connection") - FailIfNot(test.TestOnOffCluster(nodeid=device_nodeid, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), "Failed to test on off cluster") + FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=device_nodeid, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster") logger.info("Testing writing/reading fabric sensitive data") asyncio.run(test.TestFabricSensitive(nodeid=device_nodeid)) diff --git a/src/controller/python/test/test_scripts/split_commissioning_test.py b/src/controller/python/test/test_scripts/split_commissioning_test.py index 47fedb3aadee8f..9233d58b90377d 100755 --- a/src/controller/python/test/test_scripts/split_commissioning_test.py +++ b/src/controller/python/test/test_scripts/split_commissioning_test.py @@ -19,6 +19,7 @@ # Commissioning test. +import asyncio import os import sys from optparse import OptionParser @@ -118,14 +119,12 @@ def main(): "Failed to commission device 2") logger.info("Testing on off cluster on device 1") - FailIfNot(test.TestOnOffCluster(nodeid=1, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), "Failed to test on off cluster on device 1") + FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=1, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster on device 1") logger.info("Testing on off cluster on device 2") - FailIfNot(test.TestOnOffCluster(nodeid=2, - endpoint=LIGHTING_ENDPOINT_ID, - group=GROUP_ID), "Failed to test on off cluster on device 2") + FailIfNot(asyncio.run(test.TestOnOffCluster(nodeid=2, + endpoint=LIGHTING_ENDPOINT_ID)), "Failed to test on off cluster on device 2") timeoutTicker.stop() From 5466ced30666e40028ee84c4d3e9ddbf1a18b309 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 13 May 2024 22:48:35 +0200 Subject: [PATCH 06/11] Drop TestResult helper class The class is no longer required. Test results are tested directly. --- .../python/test/test_scripts/base.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 3221d0f16a5a14..884030d029d044 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -178,29 +178,6 @@ def run(self): TestFail("Timeout", doCrash=True) -class TestResult: - def __init__(self, operationName, result): - self.operationName = operationName - self.result = result - - def assertStatusEqual(self, expected): - if self.result is None: - raise Exception(f"{self.operationName}: no result got") - if self.result.status != expected: - raise Exception( - f"{self.operationName}: expected status {expected}, got {self.result.status}") - return self - - def assertValueEqual(self, expected): - self.assertStatusEqual(0) - if self.result is None: - raise Exception(f"{self.operationName}: no result got") - if self.result.value != expected: - raise Exception( - f"{self.operationName}: expected value {expected}, got {self.result.value}") - return self - - class BaseTestHelper: def __init__(self, nodeid: int, paaTrustStorePath: str, testCommissioner: bool = False, keypair: p256keypair.P256Keypair = None): From b22516806717d65afb734bd4092543a3fc29b484 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 14 May 2024 09:28:11 +0200 Subject: [PATCH 07/11] Fix send_zcl_command argument formatting --- src/test_driver/mbed/integration_tests/common/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test_driver/mbed/integration_tests/common/utils.py b/src/test_driver/mbed/integration_tests/common/utils.py index 659122ba5c0051..2b1db4da30ab8e 100644 --- a/src/test_driver/mbed/integration_tests/common/utils.py +++ b/src/test_driver/mbed/integration_tests/common/utils.py @@ -125,7 +125,7 @@ def send_zcl_command(devCtrl, line): raise exceptions.UnknownCommand(cluster, command) try: - req = commandObj(**FormatZCLArguments(args[5:], command)) + req = commandObj(**FormatZCLArguments(args[5:], commandObj)) except BaseException: raise exceptions.UnknownCommand(cluster, command) From 138a68627b0300acd03cc8741dbb5a50893c9f7c Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 14 May 2024 19:16:18 +0200 Subject: [PATCH 08/11] Catch exception more specifically --- src/controller/python/test/test_scripts/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 884030d029d044..7fe931697444ce 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -41,6 +41,7 @@ from chip import ChipDeviceCtrl from chip.ChipStack import ChipStack from chip.crypto import p256keypair +from chip.exceptions import ChipStackError from chip.utils import CommissioningBuildingBlocks from cirque_restart_remote_device import restartRemoteDevice from ecdsa import NIST256p @@ -375,7 +376,7 @@ async def TestFailsafe(self, nodeid: int): self.logger.error( 'Incorrectly succeeded in opening basic commissioning window') return False - except Exception: + except ChipStackError: pass # TODO: @@ -403,7 +404,7 @@ async def TestFailsafe(self, nodeid: int): self.logger.error( 'Incorrectly succeeded in opening enhanced commissioning window') return False - except Exception: + except ChipStackError: pass self.logger.info("Disarming failsafe on CASE connection") @@ -1197,7 +1198,7 @@ class AttributeWriteRequest: if req.expected_status != IM.Status.Success: raise AssertionError( f"Write attribute {req.attribute.__qualname__} expects failure but got success response") - except Exception as ex: + except ChipStackError as ex: if req.expected_status != IM.Status.Success: continue else: From fd30b2401194f8ab91e291e0f35d52afeccfc08a Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 14 May 2024 22:32:00 +0200 Subject: [PATCH 09/11] Fix TestWriteBasicAttributes for all cases It seems TestWriteBasicAttributes did not correctly write the attributes. The broad exception handling seems to have hidden this issue even. Make sure the attributes with the correct value get written, and check for unexpected and expected IM errors in the per-attribute results specifically. --- .../python/test/test_scripts/base.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 7fe931697444ce..4d1d11bbb1e981 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -1193,16 +1193,16 @@ class AttributeWriteRequest: failed_attribute_write = [] for req in requests: try: - try: - await self.devCtrl.WriteAttribute(nodeid, [(endpoint, req.attribute, 0)]) - if req.expected_status != IM.Status.Success: - raise AssertionError( - f"Write attribute {req.attribute.__qualname__} expects failure but got success response") - except ChipStackError as ex: - if req.expected_status != IM.Status.Success: - continue - else: - raise ex + # Errors tested here is in the per-attribute result list (type AttributeStatus) + write_res = await self.devCtrl.WriteAttribute(nodeid, [(endpoint, req.attribute(req.value))]) + status = write_res[0].Status + if req.expected_status != status: + raise AssertionError( + f"Write attribute {req.attribute.__qualname__} expects {req.expected_status} but got {status}") + + # Only execute read tests where write is successful. + if req.expected_status != IM.Status.Success: + continue res = await self.devCtrl.ReadAttribute(nodeid, [(endpoint, req.attribute)]) val = res[endpoint][req.cluster][req.attribute] From 90c50e42fd85b6402bcc7c939cfd7bd26dae7093 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 15 May 2024 08:41:47 +0200 Subject: [PATCH 10/11] Fix TestFailsafe by catching correct exception --- src/controller/python/test/test_scripts/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 4d1d11bbb1e981..fc4366340558c2 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -376,7 +376,7 @@ async def TestFailsafe(self, nodeid: int): self.logger.error( 'Incorrectly succeeded in opening basic commissioning window') return False - except ChipStackError: + except IM.InteractionModelError: pass # TODO: @@ -404,7 +404,7 @@ async def TestFailsafe(self, nodeid: int): self.logger.error( 'Incorrectly succeeded in opening enhanced commissioning window') return False - except ChipStackError: + except IM.InteractionModelError: pass self.logger.info("Disarming failsafe on CASE connection") From 33cedd717b19cae2eb281cced7c9ffac5e1819ca Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 15 May 2024 11:36:45 +0200 Subject: [PATCH 11/11] Drop unused import --- src/controller/python/test/test_scripts/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index fc4366340558c2..3f9f76d9101874 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -41,7 +41,6 @@ from chip import ChipDeviceCtrl from chip.ChipStack import ChipStack from chip.crypto import p256keypair -from chip.exceptions import ChipStackError from chip.utils import CommissioningBuildingBlocks from cirque_restart_remote_device import restartRemoteDevice from ecdsa import NIST256p