diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 9fc9300f4c8116..3f9f76d9101874 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): @@ -368,15 +345,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,17 +365,17 @@ 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') return False - except Exception: + except IM.InteractionModelError: pass # TODO: @@ -413,39 +391,39 @@ 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( 'Incorrectly succeeded in opening enhanced commissioning window') return False - except Exception: + except IM.InteractionModelError: 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 +431,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 @@ -1095,50 +1074,48 @@ 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 - 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: @@ -1171,29 +1148,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: @@ -1217,16 +1192,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 Exception 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] 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/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() 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..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,20 +102,17 @@ 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(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") - 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") @@ -123,9 +120,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") @@ -134,9 +130,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") @@ -152,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() diff --git a/src/test_driver/mbed/integration_tests/common/utils.py b/src/test_driver/mbed/integration_tests/common/utils.py index 036b612d7ba18c..2b1db4da30ab8e 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:], commandObj)) + 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))