diff --git a/examples/chip-tool-darwin/BUILD.gn b/examples/chip-tool-darwin/BUILD.gn index d7ad1c08ec7f8b..3c7d64b09a35fb 100644 --- a/examples/chip-tool-darwin/BUILD.gn +++ b/examples/chip-tool-darwin/BUILD.gn @@ -21,6 +21,7 @@ assert(chip_build_tools) executable("chip-tool-darwin") { sources = [ + "${chip_root}/zzz_generated/chip-tool-darwin/zap-generated/cluster/CHIPTestClustersObjc.mm", "commands/clusters/ModelCommandBridge.mm", "commands/common/CHIPCommandBridge.mm", "commands/common/CHIPCommandStorageDelegate.mm", diff --git a/examples/chip-tool-darwin/commands/tests/TestCommandBridge.h b/examples/chip-tool-darwin/commands/tests/TestCommandBridge.h new file mode 100644 index 00000000000000..f52fa0750ae139 --- /dev/null +++ b/examples/chip-tool-darwin/commands/tests/TestCommandBridge.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2022 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 "../common/CHIPCommandBridge.h" +#include +#include +#include +#include +#include + +#import + +constexpr uint16_t kTimeoutInSeconds = 90; + +class TestCommandBridge : public CHIPCommandBridge, public ValueChecker, public ConstraintsChecker, public PICSChecker { +public: + TestCommandBridge(const char * _Nonnull commandName) + : CHIPCommandBridge(commandName) + { + AddArgument("node-id", 0, UINT64_MAX, &mNodeId); + AddArgument("delayInMs", 0, UINT64_MAX, &mDelayInMs); + AddArgument("PICS", &mPICSFilePath); + } + + ~TestCommandBridge() {}; + + /////////// CHIPCommand Interface ///////// + CHIP_ERROR RunCommand() override + { + if (mPICSFilePath.HasValue()) { + PICS.SetValue(PICSBooleanReader::Read(mPICSFilePath.Value())); + } + + mCallbackQueue = dispatch_queue_create("com.chip-tool.command", DISPATCH_QUEUE_SERIAL); + + NextTest(); + return CHIP_NO_ERROR; + } + + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(kTimeoutInSeconds); } + + virtual void NextTest() = 0; + + void Exit(std::string message) override + { + ChipLogError(chipTool, " ***** Test Failure: %s\n", message.c_str()); + SetCommandExitStatus(CHIP_ERROR_INTERNAL); + } + + /////////// GlobalCommands Interface ///////// + void Log(NSString * _Nonnull message) + { + NSLog(@"%@", message); + NextTest(); + } + + void WaitForMs(unsigned int ms) + { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, ms * NSEC_PER_MSEC), mCallbackQueue, ^{ + NextTest(); + }); + } + + void UserPrompt(NSString * _Nonnull message) { NextTest(); } + + void WaitForCommissionee(chip::NodeId nodeId) + { + CHIPDeviceController * controller = [CHIPDeviceController sharedController]; + VerifyOrReturn(controller != nil, SetCommandExitStatus(CHIP_ERROR_INCORRECT_STATE)); + + [controller getConnectedDevice:nodeId + queue:mCallbackQueue + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + CHIP_ERROR err = [CHIPError errorToCHIPErrorCode:error]; + VerifyOrReturn(CHIP_NO_ERROR == err, SetCommandExitStatus(err)); + + mConnectedDevice = device; + NextTest(); + }]; + } + + CHIPDevice * _Nullable GetConnectedDevice(void) { return mConnectedDevice; } + +protected: + dispatch_queue_t _Nullable mCallbackQueue; + CHIPDevice * _Nullable mConnectedDevice; + chip::NodeId mNodeId; + + void Wait() + { + if (mDelayInMs.HasValue()) { + chip::test_utils::SleepMillis(mDelayInMs.Value()); + } + }; + + chip::Optional mDelayInMs; + chip::Optional mPICSFilePath; + chip::Optional mEndpointId; + chip::Optional mTimeout; + + bool CheckConstraintStartsWith( + const char * _Nonnull itemName, const NSString * _Nonnull current, const char * _Nonnull expected) + { + const chip::CharSpan value([current UTF8String], [current lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ConstraintsChecker::CheckConstraintStartsWith(itemName, value, expected); + } + + bool CheckConstraintEndsWith(const char * _Nonnull itemName, const NSString * _Nonnull current, const char * _Nonnull expected) + { + const chip::CharSpan value([current UTF8String], [current lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ConstraintsChecker::CheckConstraintEndsWith(itemName, value, expected); + } + + bool CheckConstraintIsUpperCase(const char * _Nonnull itemName, const NSString * _Nonnull current, bool expectUpperCase) + { + const chip::CharSpan value([current UTF8String], [current lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ConstraintsChecker::CheckConstraintIsUpperCase(itemName, value, expectUpperCase); + } + + bool CheckConstraintIsLowerCase(const char * _Nonnull itemName, const NSString * _Nonnull current, bool expectLowerCase) + { + const chip::CharSpan value([current UTF8String], [current lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ConstraintsChecker::CheckConstraintIsLowerCase(itemName, value, expectLowerCase); + } + + bool CheckConstraintIsHexString(const char * _Nonnull itemName, const NSString * _Nonnull current, bool expectHexString) + { + const chip::CharSpan value([current UTF8String], [current lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ConstraintsChecker::CheckConstraintIsHexString(itemName, value, expectHexString); + } + + bool CheckConstraintNotValue( + const char * _Nonnull itemName, const NSString * _Nonnull current, const NSString * _Nonnull expected) + { + const chip::CharSpan currentValue([current UTF8String], [current lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + const chip::CharSpan expectedValue([expected UTF8String], [expected lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ConstraintsChecker::CheckConstraintNotValue(itemName, currentValue, expectedValue); + } + + bool CheckConstraintNotValue(const char * _Nonnull itemName, const NSData * _Nonnull current, const NSData * _Nonnull expected) + { + const chip::ByteSpan currentValue(static_cast([current bytes]), [current length]); + const chip::ByteSpan expectedValue(static_cast([expected bytes]), [expected length]); + return ConstraintsChecker::CheckConstraintNotValue(itemName, currentValue, expectedValue); + } + + bool CheckConstraintNotValue(const char * _Nonnull itemName, const NSNumber * _Nonnull current, NSNumber * _Nonnull expected) + { + if ([current isEqualToNumber:expected]) { + Exit(std::string(itemName) + " got unexpected value: " + std::string([[current stringValue] UTF8String])); + return false; + } + + return true; + } + + template + bool CheckConstraintNotValue(const char * _Nonnull itemName, const NSNumber * _Nonnull current, T expected) + { + return CheckConstraintNotValue(itemName, current, @(expected)); + } + + template bool CheckConstraintNotValue(const char * _Nonnull itemName, NSError * _Nullable current, T expected) + { + NSNumber * currentValue = @([CHIPError errorToCHIPErrorCode:current].AsInteger()); + return CheckConstraintNotValue(itemName, currentValue, @(expected)); + } + + bool CheckValueAsString(const char * _Nonnull itemName, const id _Nonnull current, const NSString * _Nonnull expected) + { + NSString * data = current; + const chip::CharSpan currentValue([data UTF8String], [data lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + const chip::CharSpan expectedValue([expected UTF8String], [expected lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + return ValueChecker::CheckValueAsString(itemName, currentValue, expectedValue); + } + + bool CheckValueAsString(const char * _Nonnull itemName, const id _Nonnull current, const NSData * _Nonnull expected) + { + NSData * data = current; + const chip::ByteSpan currentValue(static_cast([data bytes]), [data length]); + const chip::ByteSpan expectedValue(static_cast([expected bytes]), [expected length]); + return ValueChecker::CheckValueAsString(itemName, currentValue, expectedValue); + } + + bool CheckValue(const char * _Nonnull itemName, NSNumber * _Nonnull current, NSNumber * _Nonnull expected) + { + if (![current isEqualToNumber:expected]) { + Exit(std::string(itemName) + " value mismatch: expected " + std::string([[expected stringValue] UTF8String]) + + " but got " + std::string([[current stringValue] UTF8String])); + return false; + } + + return true; + } + + bool CheckValue(const char * _Nonnull itemName, id _Nonnull current, NSNumber * _Nonnull expected) + { + NSNumber * currentValue = current; + return CheckValue(itemName, currentValue, expected); + } + + template bool CheckValue(const char * _Nonnull itemName, NSNumber * _Nonnull current, T expected) + { + return CheckValue(itemName, current, @(expected)); + } + + template bool CheckValue(const char * _Nonnull itemName, id _Nonnull current, T expected) + { + NSNumber * currentValue = current; + return CheckValue(itemName, currentValue, @(expected)); + } + + template bool CheckValue(const char * _Nonnull itemName, NSError * _Nullable current, T expected) + { + + NSNumber * currentValue = @([CHIPError errorToCHIPErrorCode:current].AsInteger()); + return CheckValue(itemName, currentValue, @(expected)); + } + + template bool CheckValue(const char * _Nonnull itemName, T current, U expected) + { + + return ValueChecker::CheckValue(itemName, current, expected); + } + + bool CheckValueNonNull(const char * _Nonnull itemName, id _Nullable current) + { + if (current != nil) { + return true; + } + + Exit(std::string(itemName) + " expected to not be null but is"); + return false; + } + + bool CheckValueNull(const char * _Nonnull itemName, id _Nullable current) + { + if (current == nil) { + return true; + } + + Exit(std::string(itemName) + " expected to be null but isn't"); + return false; + } +}; diff --git a/examples/chip-tool-darwin/main.mm b/examples/chip-tool-darwin/main.mm index 49b1f199af29cc..d5bfae7e74e648 100644 --- a/examples/chip-tool-darwin/main.mm +++ b/examples/chip-tool-darwin/main.mm @@ -19,12 +19,15 @@ #include "commands/common/Commands.h" #include "commands/pairing/Commands.h" + #include +#include int main(int argc, const char * argv[]) { Commands commands; registerCommandsPairing(commands); + registerCommandsTests(commands); registerClusters(commands); return commands.Run(argc, (char **) argv); } diff --git a/src/darwin/Framework/CHIP/templates/CHIPTestClustersObjc-src.zapt b/examples/chip-tool-darwin/templates/CHIPTestClustersObjc-src.zapt similarity index 95% rename from src/darwin/Framework/CHIP/templates/CHIPTestClustersObjc-src.zapt rename to examples/chip-tool-darwin/templates/CHIPTestClustersObjc-src.zapt index b0cc4361893a34..bfe2b051368e38 100644 --- a/src/darwin/Framework/CHIP/templates/CHIPTestClustersObjc-src.zapt +++ b/examples/chip-tool-darwin/templates/CHIPTestClustersObjc-src.zapt @@ -1,8 +1,9 @@ {{#if (chip_has_client_clusters)}} {{> header}} #import +#import -#import "CHIPCallbackBridge_internal.h" +#import "CHIP/zap-generated/CHIPCallbackBridge_internal.h" #import "CHIPCluster_internal.h" #import "CHIPDevice.h" #import "CHIPDevice_Internal.h" diff --git a/src/darwin/Framework/CHIP/templates/CHIPTestClustersObjc.zapt b/examples/chip-tool-darwin/templates/CHIPTestClustersObjc.zapt similarity index 100% rename from src/darwin/Framework/CHIP/templates/CHIPTestClustersObjc.zapt rename to examples/chip-tool-darwin/templates/CHIPTestClustersObjc.zapt diff --git a/examples/chip-tool-darwin/templates/helper.js b/examples/chip-tool-darwin/templates/helper.js new file mode 100644 index 00000000000000..9ab786467b8cf4 --- /dev/null +++ b/examples/chip-tool-darwin/templates/helper.js @@ -0,0 +1,61 @@ +/* + * + * 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. + */ + +// Import helpers from zap core +const zapPath = '../../../third_party/zap/repo/dist/src-electron/'; +const { asB_locks, ensureClusters } = require('../../../src/app/zap-templates/common/ClustersHelper.js'); +const templateUtil = require(zapPath + 'generator/template-util.js'); +const zclHelper = require(zapPath + 'generator/helper-zcl.js'); +const zclQuery = require(zapPath + 'db/query-zcl.js'); + +const ChipTypesHelper = require('../../../src/app/zap-templates/common/ChipTypesHelper.js'); + +function asHyphenatedLower(name) +{ + name = name.replace(/\s+/g, '').replace(/\.?([A-Z])/g, function(x) { + return '-' + x + }) + return name.substring(1).toLowerCase(); +} + +function toLowerCase(name) +{ + return name.toLowerCase(); +} + +function getCommands() +{ + return ensureClusters(this).getClientCommands(this.name); +} + +function hasCommands() +{ + return getCommands.call(this).then(commands => { return !!commands.length }); +} + +function hasArguments() +{ + return !!this.arguments.length +} + +// +// Module exports +// +exports.asHyphenatedLower = asHyphenatedLower; +exports.hasCommands = hasCommands; +exports.toLowerCase = toLowerCase +exports.hasArguments = hasArguments; diff --git a/examples/chip-tool-darwin/templates/partials/check_test_value.zapt b/examples/chip-tool-darwin/templates/partials/check_test_value.zapt new file mode 100644 index 00000000000000..cc9872bc4cdaec --- /dev/null +++ b/examples/chip-tool-darwin/templates/partials/check_test_value.zapt @@ -0,0 +1,46 @@ +{{#if isOptional}} + {{! This just means we expect a non-nil value. Go ahead and check the value, + stripping the optionality off. }} + {{>check_test_value actual=actual expected=expected cluster=cluster isOptional=false}} +{{else if isNullable}} + {{#if (isLiteralNull expected)}} + VerifyOrReturn(CheckValueNull("{{label}}", {{actual}})); + {{else}} + {{#if (chip_tests_variables_has expected)}} + {{! Expected value is also a nullable. }} + if ({{expected}} == nil) { + VerifyOrReturn(CheckValueNull("{{label}}", {{actual}})); + } else { + {{/if}} + VerifyOrReturn(CheckValueNonNull("{{label}}", {{actual}})); + {{>check_test_value actual=actual expected=expected cluster=cluster isNullable=false}} + {{#if (chip_tests_variables_has expected)}} + } + {{/if}} + {{/if}} +{{else if isArray}} + VerifyOrReturn(CheckValue("{{label}}", [{{actual}} count], static_cast({{expected.length}}))); + {{#each expected}} + {{>check_test_value actual=(concat ../actual "[" @index "]") expected=this cluster=../cluster isArray=false type=../type parent=../parent}} + {{/each}} +{{else}} + {{#if_is_struct type}} + {{! Iterate over the actual types in the struct, so we pick up the right + type/optionality/nullability information for them for our recursive + call. }} + {{#zcl_struct_items_by_struct_name type}} + {{#if (hasProperty ../expected label)}} + {{>check_test_value actual=(concat "((CHIP" (asUpperCamelCase ../cluster) "Cluster" (asUpperCamelCase ../type) " *)" ../actual ")." (asStructPropertyName label)) expected=(lookup ../expected label) cluster=../cluster}} + {{/if}} + {{/zcl_struct_items_by_struct_name}} + {{! Maybe we should add a check for properties in the expected object (other + than "global") that are not present in the struct ? }} + {{else}} + VerifyOrReturn(CheckValue{{#if (isString type)}}AsString{{/if}}("{{label}}", {{actual}}, + {{~#if (chip_tests_variables_has expected)}}{{expected}} + {{~else if (isOctetString type)}}[[NSData alloc] initWithBytes:"{{octetStringEscapedForCLiteral expected}}" length:{{expected.length}}] + {{~else if (isCharString type)}}@"{{expected}}" + {{~else}}{{asTypedLiteral expected type}} + {{~/if}})); + {{/if_is_struct}} +{{/if}} diff --git a/src/darwin/Framework/CHIP/templates/partials/decode_value.zapt b/examples/chip-tool-darwin/templates/partials/decode_value.zapt similarity index 100% rename from src/darwin/Framework/CHIP/templates/partials/decode_value.zapt rename to examples/chip-tool-darwin/templates/partials/decode_value.zapt diff --git a/src/darwin/Framework/CHIP/templates/partials/encode_value.zapt b/examples/chip-tool-darwin/templates/partials/encode_value.zapt similarity index 100% rename from src/darwin/Framework/CHIP/templates/partials/encode_value.zapt rename to examples/chip-tool-darwin/templates/partials/encode_value.zapt diff --git a/examples/chip-tool-darwin/templates/partials/test_cluster.zapt b/examples/chip-tool-darwin/templates/partials/test_cluster.zapt new file mode 100644 index 00000000000000..1d67bd737dd540 --- /dev/null +++ b/examples/chip-tool-darwin/templates/partials/test_cluster.zapt @@ -0,0 +1,268 @@ +{{#chip_tests tests}} +class {{filename}}: public TestCommandBridge +{ + public: + {{#if ../credsIssuerConfigArg}} + {{filename}}(CredentialIssuerCommands * credsIssuerConfig): TestCommand("{{filename}}", credsIssuerConfig), mTestIndex(0) + {{else}} + {{filename}}(): TestCommandBridge("{{filename}}"), mTestIndex(0) + {{/if}} + { + {{#chip_tests_config}} + {{#if (isString type)}} + AddArgument("{{name}}", &m{{asUpperCamelCase name}}); + {{else}} + AddArgument("{{name}}", {{asTypeMinValue type}}, {{asTypeMaxValue type}}, &m{{asUpperCamelCase name}}); + {{/if}} + {{/chip_tests_config}} + } + + ~{{filename}}() + { + } + + /////////// TestCommand Interface ///////// + void NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + if (0 == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Start: {{filename}}\n"); + } + + if (mTestCount == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Complete: {{filename}}\n"); + SetCommandExitStatus(CHIP_NO_ERROR); + return; + } + + Wait(); + + // Ensure we increment mTestIndex before we start running the relevant + // command. That way if we lose the timeslice after we send the message + // but before our function call returns, we won't end up with an + // incorrect mTestIndex value observed when we get the response. + switch (mTestIndex++) + { + {{#chip_tests_items}} + case {{index}}: + ChipLogProgress(chipTool, " ***** Test Step {{index}} : {{label}}\n"); + {{#if PICS}} + if (ShouldSkip("{{PICS}}")) + { + NextTest(); + return; + } + {{/if}} + err = Test{{asUpperCamelCase label}}_{{index}}(); + break; + {{/chip_tests_items}} + } + + if (CHIP_NO_ERROR != err) + { + ChipLogError(chipTool, " ***** Test Failure: %s\n", chip::ErrorStr(err)); + SetCommandExitStatus(err); + } + } + + {{#if timeout}} + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(mTimeout.HasValue() ? mTimeout.Value() : {{timeout}}); } + {{/if}} + + private: + std::atomic_uint16_t mTestIndex; + const uint16_t mTestCount = {{totalTests}}; + + {{#chip_tests_config}} + chip::Optional<{{chipType}}> m{{asUpperCamelCase name}}; + {{/chip_tests_config}} + + {{#chip_tests_items}} + {{#if async}} + bool testSendCluster{{parent.filename}}_{{asTestIndex index}}_{{asUpperCamelCase command}}_Fulfilled = false; + {{/if}} + {{#chip_tests_item_response_parameters}} + {{#if saveAs}} + {{asObjectiveCType type ../cluster}} {{saveAs}}; + {{/if}} + {{/chip_tests_item_response_parameters}} + + {{~#*inline "subscribeDataCallback"}} + test_{{parent.filename}}_{{attribute}}_Reported + {{/inline}} + {{#if allocateSubscribeDataCallback}} + ResponseHandler _Nullable {{> subscribeDataCallback}} = nil; + {{/if~}} + + {{#*inline "testCommand"}}Test{{asUpperCamelCase label}}_{{index}}{{/inline}} + CHIP_ERROR {{>testCommand}}() + { + {{#if (isTestOnlyCluster cluster)}} + {{command}}( + {{#chip_tests_item_parameters}} + {{#*inline "defaultValue"}}{{asTypedLiteral (chip_tests_config_get_default_value definedValue) (chip_tests_config_get_type definedValue)}}{{/inline}} + {{~#if (chip_tests_config_has definedValue)~}} + m{{asUpperCamelCase definedValue}}.HasValue() ? m{{asUpperCamelCase definedValue}}.Value() : {{>defaultValue}} + {{else}} + {{#if (isString type)}}@"{{/if}}{{definedValue}}{{#if (isString type)}}"{{/if}} + {{~/if~}} + {{/chip_tests_item_parameters}}); + {{else}} + CHIPDevice * device = GetConnectedDevice(); + CHIPTest{{asUpperCamelCase cluster}} * cluster = [[CHIPTest{{asUpperCamelCase cluster}} alloc] initWithDevice:device endpoint:{{endpoint}} queue:mCallbackQueue]; + VerifyOrReturnError(cluster != nil, CHIP_ERROR_INCORRECT_STATE); + + {{#if isCommand}} + {{#if commandObject.arguments.length}} + __auto_type * params = [[CHIP{{asUpperCamelCase cluster}}Cluster{{asUpperCamelCase command}}Params alloc] init]; + {{/if}} + {{#chip_tests_item_parameters}} + {{>test_value target=(concat "params." (asStructPropertyName label)) definedValue=definedValue cluster=parent.cluster depth=0}} + {{/chip_tests_item_parameters}} + [cluster {{asLowerCamelCase command}}With{{#if commandObject.arguments.length}}Params:params completionHandler{{else}}CompletionHandler{{/if}}: + {{#if commandObject.hasSpecificResponse}} + ^(CHIP{{asUpperCamelCase cluster}}Cluster{{asUpperCamelCase commandObject.responseName}}Params * _Nullable values, NSError * _Nullable err) { + {{else}} + ^(NSError * _Nullable err) { + {{/if}} + {{else if isSubscribeAttribute}} + {{#chip_tests_item_parameters}} + {{asObjectiveCBasicType type}} {{asLowerCamelCase name}}Argument = {{asTypedLiteral definedValue type}}; + {{/chip_tests_item_parameters}} + CHIPSubscribeParams * params = [[CHIPSubscribeParams alloc] init]; + [cluster subscribeAttribute{{asUpperCamelCase attribute}}WithMinInterval:[NSNumber numberWithUnsignedInt:minIntervalArgument] + maxInterval:[NSNumber numberWithUnsignedInt:maxIntervalArgument] + params:params + subscriptionEstablished:^{ + VerifyOrReturn(testSendCluster{{parent.filename}}_{{asTestIndex waitForReport.index}}_{{asUpperCamelCase waitForReport.command}}_Fulfilled, SetCommandExitStatus(CHIP_ERROR_INCORRECT_STATE)); + NextTest(); + } + reportHandler:^({{asObjectiveCClass attributeObject.type cluster forceList=attributeObject.isArray}} * _Nullable value, NSError * _Nullable err) { + {{else if isWaitForReport}} + {{> subscribeDataCallback }} = ^({{asObjectiveCClass attributeObject.type cluster forceList=attributeObject.isArray}} * _Nullable value, NSError * _Nullable err) { + {{else if isReadAttribute}} + {{#if_is_fabric_scoped_struct attributeObject.type}} + CHIPReadParams * params = [[CHIPReadParams alloc] init]; + params.fabricFiltered = [NSNumber numberWithBool:true]; + {{/if_is_fabric_scoped_struct}} + [cluster readAttribute{{asUpperCamelCase attribute}}With + {{~#if_is_fabric_scoped_struct attributeObject.type~}} + Params:params completionHandler: + {{~else~}} + CompletionHandler: + {{~/if_is_fabric_scoped_struct~}} + ^({{asObjectiveCClass attributeObject.type cluster forceList=attributeObject.isArray}} * _Nullable value, NSError * _Nullable err) { + {{else if isWriteAttribute}} + {{#chip_tests_item_parameters}} + id {{asLowerCamelCase name}}Argument; + {{>test_value target=(concat (asLowerCamelCase name) "Argument") definedValue=definedValue cluster=parent.cluster depth=0}} + {{/chip_tests_item_parameters}} + [cluster writeAttribute{{asUpperCamelCase attribute}}WithValue:{{#chip_tests_item_parameters}}{{asLowerCamelCase name}}Argument{{/chip_tests_item_parameters}} completionHandler:^(NSError * _Nullable err) { + {{/if}} + NSLog(@"{{label}} Error: %@", err); + + {{#if optional}} + if (err.code == MatterInteractionErrorCodeUnsupportedAttribute) { + NextTest(); + return; + } + {{/if}} + + {{#if response.error}} + VerifyOrReturn(CheckValue("status", err, {{response.error}})); + NextTest(); + {{else if response.errorWrongValue}} + VerifyOrReturn(CheckConstraintNotValue("status", err, 0)); + NextTest(); + {{else}} + VerifyOrReturn(CheckValue("status", err, 0)); + {{#unless isSubscribeAttribute}} + + {{#chip_tests_item_response_parameters}} + {{#*inline "actualValue"}}value{{#unless parent.isAttribute}}s.{{asStructPropertyName name}}{{/unless}}{{/inline}} + {{#if hasExpectedValue}} + { + id actualValue = {{> actualValue}}; + {{>check_test_value actual="actualValue" expected=expectedValue cluster=../cluster}} + } + {{/if}} + {{#if hasExpectedConstraints}} + {{~#*inline "item"}}{{asLowerCamelCase name}}{{/inline}} + {{#if (hasProperty expectedConstraints "type")}}VerifyOrReturn(CheckConstraintType("{{>item}}", "", "{{expectedConstraints.type}}"));{{/if}} + {{~#if (hasProperty expectedConstraints "format")}}VerifyOrReturn(CheckConstraintFormat("{{>item}}", "", "{{expectedConstraints.format}}"));{{/if}} + {{~#if (hasProperty expectedConstraints "startsWith")}}VerifyOrReturn(CheckConstraintStartsWith("{{>item}}", {{>actualValue}}, "{{expectedConstraints.startsWith}}"));{{/if}} + {{~#if (hasProperty expectedConstraints "endsWith")}}VerifyOrReturn(CheckConstraintEndsWith("{{>item}}", {{>actualValue}}, "{{expectedConstraints.endsWith}}"));{{/if}} + {{~#if (hasProperty expectedConstraints "isUpperCase")}}VerifyOrReturn(CheckConstraintIsUpperCase("{{>item}}", {{>actualValue}}, {{expectedConstraints.isUpperCase}}));{{/if}} + {{~#if (hasProperty expectedConstraints "isLowerCase")}}VerifyOrReturn(CheckConstraintIsLowerCase("{{>item}}", {{>actualValue}}, {{expectedConstraints.isLowerCase}}));{{/if}} + {{~#if (hasProperty expectedConstraints "isHexString")}}VerifyOrReturn(CheckConstraintIsHexString("{{>item}}", {{>actualValue}}, {{expectedConstraints.isHexString}}));{{/if}} + {{~#if (hasProperty expectedConstraints "minLength")}}VerifyOrReturn(CheckConstraintMinLength("{{>item}}", [{{>actualValue}} length], {{expectedConstraints.minLength}}));{{/if}} + {{~#if (hasProperty expectedConstraints "maxLength")}}VerifyOrReturn(CheckConstraintMaxLength("{{>item}}", [{{>actualValue}} length], {{expectedConstraints.maxLength}}));{{/if}} + {{~#if (hasProperty expectedConstraints "minValue")}} + if ({{>actualValue}} != nil) { + VerifyOrReturn(CheckConstraintMinValue<{{chipType}}>("{{>item}}", [{{>actualValue}} {{asObjectiveCNumberType "" type true}}Value], {{asTypedLiteral expectedConstraints.minValue type}})); + } + {{/if}} + {{~#if (hasProperty expectedConstraints "maxValue")}} + if ({{>actualValue}} != nil) { + VerifyOrReturn(CheckConstraintMaxValue<{{chipType}}>("{{>item}}", [{{>actualValue}} {{asObjectiveCNumberType "" type true}}Value], {{asTypedLiteral expectedConstraints.maxValue type}})); + } + {{/if}} + {{~#if (hasProperty expectedConstraints "notValue")}} + {{#if (isLiteralNull expectedConstraints.notValue)}} + VerifyOrReturn(CheckValueNonNull("{{>item}}", {{>actualValue}})); + {{else}} + if ({{>actualValue}} != nil) { + {{#if (isString type)}} + VerifyOrReturn(CheckConstraintNotValue("{{>item}}", {{>actualValue}}, {{asTypedLiteral expectedConstraints.notValue type}})); + {{else}} + VerifyOrReturn(CheckConstraintNotValue("{{>item}}", {{>actualValue}}, {{asTypedLiteral expectedConstraints.notValue type}})); + {{/if}} + } + {{/if}} + {{/if}} + {{/if}} + {{#if saveAs}} + { + {{saveAs}} = {{>actualValue}}; + } + {{/if}} + {{/chip_tests_item_response_parameters}} + + {{#unless async}} + NextTest(); + {{else}} + testSendCluster{{parent.filename}}_{{asTestIndex index}}_{{asUpperCamelCase command}}_Fulfilled = true; + {{/unless}} + {{else}} + {{! We're a subscription }} + if ({{> subscribeDataCallback}} != nil) { + ResponseHandler callback = {{> subscribeDataCallback}}; + {{> subscribeDataCallback}} = nil; + callback(value, err); + } + {{/unless}} + {{/if}} + }{{#unless isWaitForReport}}]{{/unless}}; + + {{/if}} + {{#if async}} + NextTest(); + {{else}} + {{#*inline "minCommandTimeout"~}} + {{#if (isTestOnlyCluster cluster)~}} + {{#if (isStrEqual command "WaitForMs")~}} + {{#chip_tests_item_parameters}}{{#if (isStrEqual name "ms")}}({{definedValue}} / 1000) + {{/if}}{{/chip_tests_item_parameters}} + {{~/if}} + {{~/if}} + {{~/inline}} + {{/if}} + return CHIP_NO_ERROR; + } +{{/chip_tests_items}} + +}; + +{{/chip_tests}} diff --git a/examples/chip-tool-darwin/templates/partials/test_value.zapt b/examples/chip-tool-darwin/templates/partials/test_value.zapt new file mode 100644 index 00000000000000..77438fd32fb7f3 --- /dev/null +++ b/examples/chip-tool-darwin/templates/partials/test_value.zapt @@ -0,0 +1,43 @@ +{{#if (chip_tests_config_has definedValue)}} + {{! Just replace the value's name with the actual value in the rest of the processing~}} + {{>test_value target=target definedValue=(chip_tests_config_get_default_value definedValue) cluster=cluster depth=depth}} +{{else if isOptional}} + {{! Just go ahead and assign to the value, stripping the optionality bit off. }} + {{>test_value target=target definedValue=definedValue cluster=cluster isOptional=false depth=(incrementDepth depth)}} +{{else if isNullable}} + {{#if (isLiteralNull definedValue)}} + {{target}} = nil; + {{else}} + {{>test_value target=target definedValue=definedValue cluster=cluster isNullable=false depth=(incrementDepth depth)}} + {{/if}} +{{else if isArray}} + {{ensureIsArray definedValue~}} + { + NSMutableArray * temp_{{depth}} = [[NSMutableArray alloc] init]; + {{#each definedValue}} + {{>test_value target=(concat "temp_" ../depth "[" @index "]") definedValue=this cluster=../cluster depth=(incrementDepth ../depth) type=../type isArray=false parent=../parent}} + {{/each}} + {{target}} = temp_{{depth}}; + } +{{else}} + {{#if_is_struct type}} + {{target}} = [[CHIP{{asUpperCamelCase cluster}}Cluster{{asUpperCamelCase type}} alloc] init]; + {{#zcl_struct_items_by_struct_name type}} + {{#if_include_struct_item_value ../definedValue name}} + {{! target may be some place where we lost type information (e.g. an + id), so add explicit cast when trying to assign to our + properties. }} + {{>test_value target=(concat "((CHIP" (asUpperCamelCase ../cluster) "Cluster" (asUpperCamelCase ../type) " *)" ../target ")." (asStructPropertyName label)) definedValue=(lookup ../definedValue name) cluster=../cluster depth=(incrementDepth ../depth)}} + {{/if_include_struct_item_value}} + {{/zcl_struct_items_by_struct_name}} + + {{else if (chip_tests_variables_has definedValue)}} + {{target}} = [{{definedValue}} copy]; + {{else if (isCharString type)}} + {{target}} = @"{{definedValue}}"; + {{else if (isOctetString type)}} + {{target}} = [[NSData alloc] initWithBytes:"{{octetStringEscapedForCLiteral definedValue}}" length:{{definedValue.length}}]; + {{else}} + {{target}} = [NSNumber numberWith{{asObjectiveCNumberType definedValue type false}}:{{asTypedLiteral definedValue type}}]; + {{/if_is_struct}} +{{/if}} diff --git a/examples/chip-tool-darwin/templates/templates.json b/examples/chip-tool-darwin/templates/templates.json index 1e7c98e8cb1c28..cefb80332a2429 100644 --- a/examples/chip-tool-darwin/templates/templates.json +++ b/examples/chip-tool-darwin/templates/templates.json @@ -7,7 +7,10 @@ "../../../src/app/zap-templates/partials/helper.js", "../../../src/app/zap-templates/templates/app/helper.js", "../../../src/app/zap-templates/templates/chip/helper.js", - "../../../src/darwin/Framework/CHIP/templates/helper.js" + "../../../src/darwin/Framework/CHIP/templates/helper.js", + "../../../src/darwin/Framework/CHIP/templates/tests/helper.js", + "../../../src/app/zap-templates/common/ClusterTestGeneration.js", + "tests.js" ], "override": "../../../src/app/zap-templates/common/override.js", "partials": [ @@ -22,6 +25,26 @@ { "name": "cluster_header", "path": "../../../src/app/zap-templates/partials/cluster_header.zapt" + }, + { + "name": "test_cluster", + "path": "partials/test_cluster.zapt" + }, + { + "name": "encode_value", + "path": "partials/encode_value.zapt" + }, + { + "name": "decode_value", + "path": "partials/decode_value.zapt" + }, + { + "name": "test_value", + "path": "partials/test_value.zapt" + }, + { + "name": "check_test_value", + "path": "partials/check_test_value.zapt" } ], "templates": [ @@ -29,6 +52,21 @@ "path": "commands.zapt", "name": "Cluster Commands header", "output": "cluster/Commands.h" + }, + { + "path": "tests-commands.zapt", + "name": "Tests Commands header", + "output": "test/Commands.h" + }, + { + "path": "CHIPTestClustersObjc.zapt", + "name": "Test Objc API Header", + "output": "cluster/CHIPTestClustersObjc.h" + }, + { + "path": "CHIPTestClustersObjc-src.zapt", + "name": "Test Objc API", + "output": "cluster/CHIPTestClustersObjc.mm" } ] } diff --git a/examples/chip-tool-darwin/templates/tests-commands.zapt b/examples/chip-tool-darwin/templates/tests-commands.zapt new file mode 100644 index 00000000000000..318c3bbb3c7e4a --- /dev/null +++ b/examples/chip-tool-darwin/templates/tests-commands.zapt @@ -0,0 +1,55 @@ +{{> header}} + +#pragma once + +#include +#include +#include +#include + +#include // For INFINITY + +class TestList : public Command +{ +public: + TestList() : Command("list") {}; + CHIP_ERROR Run() override + { + {{#chip_tests (getTests)}}printf("{{filename}}\n");{{/chip_tests}} + + return CHIP_NO_ERROR; + } +}; + +class ManualTestList : public Command +{ +public: + ManualTestList() : Command("list-manual") {}; + CHIP_ERROR Run() override + { + {{#chip_tests (getManualTests)}}printf("{{filename}}\n");{{/chip_tests}} + + return CHIP_NO_ERROR; + } +}; + +{{>test_cluster tests=(getTests) credsIssuerConfigArg=false}} +{{>test_cluster tests=(getManualTests) credsIssuerConfigArg=false}} + +void registerCommandsTests(Commands & commands) +{ + const char * clusterName = "Tests"; + + commands_list clusterCommands = { + make_unique(), + make_unique(), + {{#chip_tests (getTests)}} + make_unique<{{filename}}>(), + {{/chip_tests}} + {{#chip_tests (getManualTests)}} + make_unique<{{filename}}>(), + {{/chip_tests}} + }; + + commands.Register(clusterName, clusterCommands); +} diff --git a/examples/chip-tool-darwin/templates/tests.js b/examples/chip-tool-darwin/templates/tests.js new file mode 100644 index 00000000000000..7cd1779c34e63a --- /dev/null +++ b/examples/chip-tool-darwin/templates/tests.js @@ -0,0 +1,289 @@ +/* + * + * 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. + */ + +function getManualTests() +{ + return []; +} + +function getTests() +{ + const AccessControl = [ + 'TestAccessControlCluster', + ]; + + const BinaryInput = [ + 'Test_TC_BI_1_1', + 'Test_TC_BI_2_1', + 'Test_TC_BI_2_2', + ]; + + const BooleanState = [ + 'Test_TC_BOOL_1_1', + 'Test_TC_BOOL_2_1', + ]; + + const BridgedActions = [ + 'Test_TC_BRAC_1_1', + ]; + + const ColorControl = [ + 'Test_TC_CC_1_1', + 'Test_TC_CC_2_1', + 'Test_TC_CC_3_1', + 'Test_TC_CC_3_2', + 'Test_TC_CC_3_3', + 'Test_TC_CC_4_1', + 'Test_TC_CC_4_2', + 'Test_TC_CC_4_3', + 'Test_TC_CC_4_4', + 'Test_TC_CC_5_1', + 'Test_TC_CC_5_2', + 'Test_TC_CC_5_3', + 'Test_TC_CC_6_1', + 'Test_TC_CC_6_2', + 'Test_TC_CC_6_3', + 'Test_TC_CC_7_1', + 'Test_TC_CC_7_2', + 'Test_TC_CC_7_3', + 'Test_TC_CC_7_4', + 'Test_TC_CC_8_1', + 'Test_TC_CC_9_1', + 'Test_TC_CC_9_2', + 'Test_TC_CC_9_3', + ]; + + const DeviceDiscovery = [ + 'Test_TC_DD_1_5', + 'Test_TC_DD_1_6', + 'Test_TC_DD_1_7', + 'Test_TC_DD_1_8', + 'Test_TC_DD_1_9', + ]; + + const DeviceManagement = [ + 'Test_TC_DM_1_1', + 'Test_TC_DM_3_1', + ]; + + const ElectricalMeasurement = [ + 'Test_TC_EMR_1_1', + ]; + + const EthernetNetworkDiagnostics = [ + 'Test_TC_ETHDIAG_1_1', + 'Test_TC_ETHDIAG_2_1', + ]; + + const FlowMeasurement = [ + 'Test_TC_FLW_1_1', + 'Test_TC_FLW_2_1', + 'Test_TC_FLW_2_2', + ]; + + const IlluminanceMeasurement = [ + 'Test_TC_ILL_1_1', + ]; + + const OccupancySensing = [ + 'Test_TC_OCC_1_1', + 'Test_TC_OCC_2_1', + 'Test_TC_OCC_2_2', + ]; + + const LevelControl = [ + 'Test_TC_LVL_1_1', + 'Test_TC_LVL_2_1', + 'Test_TC_LVL_2_2', + 'Test_TC_LVL_3_1', + 'Test_TC_LVL_4_1', + 'Test_TC_LVL_5_1', + 'Test_TC_LVL_6_1', + ]; + + const MediaControl = [ + 'Test_TC_MC_1_1', + 'Test_TC_MC_2_1', + 'Test_TC_MC_3_1', + 'Test_TC_MC_3_2', + 'Test_TC_MC_3_3', + 'Test_TC_MC_3_4', + 'Test_TC_MC_3_5', + 'Test_TC_MC_3_6', + 'Test_TC_MC_3_7', + 'Test_TC_MC_3_8', + 'Test_TC_MC_3_9', + 'Test_TC_MC_3_10', + 'Test_TC_MC_3_11', + 'Test_TC_MC_5_1', + 'Test_TC_MC_5_2', + 'Test_TC_MC_5_3', + 'Test_TC_MC_6_1', + 'Test_TC_MC_6_2', + 'Test_TC_MC_6_3', + 'Test_TC_MC_6_4', + 'Test_TC_MC_7_1', + 'Test_TC_MC_7_2', + 'Test_TC_MC_8_1', + 'Test_TC_MC_9_1', + ]; + + const OnOff = [ + 'Test_TC_OO_1_1', + 'Test_TC_OO_2_1', + 'Test_TC_OO_2_2', + 'Test_TC_OO_2_3', + ]; + + const PowerSource = [ + 'Test_TC_PS_1_1', + ]; + + const PressureMeasurement = [ + 'Test_TC_PRS_1_1', + 'Test_TC_PRS_2_1', + ]; + + const PumpConfigurationControl = [ + 'Test_TC_PCC_1_1', + 'Test_TC_PCC_2_1', + 'Test_TC_PCC_2_2', + 'Test_TC_PCC_2_3', + ]; + + const RelativeHumidityMeasurement = [ + 'Test_TC_RH_1_1', + 'Test_TC_RH_2_1', + 'Test_TC_RH_2_2', + ]; + + const Switch = [ + 'Test_TC_SWTCH_2_1', + 'Test_TC_SWTCH_2_2', + ]; + + const TemperatureMeasurement = [ + 'Test_TC_TM_1_1', + 'Test_TC_TM_2_1', + 'Test_TC_TM_2_2', + ]; + + const Thermostat = [ + 'Test_TC_TSTAT_1_1', + 'Test_TC_TSTAT_2_1', + 'Test_TC_TSTAT_2_2', + ]; + + const ThermostatUserConfiguration = [ + 'Test_TC_TSUIC_1_1', + 'Test_TC_TSUIC_2_1', + 'Test_TC_TSUIC_2_2', + ]; + + const ThreadNetworkDiagnostics = [ + 'Test_TC_DIAG_TH_NW_1_1', + ]; + + const WiFiNetworkDiagnostics = [ + 'Test_TC_WIFIDIAG_1_1', + 'Test_TC_WIFIDIAG_3_1', + ]; + + const WindowCovering = [ + // WindowCovering is make uses of multiples "subscribeAttribute", but it triggers + // some failures on darwin supposely because the ReadClient stays open for the + // whole duration of the tests and that goes past some internal limits. + // Because of this, some of the tests are disabled on darwin. + 'Test_TC_WNCV_1_1', + 'Test_TC_WNCV_2_1', + 'Test_TC_WNCV_2_2', + 'Test_TC_WNCV_2_4', + 'Test_TC_WNCV_2_5', + //'Test_TC_WNCV_3_1', + //'Test_TC_WNCV_3_2', + //'Test_TC_WNCV_3_3', + 'Test_TC_WNCV_3_4', + 'Test_TC_WNCV_3_5', + 'Test_TC_WNCV_4_3', + 'Test_TC_WNCV_4_4', + ]; + + const Others = [ + 'TestCluster', + 'TestSaveAs', + 'TestConstraints', + 'TestDelayCommands', + 'TestDescriptorCluster', + 'TestBasicInformation', + 'TestGroupsCluster', + 'TestGroupKeyManagementCluster', + 'TestIdentifyCluster', + 'TestLogCommands', + 'TestOperationalCredentialsCluster', + 'TestModeSelectCluster', + 'TestBinding', + ]; + + const SoftwareDiagnostics = [ + 'Test_TC_SWDIAG_1_1', + 'Test_TC_SWDIAG_2_1', + 'Test_TC_SWDIAG_3_1', + ]; + + const Subscriptions = [ + 'TestSubscribe_OnOff', + ]; + + const tests = [ + AccessControl, // + BinaryInput, // + BooleanState, // + BridgedActions, // + ColorControl, // + DeviceDiscovery, // + DeviceManagement, // + ElectricalMeasurement, // + EthernetNetworkDiagnostics, // + FlowMeasurement, // + IlluminanceMeasurement, // + LevelControl, // + MediaControl, // + OccupancySensing, // + OnOff, // + PowerSource, // + PressureMeasurement, // + PumpConfigurationControl, // + RelativeHumidityMeasurement, // + Switch, // + TemperatureMeasurement, // + Thermostat, // + ThermostatUserConfiguration, // + ThreadNetworkDiagnostics, // + WiFiNetworkDiagnostics, // + WindowCovering, // + Others, // + SoftwareDiagnostics, // + Subscriptions, // + ]; + return tests.flat(1); +} + +// +// Module exports +// +exports.getTests = getTests; +exports.getManualTests = getManualTests; diff --git a/src/darwin/Framework/CHIP/templates/templates.json b/src/darwin/Framework/CHIP/templates/templates.json index f90bc62a5e1cd3..3d32aa2cedca30 100644 --- a/src/darwin/Framework/CHIP/templates/templates.json +++ b/src/darwin/Framework/CHIP/templates/templates.json @@ -22,11 +22,11 @@ }, { "name": "encode_value", - "path": "partials/encode_value.zapt" + "path": "../../../../../examples/chip-tool-darwin/templates/partials/encode_value.zapt" }, { "name": "decode_value", - "path": "partials/decode_value.zapt" + "path": "../../../../../examples/chip-tool-darwin/templates/partials/decode_value.zapt" }, { "name": "init_struct_member", @@ -87,16 +87,6 @@ "name": "Objc reflections of CHIP command payloads header", "output": "src/darwin/Framework/CHIP/zap-generated/CHIPCommandPayloadsObjc.mm" }, - { - "path": "CHIPTestClustersObjc.zapt", - "name": "Test Objc API Header", - "output": "src/darwin/Framework/CHIP/zap-generated/CHIPTestClustersObjc.h" - }, - { - "path": "CHIPTestClustersObjc-src.zapt", - "name": "Test Objc API", - "output": "src/darwin/Framework/CHIP/zap-generated/CHIPTestClustersObjc.mm" - }, { "path": "CHIPAttributeTLVValueDecoder-src.zapt", "name": "Decode TLV attribute values into Objc objects", diff --git a/src/darwin/Framework/CHIP/templates/tests/tests.js b/src/darwin/Framework/CHIP/templates/tests/tests.js index 712fbd36e51b28..ef894546f2615a 100644 --- a/src/darwin/Framework/CHIP/templates/tests/tests.js +++ b/src/darwin/Framework/CHIP/templates/tests/tests.js @@ -15,6 +15,11 @@ * limitations under the License. */ +function getManualTests() +{ + return []; +} + function getTests() { const AccessControl = [ @@ -302,4 +307,5 @@ function getTests() // // Module exports // -exports.getTests = getTests; +exports.getTests = getTests; +exports.getManualTests = getManualTests;