From 1627948039a6f516583e117e807dec768358a022 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Thu, 3 Jun 2021 20:03:16 +0200 Subject: [PATCH] Tests yaml (#6944) * Add a simple helper for ZAP templates in order to generate tests from YAML files * Update gen/ folder --- examples/chip-tool/BUILD.gn | 1 + examples/chip-tool/commands/tests/Commands.h | 855 ++++++++++++++++++ .../chip-tool/commands/tests/TestCommand.cpp | 46 + .../chip-tool/commands/tests/TestCommand.h | 41 + examples/chip-tool/main.cpp | 2 + .../templates/partials/test_cluster.zapt | 129 +++ examples/chip-tool/templates/templates.json | 10 + .../chip-tool/templates/tests-commands.zapt | 19 + src/app/tests/suites/OnOffCluster.yaml | 44 + src/app/tests/suites/TestCluster.yaml | 47 + .../common/ClusterTestGeneration.js | 245 +++++ .../zap-templates/common/ClustersHelper.js | 41 +- .../zap-templates/templates/chip/helper.js | 18 +- .../CHIP/templates/clusters-tests.zapt | 73 +- .../CHIP/templates/partials/test_cluster.zapt | 32 + .../Framework/CHIP/templates/templates.json | 5 + .../Framework/CHIPTests/CHIPClustersTests.m | 163 +++- 17 files changed, 1653 insertions(+), 118 deletions(-) create mode 100644 examples/chip-tool/commands/tests/Commands.h create mode 100644 examples/chip-tool/commands/tests/TestCommand.cpp create mode 100644 examples/chip-tool/commands/tests/TestCommand.h create mode 100644 examples/chip-tool/templates/partials/test_cluster.zapt create mode 100644 examples/chip-tool/templates/tests-commands.zapt create mode 100644 src/app/tests/suites/OnOffCluster.yaml create mode 100644 src/app/tests/suites/TestCluster.yaml create mode 100644 src/app/zap-templates/common/ClusterTestGeneration.js create mode 100644 src/darwin/Framework/CHIP/templates/partials/test_cluster.zapt diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index 2210f80313c578..2e202081f43122 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -29,6 +29,7 @@ executable("chip-tool") { "commands/payload/AdditionalDataParseCommand.cpp", "commands/payload/SetupPayloadParseCommand.cpp", "commands/reporting/ReportingCommand.cpp", + "commands/tests/TestCommand.cpp", "config/PersistentStorage.cpp", "main.cpp", ] diff --git a/examples/chip-tool/commands/tests/Commands.h b/examples/chip-tool/commands/tests/Commands.h new file mode 100644 index 00000000000000..37d9ffdfd8a335 --- /dev/null +++ b/examples/chip-tool/commands/tests/Commands.h @@ -0,0 +1,855 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +// THIS FILE IS GENERATED BY ZAP + +#pragma once + +#include "TestCommand.h" + +class TestCluster : public TestCommand +{ +public: + TestCluster() : TestCommand("TestCluster") {} + + /////////// TestCommand Interface ///////// + CHIP_ERROR NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + switch (mTestIndex) + { + case 0: + err = TestSendClusterTestClusterCommandTest_0(); + break; + case 1: + err = TestSendClusterTestClusterCommandTestNotHandled_1(); + break; + case 2: + err = TestSendClusterTestClusterCommandTestSpecific_2(); + break; + case 3: + err = TestSendClusterTestClusterCommandWriteAttribute_3(); + break; + case 4: + err = TestSendClusterTestClusterCommandReadAttribute_4(); + break; + } + mTestIndex++; + + if (mTestCount == mTestIndex || CHIP_NO_ERROR != err) + { + ChipLogProgress(chipTool, "TestCluster: %s", chip::ErrorStr(err)); + SetCommandExitStatus(CHIP_NO_ERROR == err); + } + + return err; + } + +private: + uint16_t mTestIndex = 0; + uint16_t mTestCount = 5; + + // + // Tests methods + // + + // Test Send Test Command + typedef void (*SuccessCallback_0)(void * context); + chip::Callback::Callback * mOnSuccessCallback_0 = nullptr; + chip::Callback::Callback * mOnFailureCallback_0 = nullptr; + bool mIsFailureExpected_0 = 0; + + CHIP_ERROR TestSendClusterTestClusterCommandTest_0() + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Command: Sending command..."); + + mOnFailureCallback_0 = + new chip::Callback::Callback(OnTestSendClusterTestClusterCommandTest_0_FailureResponse, this); + mOnSuccessCallback_0 = + new chip::Callback::Callback(OnTestSendClusterTestClusterCommandTest_0_SuccessResponse, this); + + chip::Controller::TestClusterCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.Test(mOnSuccessCallback_0->Cancel(), mOnFailureCallback_0->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_0; + delete mOnSuccessCallback_0; + } + + return err; + } + + static void OnTestSendClusterTestClusterCommandTest_0_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Command: Failure Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_0; + delete runner->mOnSuccessCallback_0; + + if (runner->mIsFailureExpected_0 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterTestClusterCommandTest_0_SuccessResponse(void * context) + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Command: Success Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_0; + delete runner->mOnSuccessCallback_0; + + if (runner->mIsFailureExpected_0 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Send Test Not Handled Command + typedef void (*SuccessCallback_1)(void * context); + chip::Callback::Callback * mOnSuccessCallback_1 = nullptr; + chip::Callback::Callback * mOnFailureCallback_1 = nullptr; + bool mIsFailureExpected_1 = 1; + + CHIP_ERROR TestSendClusterTestClusterCommandTestNotHandled_1() + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Not Handled Command: Sending command..."); + + mOnFailureCallback_1 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandTestNotHandled_1_FailureResponse, this); + mOnSuccessCallback_1 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandTestNotHandled_1_SuccessResponse, this); + + chip::Controller::TestClusterCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.TestNotHandled(mOnSuccessCallback_1->Cancel(), mOnFailureCallback_1->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_1; + delete mOnSuccessCallback_1; + } + + return err; + } + + static void OnTestSendClusterTestClusterCommandTestNotHandled_1_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Not Handled Command: Failure Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_1; + delete runner->mOnSuccessCallback_1; + + if (runner->mIsFailureExpected_1 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterTestClusterCommandTestNotHandled_1_SuccessResponse(void * context) + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Not Handled Command: Success Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_1; + delete runner->mOnSuccessCallback_1; + + if (runner->mIsFailureExpected_1 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Send Test Specific Command + typedef void (*SuccessCallback_2)(void * context, uint8_t returnValue); + chip::Callback::Callback * mOnSuccessCallback_2 = nullptr; + chip::Callback::Callback * mOnFailureCallback_2 = nullptr; + bool mIsFailureExpected_2 = 0; + + CHIP_ERROR TestSendClusterTestClusterCommandTestSpecific_2() + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Specific Command: Sending command..."); + + mOnFailureCallback_2 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandTestSpecific_2_FailureResponse, this); + mOnSuccessCallback_2 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandTestSpecific_2_SuccessResponse, this); + + chip::Controller::TestClusterCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.TestSpecific(mOnSuccessCallback_2->Cancel(), mOnFailureCallback_2->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_2; + delete mOnSuccessCallback_2; + } + + return err; + } + + static void OnTestSendClusterTestClusterCommandTestSpecific_2_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Specific Command: Failure Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_2; + delete runner->mOnSuccessCallback_2; + + if (runner->mIsFailureExpected_2 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterTestClusterCommandTestSpecific_2_SuccessResponse(void * context, uint8_t returnValue) + { + ChipLogProgress(chipTool, "Test Cluster - Send Test Specific Command: Success Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_2; + delete runner->mOnSuccessCallback_2; + + if (runner->mIsFailureExpected_2 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + if (returnValue != 7) + { + ChipLogError(chipTool, "Error: Value mismatch. Expected: '%s'", "7"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Write attribute BOOLEAN + typedef void (*SuccessCallback_3)(void * context, uint8_t boolean); + chip::Callback::Callback * mOnSuccessCallback_3 = nullptr; + chip::Callback::Callback * mOnFailureCallback_3 = nullptr; + bool mIsFailureExpected_3 = 0; + + CHIP_ERROR TestSendClusterTestClusterCommandWriteAttribute_3() + { + ChipLogProgress(chipTool, "Test Cluster - Write attribute BOOLEAN: Sending command..."); + + mOnFailureCallback_3 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandWriteAttribute_3_FailureResponse, this); + mOnSuccessCallback_3 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandWriteAttribute_3_SuccessResponse, this); + + chip::Controller::TestClusterCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.WriteAttributeBoolean(mOnSuccessCallback_3->Cancel(), mOnFailureCallback_3->Cancel(), 1); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_3; + delete mOnSuccessCallback_3; + } + + return err; + } + + static void OnTestSendClusterTestClusterCommandWriteAttribute_3_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "Test Cluster - Write attribute BOOLEAN: Failure Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_3; + delete runner->mOnSuccessCallback_3; + + if (runner->mIsFailureExpected_3 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterTestClusterCommandWriteAttribute_3_SuccessResponse(void * context, uint8_t boolean) + { + ChipLogProgress(chipTool, "Test Cluster - Write attribute BOOLEAN: Success Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_3; + delete runner->mOnSuccessCallback_3; + + if (runner->mIsFailureExpected_3 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Read attribute BOOLEAN + typedef void (*SuccessCallback_4)(void * context, uint8_t boolean); + chip::Callback::Callback * mOnSuccessCallback_4 = nullptr; + chip::Callback::Callback * mOnFailureCallback_4 = nullptr; + bool mIsFailureExpected_4 = 0; + + CHIP_ERROR TestSendClusterTestClusterCommandReadAttribute_4() + { + ChipLogProgress(chipTool, "Test Cluster - Read attribute BOOLEAN: Sending command..."); + + mOnFailureCallback_4 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandReadAttribute_4_FailureResponse, this); + mOnSuccessCallback_4 = new chip::Callback::Callback( + OnTestSendClusterTestClusterCommandReadAttribute_4_SuccessResponse, this); + + chip::Controller::TestClusterCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.ReadAttributeBoolean(mOnSuccessCallback_4->Cancel(), mOnFailureCallback_4->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_4; + delete mOnSuccessCallback_4; + } + + return err; + } + + static void OnTestSendClusterTestClusterCommandReadAttribute_4_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "Test Cluster - Read attribute BOOLEAN: Failure Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_4; + delete runner->mOnSuccessCallback_4; + + if (runner->mIsFailureExpected_4 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterTestClusterCommandReadAttribute_4_SuccessResponse(void * context, uint8_t boolean) + { + ChipLogProgress(chipTool, "Test Cluster - Read attribute BOOLEAN: Success Response"); + + TestCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_4; + delete runner->mOnSuccessCallback_4; + + if (runner->mIsFailureExpected_4 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + if (boolean != 1) + { + ChipLogError(chipTool, "Error: Value mismatch. Expected: '%s'", "1"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } +}; + +class OnOffCluster : public TestCommand +{ +public: + OnOffCluster() : TestCommand("OnOffCluster") {} + + /////////// TestCommand Interface ///////// + CHIP_ERROR NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + switch (mTestIndex) + { + case 0: + err = TestSendClusterOnOffCommandReadAttribute_0(); + break; + case 1: + err = TestSendClusterOnOffCommandOn_1(); + break; + case 2: + err = TestSendClusterOnOffCommandReadAttribute_2(); + break; + case 3: + err = TestSendClusterOnOffCommandOff_3(); + break; + case 4: + err = TestSendClusterOnOffCommandReadAttribute_4(); + break; + } + mTestIndex++; + + if (mTestCount == mTestIndex || CHIP_NO_ERROR != err) + { + ChipLogProgress(chipTool, "OnOffCluster: %s", chip::ErrorStr(err)); + SetCommandExitStatus(CHIP_NO_ERROR == err); + } + + return err; + } + +private: + uint16_t mTestIndex = 0; + uint16_t mTestCount = 5; + + // + // Tests methods + // + + // Test Check on/off attribute value is false when starting + typedef void (*SuccessCallback_0)(void * context, uint8_t onOff); + chip::Callback::Callback * mOnSuccessCallback_0 = nullptr; + chip::Callback::Callback * mOnFailureCallback_0 = nullptr; + bool mIsFailureExpected_0 = 0; + + CHIP_ERROR TestSendClusterOnOffCommandReadAttribute_0() + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is false when starting: Sending command..."); + + mOnFailureCallback_0 = new chip::Callback::Callback( + OnTestSendClusterOnOffCommandReadAttribute_0_FailureResponse, this); + mOnSuccessCallback_0 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandReadAttribute_0_SuccessResponse, this); + + chip::Controller::OnOffCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.ReadAttributeOnOff(mOnSuccessCallback_0->Cancel(), mOnFailureCallback_0->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_0; + delete mOnSuccessCallback_0; + } + + return err; + } + + static void OnTestSendClusterOnOffCommandReadAttribute_0_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is false when starting: Failure Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_0; + delete runner->mOnSuccessCallback_0; + + if (runner->mIsFailureExpected_0 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterOnOffCommandReadAttribute_0_SuccessResponse(void * context, uint8_t onOff) + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is false when starting: Success Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_0; + delete runner->mOnSuccessCallback_0; + + if (runner->mIsFailureExpected_0 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + if (onOff != 0) + { + ChipLogError(chipTool, "Error: Value mismatch. Expected: '%s'", "0"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Send On Command + typedef void (*SuccessCallback_1)(void * context); + chip::Callback::Callback * mOnSuccessCallback_1 = nullptr; + chip::Callback::Callback * mOnFailureCallback_1 = nullptr; + bool mIsFailureExpected_1 = 0; + + CHIP_ERROR TestSendClusterOnOffCommandOn_1() + { + ChipLogProgress(chipTool, "On/Off - Send On Command: Sending command..."); + + mOnFailureCallback_1 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandOn_1_FailureResponse, this); + mOnSuccessCallback_1 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandOn_1_SuccessResponse, this); + + chip::Controller::OnOffCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.On(mOnSuccessCallback_1->Cancel(), mOnFailureCallback_1->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_1; + delete mOnSuccessCallback_1; + } + + return err; + } + + static void OnTestSendClusterOnOffCommandOn_1_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "On/Off - Send On Command: Failure Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_1; + delete runner->mOnSuccessCallback_1; + + if (runner->mIsFailureExpected_1 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterOnOffCommandOn_1_SuccessResponse(void * context) + { + ChipLogProgress(chipTool, "On/Off - Send On Command: Success Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_1; + delete runner->mOnSuccessCallback_1; + + if (runner->mIsFailureExpected_1 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Check on/off attribute value is true after on command + typedef void (*SuccessCallback_2)(void * context, uint8_t onOff); + chip::Callback::Callback * mOnSuccessCallback_2 = nullptr; + chip::Callback::Callback * mOnFailureCallback_2 = nullptr; + bool mIsFailureExpected_2 = 0; + + CHIP_ERROR TestSendClusterOnOffCommandReadAttribute_2() + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is true after on command: Sending command..."); + + mOnFailureCallback_2 = new chip::Callback::Callback( + OnTestSendClusterOnOffCommandReadAttribute_2_FailureResponse, this); + mOnSuccessCallback_2 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandReadAttribute_2_SuccessResponse, this); + + chip::Controller::OnOffCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.ReadAttributeOnOff(mOnSuccessCallback_2->Cancel(), mOnFailureCallback_2->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_2; + delete mOnSuccessCallback_2; + } + + return err; + } + + static void OnTestSendClusterOnOffCommandReadAttribute_2_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is true after on command: Failure Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_2; + delete runner->mOnSuccessCallback_2; + + if (runner->mIsFailureExpected_2 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterOnOffCommandReadAttribute_2_SuccessResponse(void * context, uint8_t onOff) + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is true after on command: Success Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_2; + delete runner->mOnSuccessCallback_2; + + if (runner->mIsFailureExpected_2 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + if (onOff != 1) + { + ChipLogError(chipTool, "Error: Value mismatch. Expected: '%s'", "1"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Send Off Command + typedef void (*SuccessCallback_3)(void * context); + chip::Callback::Callback * mOnSuccessCallback_3 = nullptr; + chip::Callback::Callback * mOnFailureCallback_3 = nullptr; + bool mIsFailureExpected_3 = 0; + + CHIP_ERROR TestSendClusterOnOffCommandOff_3() + { + ChipLogProgress(chipTool, "On/Off - Send Off Command: Sending command..."); + + mOnFailureCallback_3 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandOff_3_FailureResponse, this); + mOnSuccessCallback_3 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandOff_3_SuccessResponse, this); + + chip::Controller::OnOffCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.Off(mOnSuccessCallback_3->Cancel(), mOnFailureCallback_3->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_3; + delete mOnSuccessCallback_3; + } + + return err; + } + + static void OnTestSendClusterOnOffCommandOff_3_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "On/Off - Send Off Command: Failure Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_3; + delete runner->mOnSuccessCallback_3; + + if (runner->mIsFailureExpected_3 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterOnOffCommandOff_3_SuccessResponse(void * context) + { + ChipLogProgress(chipTool, "On/Off - Send Off Command: Success Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_3; + delete runner->mOnSuccessCallback_3; + + if (runner->mIsFailureExpected_3 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + // Test Check on/off attribute value is false after off command + typedef void (*SuccessCallback_4)(void * context, uint8_t onOff); + chip::Callback::Callback * mOnSuccessCallback_4 = nullptr; + chip::Callback::Callback * mOnFailureCallback_4 = nullptr; + bool mIsFailureExpected_4 = 0; + + CHIP_ERROR TestSendClusterOnOffCommandReadAttribute_4() + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is false after off command: Sending command..."); + + mOnFailureCallback_4 = new chip::Callback::Callback( + OnTestSendClusterOnOffCommandReadAttribute_4_FailureResponse, this); + mOnSuccessCallback_4 = + new chip::Callback::Callback(OnTestSendClusterOnOffCommandReadAttribute_4_SuccessResponse, this); + + chip::Controller::OnOffCluster cluster; + cluster.Associate(mDevice, 1); + + CHIP_ERROR err = CHIP_NO_ERROR; + + err = cluster.ReadAttributeOnOff(mOnSuccessCallback_4->Cancel(), mOnFailureCallback_4->Cancel()); + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_4; + delete mOnSuccessCallback_4; + } + + return err; + } + + static void OnTestSendClusterOnOffCommandReadAttribute_4_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is false after off command: Failure Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_4; + delete runner->mOnSuccessCallback_4; + + if (runner->mIsFailureExpected_4 == false) + { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendClusterOnOffCommandReadAttribute_4_SuccessResponse(void * context, uint8_t onOff) + { + ChipLogProgress(chipTool, "On/Off - Check on/off attribute value is false after off command: Success Response"); + + OnOffCluster * runner = reinterpret_cast(context); + + delete runner->mOnFailureCallback_4; + delete runner->mOnSuccessCallback_4; + + if (runner->mIsFailureExpected_4 == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + if (onOff != 0) + { + ChipLogError(chipTool, "Error: Value mismatch. Expected: '%s'", "0"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } +}; + +void registerCommandsTests(Commands & commands) +{ + const char * clusterName = "Tests"; + + commands_list clusterCommands = { + make_unique(), + make_unique(), + }; + + commands.Register(clusterName, clusterCommands); +} diff --git a/examples/chip-tool/commands/tests/TestCommand.cpp b/examples/chip-tool/commands/tests/TestCommand.cpp new file mode 100644 index 00000000000000..51b5192226c981 --- /dev/null +++ b/examples/chip-tool/commands/tests/TestCommand.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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. + * + */ + +#include "TestCommand.h" + +constexpr uint16_t kWaitDurationInSeconds = 30; + +CHIP_ERROR TestCommand::Run(PersistentStorage & storage, NodeId localId, NodeId remoteId) +{ + chip::Controller::CommissionerInitParams params; + + params.storageDelegate = &storage; + params.operationalCredentialsDelegate = &mOpCredsIssuer; + + ReturnErrorOnFailure(mCommissioner.SetUdpListenPort(storage.GetListenPort())); + ReturnErrorOnFailure(mCommissioner.Init(localId, params)); + ReturnErrorOnFailure(mCommissioner.ServiceEvents()); + ReturnErrorOnFailure(mCommissioner.GetDevice(remoteId, &mDevice)); + + ReturnErrorOnFailure(NextTest()); + + UpdateWaitForResponse(true); + WaitForResponse(kWaitDurationInSeconds); + + mCommissioner.ServiceEventSignal(); + mCommissioner.Shutdown(); + + VerifyOrReturnError(GetCommandExitStatus(), CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} diff --git a/examples/chip-tool/commands/tests/TestCommand.h b/examples/chip-tool/commands/tests/TestCommand.h new file mode 100644 index 00000000000000..574b9e609cecd6 --- /dev/null +++ b/examples/chip-tool/commands/tests/TestCommand.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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. + * + */ + +#pragma once + +#include "../../config/PersistentStorage.h" +#include "../common/Command.h" +#include + +class TestCommand : public Command +{ +public: + TestCommand(const char * commandName) : Command(commandName) {} + + /////////// Command Interface ///////// + CHIP_ERROR Run(PersistentStorage & storage, NodeId localId, NodeId remoteId) override; + + virtual CHIP_ERROR NextTest() = 0; + +protected: + ChipDeviceCommissioner mCommissioner; + ChipDevice * mDevice; + +private: + chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer; +}; diff --git a/examples/chip-tool/main.cpp b/examples/chip-tool/main.cpp index 2730abf0ac777e..08b5923a3b886c 100644 --- a/examples/chip-tool/main.cpp +++ b/examples/chip-tool/main.cpp @@ -23,6 +23,7 @@ #include "commands/pairing/Commands.h" #include "commands/payload/Commands.h" #include "commands/reporting/Commands.h" +#include "commands/tests/Commands.h" #include @@ -36,6 +37,7 @@ int main(int argc, char * argv[]) registerCommandsPayload(commands); registerCommandsPairing(commands); registerCommandsReporting(commands); + registerCommandsTests(commands); registerClusters(commands); return commands.Run(chip::kTestControllerNodeId, chip::kTestDeviceNodeId, argc, argv); diff --git a/examples/chip-tool/templates/partials/test_cluster.zapt b/examples/chip-tool/templates/partials/test_cluster.zapt new file mode 100644 index 00000000000000..e8393319139364 --- /dev/null +++ b/examples/chip-tool/templates/partials/test_cluster.zapt @@ -0,0 +1,129 @@ +{{#chip_tests tests}} +class {{asCamelCased filename false}}: public TestCommand +{ + public: + {{asCamelCased filename false}}(): TestCommand("{{filename}}") {} + + /////////// TestCommand Interface ///////// + CHIP_ERROR NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + switch (mTestIndex) + { + {{#chip_tests_items}} + case {{index}}: + err = TestSendCluster{{asCamelCased cluster false}}Command{{asCamelCased command false}}_{{index}}(); + break; + {{/chip_tests_items}} + } + mTestIndex++; + + if (mTestCount == mTestIndex || CHIP_NO_ERROR != err) + { + ChipLogProgress(chipTool, "{{filename}}: %s", chip::ErrorStr(err)); + SetCommandExitStatus(CHIP_NO_ERROR == err); + } + + return err; + } + + + private: + uint16_t mTestIndex = 0; + uint16_t mTestCount = {{totalTests}}; + + // + // Tests methods + // + + {{#chip_tests_items}} + // Test {{label}} + typedef void (*SuccessCallback_{{index}})(void * context{{#chip_tests_item_response_parameters}}, {{chipType}} {{asCamelCased name true}}{{/chip_tests_item_response_parameters}}); + chip::Callback::Callback * mOnSuccessCallback_{{index}} = nullptr; + chip::Callback::Callback * mOnFailureCallback_{{index}} = nullptr; + bool mIsFailureExpected_{{index}} = {{response.error}}; + + CHIP_ERROR TestSendCluster{{asCamelCased cluster false}}Command{{asCamelCased command false}}_{{index}}() + { + ChipLogProgress(chipTool, "{{cluster}} - {{label}}: Sending command..."); + + mOnFailureCallback_{{index}} = new chip::Callback::Callback(OnTestSendCluster{{asCamelCased cluster false}}Command{{asCamelCased command false}}_{{index}}_FailureResponse, this); + mOnSuccessCallback_{{index}} = new chip::Callback::Callback(OnTestSendCluster{{asCamelCased cluster false}}Command{{asCamelCased command false}}_{{index}}_SuccessResponse, this); + + chip::Controller::{{asCamelCased cluster false}}Cluster cluster; + cluster.Associate(mDevice, {{endpoint}}); + + CHIP_ERROR err = CHIP_NO_ERROR; + + {{#if isCommand}} + err = cluster.{{asCamelCased command false}}(mOnSuccessCallback_{{index}}->Cancel(), mOnFailureCallback_{{index}}->Cancel()); + {{else if isReadAttribute}} + err = cluster.ReadAttribute{{asCamelCased attribute false}}(mOnSuccessCallback_{{index}}->Cancel(), mOnFailureCallback_{{index}}->Cancel()); + {{else if isWriteAttribute}} + err = cluster.WriteAttribute{{asCamelCased attribute false}}(mOnSuccessCallback_{{index}}->Cancel(), mOnFailureCallback_{{index}}->Cancel(), {{#chip_tests_item_parameters}}{{definedValue}}{{/chip_tests_item_parameters}}); + {{else}} + err = CHIP_ERROR_NOT_IMPLEMENTED; + {{/if}} + + if (CHIP_NO_ERROR != err) + { + delete mOnFailureCallback_{{index}}; + delete mOnSuccessCallback_{{index}}; + } + + return err; + } + + static void OnTestSendCluster{{asCamelCased cluster false}}Command{{asCamelCased command false}}_{{index}}_FailureResponse(void * context, uint8_t status) + { + ChipLogProgress(chipTool, "{{cluster}} - {{label}}: Failure Response"); + + {{asCamelCased parent.filename false}} * runner = reinterpret_cast<{{asCamelCased parent.filename false}} *>(context); + + delete runner->mOnFailureCallback_{{index}}; + delete runner->mOnSuccessCallback_{{index}}; + + if (runner->mIsFailureExpected_{{index}} == false) { + ChipLogError(chipTool, "Error: The test was expecting a success callback. Got failure callback"); + runner->SetCommandExitStatus(false); + return; + } + + runner->NextTest(); + } + + static void OnTestSendCluster{{asCamelCased cluster false}}Command{{asCamelCased command false}}_{{index}}_SuccessResponse(void * context {{#chip_tests_item_response_parameters}}, {{chipType}} {{asCamelCased name true}}{{/chip_tests_item_response_parameters}}) + { + ChipLogProgress(chipTool, "{{cluster}} - {{label}}: Success Response"); + + {{asCamelCased parent.filename false}} * runner = reinterpret_cast<{{asCamelCased parent.filename false}} *>(context); + + delete runner->mOnFailureCallback_{{index}}; + delete runner->mOnSuccessCallback_{{index}}; + + if (runner->mIsFailureExpected_{{index}} == true) + { + ChipLogError(chipTool, "Error: The test was expecting a failure callback. Got success callback"); + runner->SetCommandExitStatus(false); + return; + } + + {{#chip_tests_item_response_parameters}} + {{#if hasExpectedValue}} + if ({{asCamelCased name true}} != {{expectedValue}}) + { + ChipLogError(chipTool, "Error: Value mismatch. Expected: '%s'", "{{expectedValue}}"); + runner->SetCommandExitStatus(false); + return; + } + {{/if}} + {{/chip_tests_item_response_parameters}} + + runner->NextTest(); + } + + {{/chip_tests_items}} +}; + +{{/chip_tests}} diff --git a/examples/chip-tool/templates/templates.json b/examples/chip-tool/templates/templates.json index 243b3e0427d142..9ca469a1617cf9 100644 --- a/examples/chip-tool/templates/templates.json +++ b/examples/chip-tool/templates/templates.json @@ -6,6 +6,7 @@ "../../../src/app/zap-templates/common/StringHelper.js", "../../../src/app/zap-templates/templates/app/helper.js", "../../../src/app/zap-templates/templates/chip/helper.js", + "../../../src/app/zap-templates/common/ClusterTestGeneration.js", "helper.js" ], "override": "../../../src/app/zap-templates/common/override.js", @@ -21,6 +22,10 @@ { "name": "cluster_header", "path": "../../../src/app/zap-templates/partials/cluster_header.zapt" + }, + { + "name": "test_cluster", + "path": "partials/test_cluster.zapt" } ], "templates": [ @@ -33,6 +38,11 @@ "path": "reporting-commands.zapt", "name": "Reporting Commands header", "output": "examples/chip-tool/commands/reporting/Commands.h" + }, + { + "path": "tests-commands.zapt", + "name": "Tests Commands header", + "output": "examples/chip-tool/commands/tests/Commands.h" } ] } diff --git a/examples/chip-tool/templates/tests-commands.zapt b/examples/chip-tool/templates/tests-commands.zapt new file mode 100644 index 00000000000000..86511fee7b79ce --- /dev/null +++ b/examples/chip-tool/templates/tests-commands.zapt @@ -0,0 +1,19 @@ +{{> header}} + +#pragma once + +#include "TestCommand.h" + +{{>test_cluster tests="TestCluster, OnOffCluster"}} + +void registerCommandsTests(Commands & commands) +{ + const char * clusterName = "Tests"; + + commands_list clusterCommands = { + make_unique(), + make_unique(), + }; + + commands.Register(clusterName, clusterCommands); +} diff --git a/src/app/tests/suites/OnOffCluster.yaml b/src/app/tests/suites/OnOffCluster.yaml new file mode 100644 index 00000000000000..774f85b76a04d4 --- /dev/null +++ b/src/app/tests/suites/OnOffCluster.yaml @@ -0,0 +1,44 @@ +# Copyright (c) 2021 Project CHIP Authors +# +# 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. + +name: OnOff Tests + +config: + cluster: "On/Off" + endpoint: 1 + +tests: + - label: "Check on/off attribute value is false when starting" + command: "readAttribute" + attribute: "on/off" + response: + value: 0 + + - label: "Send On Command" + command: "on" + + - label: "Check on/off attribute value is true after on command" + command: "readAttribute" + attribute: "on/off" + response: + value: 1 + + - label: "Send Off Command" + command: "off" + + - label: "Check on/off attribute value is false after off command" + command: "readAttribute" + attribute: "on/off" + response: + value: 0 diff --git a/src/app/tests/suites/TestCluster.yaml b/src/app/tests/suites/TestCluster.yaml new file mode 100644 index 00000000000000..0414f9418e8623 --- /dev/null +++ b/src/app/tests/suites/TestCluster.yaml @@ -0,0 +1,47 @@ +# Copyright (c) 2021 Project CHIP Authors +# +# 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. + +name: Test Cluster Tests + +config: + cluster: "Test Cluster" + endpoint: 1 + +tests: + - label: "Send Test Command" + command: "test" + + - label: "Send Test Not Handled Command" + command: "testNotHandled" + response: + error: 1 + + - label: "Send Test Specific Command" + command: "testSpecific" + response: + values: + - name: "returnValue" + value: 7 + + - label: "Write attribute BOOLEAN" + command: "writeAttribute" + attribute: "boolean" + arguments: + value: 1 + + - label: "Read attribute BOOLEAN" + command: "readAttribute" + attribute: "boolean" + response: + value: 1 diff --git a/src/app/zap-templates/common/ClusterTestGeneration.js b/src/app/zap-templates/common/ClusterTestGeneration.js new file mode 100644 index 00000000000000..519021da4741df --- /dev/null +++ b/src/app/zap-templates/common/ClusterTestGeneration.js @@ -0,0 +1,245 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +const basePath = '../../../../'; +const testPath = 'src/app/tests/suites/'; +const zapPath = basePath + 'third_party/zap/repo/'; +const YAML = require(zapPath + 'node_modules/yaml'); +const fs = require('fs'); +const path = require('path'); + +// Import helpers from zap core +const templateUtil = require(zapPath + 'src-electron/generator/template-util.js') + +const { Clusters, asBlocks, asPromise } = require('./ClustersHelper.js'); + +const kClusterName = 'cluster'; +const kEndpointName = 'endpoint'; +const kCommandName = 'command'; +const kIndexName = 'index'; +const kValuesName = 'values'; +const kArgumentsName = 'arguments'; +const kResponseName = 'response'; +const kResponseErrorName = 'error'; + +function setDefault(test, name, defaultValue) +{ + if (!(name in test)) { + if (defaultValue == null) { + const errorStr = 'Test with label "' + test.label + '" does not have any "' + name + '" defined.'; + throw new Error(errorStr); + } + + test[name] = defaultValue; + } +} + +function setDefaultType(test) +{ + const type = test[kCommandName]; + switch (type) { + case 'readAttribute': + test.isAttribute = true; + test.isReadAttribute = true; + break; + + case 'writeAttribute': + test.isAttribute = true; + test.isWriteAttribute = true; + break; + + default: + test.isCommand = true; + break; + } +} + +function setDefaultArguments(test) +{ + const defaultArguments = {}; + setDefault(test, kArgumentsName, defaultArguments); + + const defaultArgumentsValues = []; + setDefault(test[kArgumentsName], kValuesName, defaultArgumentsValues); + + if (!test.isWriteAttribute) { + return; + } + + if (!('value' in test[kArgumentsName])) { + const errorStr = 'Test with label "' + test.label + '" does not have a "value" defined.'; + throw new Error(errorStr); + } + + test[kArgumentsName].values.push({ name : test.attribute, value : test[kArgumentsName].value }); + delete test[kArgumentsName].value; +} + +function setDefaultResponse(test) +{ + const defaultResponse = {}; + setDefault(test, kResponseName, defaultResponse); + + const defaultResponseError = 0; + setDefault(test[kResponseName], kResponseErrorName, defaultResponseError); + + const defaultResponseValues = []; + setDefault(test[kResponseName], kValuesName, defaultResponseValues); + + if (!test.isReadAttribute) { + return; + } + + if (!('value' in test[kResponseName])) { + const errorStr = 'Test with label "' + test.label + '" does not have a "value" defined.'; + throw new Error(errorStr); + } + + test[kResponseName].values.push({ name : test.attribute, value : test[kResponseName].value }); + delete test[kResponseName].value; +} + +function setDefaults(test, index, defaultConfig) +{ + const defaultClusterName = defaultConfig[kClusterName] || null; + const defaultEndpointId = defaultConfig[kEndpointName] || null; + + setDefaultType(test); + setDefault(test, kIndexName, index); + setDefault(test, kClusterName, defaultClusterName); + setDefault(test, kEndpointName, defaultEndpointId); + setDefaultArguments(test); + setDefaultResponse(test); +} + +function parse(filename) +{ + const filepath = path.resolve(__dirname, basePath + testPath + filename + '.yaml'); + const data = fs.readFileSync(filepath, { encoding : 'utf8', flag : 'r' }); + const yaml = YAML.parse(data); + + const defaultConfig = yaml.config || []; + yaml.tests.forEach((test, index) => { + setDefaults(test, index, defaultConfig); + }); + + yaml.filename = filename; + yaml.totalTests = yaml.tests.length; + + return yaml; +} + +// +// Templates +// + +function chip_tests(items, options) +{ + const names = items.split(',').map(name => name.trim()); + const tests = names.map(item => parse(item)); + return templateUtil.collectBlocks(tests, options, this); +} + +function chip_tests_items(options) +{ + return templateUtil.collectBlocks(this.tests, options, this); +} + +function chip_tests_item_parameters(options) +{ + const clusterName = this.cluster; + const commandValues = this.arguments.values; + + let filterName; + let items; + + if (this.isCommand) { + filterName = this.command; + items = Clusters.getClientCommands(clusterName); + } else if (this.isAttribute) { + filterName = this.attribute; + items = Clusters.getServerAttributes(clusterName); + } else { + throw new Error("Unsupported command type: ", this); + } + + const promise = items.then(items => { + const filter = item => item.name.toLowerCase() == filterName.toLowerCase(); + const commandArgs = items.find(filter).arguments; + + const commands = commandArgs.map(commandArg => { + commandArg = JSON.parse(JSON.stringify(commandArg)); + + const expected = commandValues.find(value => value.name == commandArg.name); + commandArg.definedValue = expected.value; + + return commandArg; + }); + + return commands; + }); + + return asBlocks.call(this, promise, options); +} + +function chip_tests_item_response_parameters(options) +{ + const clusterName = this.cluster; + const responseValues = this.response.values; + + let filterName; + let items; + + if (this.isCommand) { + filterName = this.command; + items = Clusters.getClientCommands(clusterName); + } else if (this.isAttribute) { + filterName = this.attribute; + items = Clusters.getServerAttributes(clusterName); + } else { + throw new Error("Unsupported command type: ", this); + } + + const promise = items.then(items => { + const filter = item => item.name.toLowerCase() == filterName.toLowerCase(); + const responseArgs = items.find(filter).response.arguments; + + const responses = responseArgs.map(responseArg => { + responseArg = JSON.parse(JSON.stringify(responseArg)); + + const expected = responseValues.find(value => value.name == responseArg.name); + if (expected) { + responseArg.hasExpectedValue = true; + responseArg.expectedValue = expected.value; + } + + return responseArg; + }); + + return responses; + }); + + return asBlocks.call(this, promise, options); +} + +// +// Module exports +// +exports.chip_tests = chip_tests; +exports.chip_tests_items = chip_tests_items; +exports.chip_tests_item_parameters = chip_tests_item_parameters; +exports.chip_tests_item_response_parameters = chip_tests_item_response_parameters; diff --git a/src/app/zap-templates/common/ClustersHelper.js b/src/app/zap-templates/common/ClustersHelper.js index ca702396ba7087..0990d5c9c69f1a 100644 --- a/src/app/zap-templates/common/ClustersHelper.js +++ b/src/app/zap-templates/common/ClustersHelper.js @@ -324,11 +324,11 @@ function enhancedCommands(commands, types) }); }); - // This filter uses the assumption that a response to a command has a well defined name, such as - // (response name) == (command name + 'Response') or s/Request/Response. This is very often the case, - // but this is not always true since some clusters use the same response to answer different commands, such as the - // operational cluster. commands.forEach(command => { + // This filter uses the assumption that a response to a command has a well defined name, such as + // (response name) == (command name + 'Response') or s/Request/Response. This is very often the case, + // but this is not always true since some clusters use the same response to answer different commands, such as the + // operational cluster. const automaticFilter = response => (response.name == (command.name + 'Response') || (command.name.includes('Request') && response.name == (command.name.replace('Request', 'Response')))); const manualFilter = response => { @@ -348,6 +348,11 @@ function enhancedCommands(commands, types) if (response) { command.hasSpecificResponse = true; command.responseName = response.name; + command.response = response; + } else { + command.hasSpecificResponse = false; + command.responseName = 'DefaultSuccess'; + command.response = { arguments : [] }; } }); @@ -365,6 +370,12 @@ function enhancedAttributes(attributes, types) enhancedItem(attribute, types); }); + attributes.forEach(attribute => { + const argument = { name : attribute.name, chipType : attribute.chipType }; + attribute.arguments = [ argument ]; + attribute.response = { arguments : [ argument ] }; + }); + // At this stage, the 'attributes' array contains all attributes enabled for all endpoints. It means // that a lot of attributes are duplicated if a cluster is enabled on multiple endpoints but that's // not what the templates expect. So let's deduplicate them. @@ -410,6 +421,18 @@ Clusters.init = function(context, packageId) { // // Helpers: All // +function asBlocks(promise, options) +{ + const fn = pkgId => Clusters.init(this, pkgId).then(() => promise.then(data => templateUtil.collectBlocks(data, options, this))); + return templateUtil.ensureZclPackageId(this).then(fn).catch(err => console.log(err)); +} + +function asPromise(promise) +{ + const fn = pkgId => Clusters.init(this, pkgId).then(() => promise); + return templateUtil.ensureZclPackageId(this).then(fn).catch(err => console.log(err)); +} + Clusters.getClusters = function() { return this.ready.then(() => this._clusters); @@ -417,20 +440,20 @@ Clusters.getClusters = function() Clusters.getCommands = function(name, side) { - const filter = command => command.clusterName == name && command.clusterSide == side && command.name.includes('Response') == false; + const filter = command => command.clusterName.toLowerCase() == name.toLowerCase() && command.clusterSide == side && command.name.includes('Response') == false; return this.ready.then(() => this._commands.filter(filter)); } Clusters.getResponses = function(name, side) { - const filter = command => command.clusterName == name && command.clusterSide == side && command.name.includes('Response') == true; + const filter = command => command.clusterName.toLowerCase() == name.toLowerCase() && command.clusterSide == side && command.name.includes('Response') == true; return this.ready.then(() => this._commands.filter(filter)); } Clusters.getAttributes = function(name, side) { return this.ready.then(() => { - const code = this._clusters.find(cluster => cluster.name == name).id; + const code = this._clusters.find(cluster => cluster.name.toLowerCase() == name.toLowerCase()).id; const filter = attribute => attribute.clusterId == code && attribute.side == side; return this._attributes.filter(filter); }); @@ -487,4 +510,6 @@ Clusters.getServerAttributes = function(name) // // Module exports // -exports.Clusters = Clusters; +exports.Clusters = Clusters; +exports.asBlocks = asBlocks; +exports.asPromise = asPromise; diff --git a/src/app/zap-templates/templates/chip/helper.js b/src/app/zap-templates/templates/chip/helper.js index ddffc234420f70..a5fc164bb25c01 100644 --- a/src/app/zap-templates/templates/chip/helper.js +++ b/src/app/zap-templates/templates/chip/helper.js @@ -20,21 +20,9 @@ const zapPath = '../../../../../third_party/zap/repo/src-electron/'; const templateUtil = require(zapPath + 'generator/template-util.js') const zclHelper = require(zapPath + 'generator/helper-zcl.js') -const { Clusters } = require('../../common/ClustersHelper.js'); -const StringHelper = require('../../common/StringHelper.js'); -const ChipTypesHelper = require('../../common/ChipTypesHelper.js'); - -function asBlocks(promise, options) -{ - const fn = pkgId => Clusters.init(this, pkgId).then(() => promise.then(data => templateUtil.collectBlocks(data, options, this))); - return templateUtil.ensureZclPackageId(this).then(fn).catch(err => console.log(err)); -} - -function asPromise(promise) -{ - const fn = pkgId => Clusters.init(this, pkgId).then(() => promise); - return templateUtil.ensureZclPackageId(this).then(fn).catch(err => console.log(err)); -} +const { Clusters, asBlocks, asPromise } = require('../../common/ClustersHelper.js'); +const StringHelper = require('../../common/StringHelper.js'); +const ChipTypesHelper = require('../../common/ChipTypesHelper.js'); function throwErrorIfUndefined(item, errorMsg, conditions) { diff --git a/src/darwin/Framework/CHIP/templates/clusters-tests.zapt b/src/darwin/Framework/CHIP/templates/clusters-tests.zapt index 965eed74f3b600..024ba34d112c86 100644 --- a/src/darwin/Framework/CHIP/templates/clusters-tests.zapt +++ b/src/darwin/Framework/CHIP/templates/clusters-tests.zapt @@ -1,22 +1,7 @@ // // CHIPClustersTests.m // CHIPClustersTests -/** - * - * Copyright (c) 2021 Project CHIP Authors - * - * 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. - */ +{{>header}} // module headers #import @@ -122,25 +107,6 @@ CHIPDevice * GetPairedDevice(uint64_t deviceId) XCTAssertTrue(stopped); } -- (void)testSendClusterTestCommand -{ - XCTestExpectation * expectation = [self expectationWithDescription:@"TestClusterTestCommand"]; - - CHIPDevice * device = GetPairedDevice(kDeviceId); - dispatch_queue_t queue = dispatch_get_main_queue(); - CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; - XCTAssertNotNil(cluster); - - [cluster test:^(NSError * err, NSDictionary * values) { - NSLog(@"TestCluster test Error: %@", err); - XCTAssertEqual(err.code, 0); - [expectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; -} - - - (void)testReuseChipClusterObject { XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseCHIPClusterObjectFirstCall"]; @@ -171,42 +137,7 @@ CHIPDevice * GetPairedDevice(uint64_t deviceId) [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestNotHandledCommand -{ - XCTestExpectation * expectation = [self expectationWithDescription:@"TestClusterTestNotHandledCommand"]; - - CHIPDevice * device = GetPairedDevice(kDeviceId); - dispatch_queue_t queue = dispatch_get_main_queue(); - CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; - XCTAssertNotNil(cluster); - - [cluster testNotHandled:^(NSError * err, NSDictionary * values) { - NSLog(@"TestCluster testNotHandled Error: %@", err); - XCTAssertEqual(err.code, 1); - [expectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; -} - -- (void)testSendClusterTestSpecificCommand -{ - XCTestExpectation * expectation = [self expectationWithDescription:@"TestClusterTestSpecificCommand"]; - - CHIPDevice * device = GetPairedDevice(kDeviceId); - dispatch_queue_t queue = dispatch_get_main_queue(); - CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; - XCTAssertNotNil(cluster); - - [cluster testSpecific:^(NSError * err, NSDictionary * values) { - NSLog(@"TestCluster testSpecific Error: %@", err); - XCTAssertEqual(err.code, 0); - XCTAssertEqual([values[@"returnValue"] intValue], 7); - [expectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; -} +{{>test_cluster tests="TestCluster, OnOffCluster"}} {{#chip_client_clusters}} {{#chip_server_cluster_attributes}} diff --git a/src/darwin/Framework/CHIP/templates/partials/test_cluster.zapt b/src/darwin/Framework/CHIP/templates/partials/test_cluster.zapt new file mode 100644 index 00000000000000..bfbe9f398b4816 --- /dev/null +++ b/src/darwin/Framework/CHIP/templates/partials/test_cluster.zapt @@ -0,0 +1,32 @@ +{{#chip_tests tests}} +{{#chip_tests_items}} +- (void)testSendCluster{{asCamelCased parent.name false}}_{{index}}_{{asCamelCased command false}} +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"{{label}}"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIP{{asCamelCased cluster false}} * cluster = [[CHIP{{asCamelCased cluster false}} alloc] initWithDevice:device endpoint:{{endpoint}} queue:queue]; + XCTAssertNotNil(cluster); + + {{#if isCommand}} + [cluster {{command}}:^(NSError * err, NSDictionary * values) { + {{else if isReadAttribute}} + [cluster readAttribute{{asCamelCased attribute false}}WithResponseHandler:^(NSError * err, NSDictionary * values) { + {{else if isWriteAttribute}} + [cluster writeAttribute{{asCamelCased attribute false}}WithValue:{{#chip_tests_item_parameters}}{{definedValue}}{{/chip_tests_item_parameters}} responseHandler:^(NSError * err, NSDictionary * values) { + {{/if}} + NSLog(@"{{label}} Error: %@", err); + XCTAssertEqual(err.code, {{response.error}}); + {{#chip_tests_item_response_parameters}} + {{#if hasExpectedValue}} + XCTAssertEqual([values[@"{{#if parent.isReadAttribute}}value{{else}}{{name}}{{/if}}"] intValue], {{expectedValue}}); + {{/if}} + {{/chip_tests_item_response_parameters}} + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +{{/chip_tests_items}} + +{{/chip_tests}} diff --git a/src/darwin/Framework/CHIP/templates/templates.json b/src/darwin/Framework/CHIP/templates/templates.json index 646942e13d5dfb..6da34686bbf3b7 100644 --- a/src/darwin/Framework/CHIP/templates/templates.json +++ b/src/darwin/Framework/CHIP/templates/templates.json @@ -6,6 +6,7 @@ "../../../../../src/app/zap-templates/common/StringHelper.js", "../../../../../src/app/zap-templates/templates/app/helper.js", "../../../../../src/app/zap-templates/templates/chip/helper.js", + "../../../../../src/app/zap-templates/common/ClusterTestGeneration.js", "helper.js" ], "override": "../../../../../src/app/zap-templates/common/override.js", @@ -13,6 +14,10 @@ { "name": "header", "path": "../../../../../src/app/zap-templates/partials/header.zapt" + }, + { + "name": "test_cluster", + "path": "partials/test_cluster.zapt" } ], "templates": [ diff --git a/src/darwin/Framework/CHIPTests/CHIPClustersTests.m b/src/darwin/Framework/CHIPTests/CHIPClustersTests.m index 3f77de3e1581d7..0f5323c22082d1 100644 --- a/src/darwin/Framework/CHIPTests/CHIPClustersTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPClustersTests.m @@ -1,7 +1,7 @@ // // CHIPClustersTests.m // CHIPClustersTests -/** +/* * * Copyright (c) 2021 Project CHIP Authors * @@ -18,6 +18,8 @@ * limitations under the License. */ +// THIS FILE IS GENERATED BY ZAP + // module headers #import @@ -123,9 +125,9 @@ - (void)testShutdownStack XCTAssertTrue(stopped); } -- (void)testSendClusterTestCommand +- (void)testReuseChipClusterObject { - XCTestExpectation * expectation = [self expectationWithDescription:@"TestClusterTestCommand"]; + XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseCHIPClusterObjectFirstCall"]; CHIPDevice * device = GetPairedDevice(kDeviceId); dispatch_queue_t queue = dispatch_get_main_queue(); @@ -133,7 +135,19 @@ - (void)testSendClusterTestCommand XCTAssertNotNil(cluster); [cluster test:^(NSError * err, NSDictionary * values) { - NSLog(@"TestCluster test Error: %@", err); + NSLog(@"ReuseCHIPClusterObject test Error: %@", err); + XCTAssertEqual(err.code, 0); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; + + expectation = [self expectationWithDescription:@"ReuseCHIPClusterObjectSecondCall"]; + + // Reuse the CHIPCluster Object for multiple times. + + [cluster test:^(NSError * err, NSDictionary * values) { + NSLog(@"ReuseCHIPClusterObject test Error: %@", err); XCTAssertEqual(err.code, 0); [expectation fulfill]; }]; @@ -141,67 +155,168 @@ - (void)testSendClusterTestCommand [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testReuseChipClusterObject +- (void)testSendClusterTestClusterTests_0_Test { - XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseCHIPClusterObjectFirstCall"]; - + XCTestExpectation * expectation = [self expectationWithDescription:@"Send Test Command"]; CHIPDevice * device = GetPairedDevice(kDeviceId); dispatch_queue_t queue = dispatch_get_main_queue(); CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; XCTAssertNotNil(cluster); [cluster test:^(NSError * err, NSDictionary * values) { - NSLog(@"ReuseCHIPClusterObject test Error: %@", err); + NSLog(@"Send Test Command Error: %@", err); XCTAssertEqual(err.code, 0); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestClusterTests_1_TestNotHandled +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Send Test Not Handled Command"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); - expectation = [self expectationWithDescription:@"ReuseCHIPClusterObjectSecondCall"]; + [cluster testNotHandled:^(NSError * err, NSDictionary * values) { + NSLog(@"Send Test Not Handled Command Error: %@", err); + XCTAssertEqual(err.code, 1); + [expectation fulfill]; + }]; - // Reuse the CHIPCluster Object for multiple times. + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestClusterTests_2_TestSpecific +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Send Test Specific Command"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); - [cluster test:^(NSError * err, NSDictionary * values) { - NSLog(@"ReuseCHIPClusterObject test Error: %@", err); + [cluster testSpecific:^(NSError * err, NSDictionary * values) { + NSLog(@"Send Test Specific Command Error: %@", err); XCTAssertEqual(err.code, 0); + XCTAssertEqual([values[@"returnValue"] intValue], 7); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } - -- (void)testSendClusterTestNotHandledCommand +- (void)testSendClusterTestClusterTests_3_WriteAttribute { - XCTestExpectation * expectation = [self expectationWithDescription:@"TestClusterTestNotHandledCommand"]; + XCTestExpectation * expectation = [self expectationWithDescription:@"Write attribute BOOLEAN"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + [cluster writeAttributeBooleanWithValue:1 + responseHandler:^(NSError * err, NSDictionary * values) { + NSLog(@"Write attribute BOOLEAN Error: %@", err); + XCTAssertEqual(err.code, 0); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestClusterTests_4_ReadAttribute +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Read attribute BOOLEAN"]; CHIPDevice * device = GetPairedDevice(kDeviceId); dispatch_queue_t queue = dispatch_get_main_queue(); CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; XCTAssertNotNil(cluster); - [cluster testNotHandled:^(NSError * err, NSDictionary * values) { - NSLog(@"TestCluster testNotHandled Error: %@", err); - XCTAssertEqual(err.code, 1); + [cluster readAttributeBooleanWithResponseHandler:^(NSError * err, NSDictionary * values) { + NSLog(@"Read attribute BOOLEAN Error: %@", err); + XCTAssertEqual(err.code, 0); + XCTAssertEqual([values[@"value"] intValue], 1); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestSpecificCommand +- (void)testSendClusterOnOffTests_0_ReadAttribute { - XCTestExpectation * expectation = [self expectationWithDescription:@"TestClusterTestSpecificCommand"]; + XCTestExpectation * expectation = [self expectationWithDescription:@"Check on/off attribute value is false when starting"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPOnOff * cluster = [[CHIPOnOff alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + [cluster readAttributeOnOffWithResponseHandler:^(NSError * err, NSDictionary * values) { + NSLog(@"Check on/off attribute value is false when starting Error: %@", err); + XCTAssertEqual(err.code, 0); + XCTAssertEqual([values[@"value"] intValue], 0); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterOnOffTests_1_On +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Send On Command"]; CHIPDevice * device = GetPairedDevice(kDeviceId); dispatch_queue_t queue = dispatch_get_main_queue(); - CHIPTestCluster * cluster = [[CHIPTestCluster alloc] initWithDevice:device endpoint:1 queue:queue]; + CHIPOnOff * cluster = [[CHIPOnOff alloc] initWithDevice:device endpoint:1 queue:queue]; XCTAssertNotNil(cluster); - [cluster testSpecific:^(NSError * err, NSDictionary * values) { - NSLog(@"TestCluster testSpecific Error: %@", err); + [cluster on:^(NSError * err, NSDictionary * values) { + NSLog(@"Send On Command Error: %@", err); XCTAssertEqual(err.code, 0); - XCTAssertEqual([values[@"returnValue"] intValue], 7); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterOnOffTests_2_ReadAttribute +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Check on/off attribute value is true after on command"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPOnOff * cluster = [[CHIPOnOff alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + [cluster readAttributeOnOffWithResponseHandler:^(NSError * err, NSDictionary * values) { + NSLog(@"Check on/off attribute value is true after on command Error: %@", err); + XCTAssertEqual(err.code, 0); + XCTAssertEqual([values[@"value"] intValue], 1); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterOnOffTests_3_Off +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Send Off Command"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPOnOff * cluster = [[CHIPOnOff alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + [cluster off:^(NSError * err, NSDictionary * values) { + NSLog(@"Send Off Command Error: %@", err); + XCTAssertEqual(err.code, 0); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterOnOffTests_4_ReadAttribute +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Check on/off attribute value is false after off command"]; + CHIPDevice * device = GetPairedDevice(kDeviceId); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPOnOff * cluster = [[CHIPOnOff alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + [cluster readAttributeOnOffWithResponseHandler:^(NSError * err, NSDictionary * values) { + NSLog(@"Check on/off attribute value is false after off command Error: %@", err); + XCTAssertEqual(err.code, 0); + XCTAssertEqual([values[@"value"] intValue], 0); [expectation fulfill]; }];