diff --git a/examples/chip-tool/templates/partials/test_cluster.zapt b/examples/chip-tool/templates/partials/test_cluster.zapt index 360fa4656998a8..f666c463e2ee4f 100644 --- a/examples/chip-tool/templates/partials/test_cluster.zapt +++ b/examples/chip-tool/templates/partials/test_cluster.zapt @@ -145,7 +145,7 @@ class {{filename}}: public TestCommand (static_cast<{{filename}} *>(context))->{{>successResponse}}({{#chip_tests_item_response_parameters}}{{#not_first}}, {{/not_first}}{{asLowerCamelCase name}}{{/chip_tests_item_response_parameters}}); } - {{#if isSubscribeAttribute}} + {{#if isSubscribe}} static void {{> staticSubscriptionEstablished}}({{> staticSubscriptionEstablishedArguments}}) { (static_cast<{{filename}} *>(context))->{{> subscriptionEstablished}}(); @@ -258,6 +258,10 @@ class {{filename}}: public TestCommand {{~#if isGroupCommand}}, {{>staticDoneResponse}}{{/if~}} )); {{> maybeWait }} + {{else if isReadEvent}} + ReturnErrorOnFailure(cluster.ReadEvent<{{zapTypeToDecodableClusterObjectType event ns=cluster isArgument=true}}>(this, {{>staticSuccessResponse}}, {{>staticFailureResponse}})); + {{else if isSubscribeEvent}} + ReturnErrorOnFailure(cluster.SubscribeEvent<{{zapTypeToDecodableClusterObjectType event ns=cluster isArgument=true}}>(this, {{>staticSuccessResponse}}, {{>staticFailureResponse}}, minIntervalArgument, maxIntervalArgument, {{>staticSubscriptionEstablished}})); {{else if isReadAttribute}} ReturnErrorOnFailure(cluster.ReadAttribute({{#chip_tests_item_parameters}}{{asLowerCamelCase name}}Argument, {{/chip_tests_item_parameters}}this, {{>staticSuccessResponse}}, {{>staticFailureResponse}})); {{else if isSubscribeAttribute}} @@ -288,7 +292,7 @@ class {{filename}}: public TestCommand {{/if}} } - {{#if isSubscribeAttribute}} + {{#if isSubscribe}} void {{>successResponse}}({{> subscribeResponseArguments attr=attributeObject}}) { {{#if response.error}} diff --git a/examples/chip-tool/templates/partials/test_cluster_value_equals.zapt b/examples/chip-tool/templates/partials/test_cluster_value_equals.zapt index 2f2d6b8a80415e..a50b9be89adca2 100644 --- a/examples/chip-tool/templates/partials/test_cluster_value_equals.zapt +++ b/examples/chip-tool/templates/partials/test_cluster_value_equals.zapt @@ -17,6 +17,12 @@ {{/each}} VerifyOrReturn(CheckNoMoreListItems("{{label}}", iter_{{depth}}, {{expected.length}})); } +{{else if isEvent}} + {{#zcl_events_fields_by_event_name type}} + {{#if (hasProperty ../expected label)}} + {{>valueEquals label=(concat ../label "." (asLowerCamelCase label)) actual=(concat ../actual "." (asLowerCamelCase label)) expected=(lookup ../expected label) depth=(incrementDepth depth)}} + {{/if}} + {{/zcl_events_fields_by_event_name}} {{else}} {{#if_is_struct type}} {{! Iterate over the actual types in the struct, so we pick up the right diff --git a/src/app/zap-templates/common/ClusterTestGeneration.js b/src/app/zap-templates/common/ClusterTestGeneration.js index 59b2bc904c6b95..19a669a9e8ae8d 100644 --- a/src/app/zap-templates/common/ClusterTestGeneration.js +++ b/src/app/zap-templates/common/ClusterTestGeneration.js @@ -26,9 +26,10 @@ const path = require('path'); // Import helpers from zap core const templateUtil = require(zapPath + 'dist/src-electron/generator/template-util.js') -const { getClusters, getCommands, getAttributes, isTestOnlyCluster } = require('./simulated-clusters/SimulatedClusters.js'); -const { asBlocks, ensureClusters } = require('./ClustersHelper.js'); -const { Variables } = require('./variables/Variables.js'); +const { getClusters, getCommands, getAttributes, getEvents, isTestOnlyCluster } += require('./simulated-clusters/SimulatedClusters.js'); +const { asBlocks, ensureClusters } = require('./ClustersHelper.js'); +const { Variables } = require('./variables/Variables.js'); const kIdentityName = 'identity'; const kClusterName = 'cluster'; @@ -86,6 +87,15 @@ function setDefaultTypeForWaitCommand(test) { const type = test[kWaitCommandName]; switch (type) { + case 'readEvent': + test.isEvent = true; + test.isReadEvent = true; + break; + case 'subscribeEvent': + test.isEvent = true; + test.isSubscribe = true; + test.isSubscribeEvent = true; + break; case 'readAttribute': test.isAttribute = true; test.isReadAttribute = true; @@ -96,6 +106,7 @@ function setDefaultTypeForWaitCommand(test) break; case 'subscribeAttribute': test.isAttribute = true; + test.isSubscribe = true; test.isSubscribeAttribute = true; break; default: @@ -111,6 +122,19 @@ function setDefaultTypeForCommand(test) { const type = test[kCommandName]; switch (type) { + case 'readEvent': + test.commandName = 'Read'; + test.isEvent = true; + test.isReadEvent = true; + break; + + case 'subscribeEvent': + test.commandName = 'Subscribe'; + test.isEvent = true; + test.isSubscribe = true; + test.isSubscribeEvent = true; + break; + case 'readAttribute': test.commandName = 'Read'; test.isAttribute = true; @@ -130,6 +154,7 @@ function setDefaultTypeForCommand(test) case 'subscribeAttribute': test.commandName = 'Subscribe'; test.isAttribute = true; + test.isSubscribe = true; test.isSubscribeAttribute = true; break; @@ -243,13 +268,13 @@ function setDefaultResponse(test) return; } - if (!test.isAttribute) { + if (!test.isAttribute && !test.isEvent) { return; } - if (test.isWriteAttribute || test.isSubscribeAttribute) { + if (test.isWriteAttribute || test.isSubscribe) { if (hasResponseValueOrConstraints) { - const errorStr = 'Attribute test has a "value" or a "constraints" defined.'; + const errorStr = 'Test has a "value" or a "constraints" defined.'; throwError(test, errorStr); } @@ -263,14 +288,14 @@ function setDefaultResponse(test) throwError(test, errorStr); } + const name = test.isAttribute ? test.attribute : test.event; if (hasResponseValue) { - test[kResponseName].values.push( - { name : test.attribute, value : test[kResponseName].value, saveAs : test[kResponseName].saveAs }); + test[kResponseName].values.push({ name : name, value : test[kResponseName].value, saveAs : test[kResponseName].saveAs }); } if (hasResponseConstraints) { test[kResponseName].values.push( - { name : test.attribute, constraints : test[kResponseName].constraints, saveAs : test[kResponseName].saveAs }); + { name : name, constraints : test[kResponseName].constraints, saveAs : test[kResponseName].saveAs }); } delete test[kResponseName].value; @@ -364,7 +389,7 @@ function printErrorAndExit(context, msg) process.exit(1); } -function assertCommandOrAttribute(context) +function assertCommandOrAttributeOrEvent(context) { const clusterName = context.cluster; return getClusters(context).then(clusters => { @@ -382,6 +407,9 @@ function assertCommandOrAttribute(context) } else if (context.isAttribute) { filterName = context.attribute; items = getAttributes(context, clusterName); + } else if (context.isEvent) { + filterName = context.event; + items = getEvents(context, clusterName); } else { printErrorAndExit(context, 'Unsupported command type: ', context); } @@ -389,17 +417,17 @@ function assertCommandOrAttribute(context) return items.then(items => { const filter = item => item.name.toLowerCase() == filterName.toLowerCase(); const item = items.find(filter); - const itemType = (context.isCommand ? 'Command' : 'Attribute'); + const itemType = (context.isCommand ? 'Command' : context.isAttribute ? 'Attribute' : 'Event'); - // If the command or attribute is not found, it could be because of a typo in the test - // description, or an attribute name not matching the spec, or a wrongly configured zap + // If the command/attribute/event is not found, it could be because of a typo in the test + // description, or an attribute/event name not matching the spec, or a wrongly configured zap // file. if (!item) { const names = items.map(item => item.name); printErrorAndExit(context, 'Missing ' + itemType + ' "' + filterName + '" in: \n\t* ' + names.join('\n\t* ')); } - // If the command or attribute has been found but the response can not be found, it could be + // If the command/attribute/event has been found but the response can not be found, it could be // because of a wrongly configured cluster definition. if (!item.response) { printErrorAndExit(context, 'Missing ' + itemType + ' "' + filterName + '" response'); @@ -437,7 +465,7 @@ function chip_tests_pics(options) async function chip_tests(list, options) { - // Set a global on our items so assertCommandOrAttribute can work. + // Set a global on our items so assertCommandOrAttributeOrEvent can work. let global = this.global; const items = Array.isArray(list) ? list : list.split(','); const names = items.map(name => name.trim()); @@ -448,11 +476,14 @@ async function chip_tests(list, options) test.tests = await Promise.all(test.tests.map(async function(item) { item.global = global; if (item.isCommand) { - let command = await assertCommandOrAttribute(item); + let command = await assertCommandOrAttributeOrEvent(item); item.commandObject = command; } else if (item.isAttribute) { - let attr = await assertCommandOrAttribute(item); + let attr = await assertCommandOrAttributeOrEvent(item); item.attributeObject = attr; + } else if (item.isEvent) { + let evt = await assertCommandOrAttributeOrEvent(item); + item.eventObject = evt; } return item; })); @@ -557,9 +588,9 @@ function chip_tests_item_parameters(options) { const commandValues = this.arguments.values; - const promise = assertCommandOrAttribute(this).then(item => { - if (this.isAttribute && !this.isWriteAttribute) { - if (this.isSubscribeAttribute) { + const promise = assertCommandOrAttributeOrEvent(this).then(item => { + if ((this.isAttribute || this.isEvent) && !this.isWriteAttribute) { + if (this.isSubscribe) { const minInterval = { name : 'minInterval', type : 'int16u', chipType : 'uint16_t', definedValue : this.minInterval }; const maxInterval = { name : 'maxInterval', type : 'int16u', chipType : 'uint16_t', definedValue : this.maxInterval }; return [ minInterval, maxInterval ]; @@ -595,7 +626,7 @@ function chip_tests_item_response_parameters(options) { const responseValues = this.response.values.slice(); - const promise = assertCommandOrAttribute(this).then(item => { + const promise = assertCommandOrAttributeOrEvent(this).then(item => { if (this.isWriteAttribute) { return []; } diff --git a/src/app/zap-templates/common/ClustersHelper.js b/src/app/zap-templates/common/ClustersHelper.js index 7d15993497f0ed..66c5a6d3ecdefd 100644 --- a/src/app/zap-templates/common/ClustersHelper.js +++ b/src/app/zap-templates/common/ClustersHelper.js @@ -425,6 +425,23 @@ function enhancedCommands(commands, types) return commands; } +function enhancedEvents(events, types) +{ + events.forEach(event => { + const argument = { + name : event.name, + type : event.name, + isList : false, + isArray : false, + isEvent : true, + isNullable : false, + label : event.name, + }; + event.response = { arguments : [ argument ] }; + }); + return events; +} + function enhancedAttributes(attributes, globalAttributes, types) { attributes.forEach(attribute => { @@ -442,6 +459,7 @@ function enhancedAttributes(attributes, globalAttributes, types) size : attribute.size, isList : attribute.isList, isArray : attribute.isList, + isEvent : false, isNullable : attribute.isNullable, chipType : attribute.chipType, chipCallback : attribute.chipCallback, @@ -491,7 +509,7 @@ Clusters.init = async function(context) { this._clusters = clusters; this._commands = enhancedCommands(commands, types); this._attributes = enhancedAttributes(attributes, globalAttributes, types); - this._events = events; + this._events = enhancedEvents(events, types); return this.ready.resolve(); }, err => this.ready.reject(err)); diff --git a/src/app/zap-templates/common/simulated-clusters/SimulatedClusters.js b/src/app/zap-templates/common/simulated-clusters/SimulatedClusters.js index 19ace7b76d17f4..edc1d0b8e5c841 100644 --- a/src/app/zap-templates/common/simulated-clusters/SimulatedClusters.js +++ b/src/app/zap-templates/common/simulated-clusters/SimulatedClusters.js @@ -50,6 +50,12 @@ function getAttributes(context, clusterName) return cluster ? Promise.resolve(cluster.attributes) : ensureClusters(context).getServerAttributes(clusterName); } +function getEvents(context, clusterName) +{ + const cluster = getSimulatedCluster(clusterName); + return cluster ? Promise.resolve(cluster.events) : ensureClusters(context).getServerEvents(clusterName); +} + function isTestOnlyCluster(clusterName) { return !!getSimulatedCluster(clusterName); @@ -61,4 +67,5 @@ function isTestOnlyCluster(clusterName) exports.getClusters = getClusters; exports.getCommands = getCommands; exports.getAttributes = getAttributes; +exports.getEvents = getEvents; exports.isTestOnlyCluster = isTestOnlyCluster; diff --git a/src/app/zap-templates/templates/app/helper.js b/src/app/zap-templates/templates/app/helper.js index 0af4118c78b719..efff1cf2b38aa3 100644 --- a/src/app/zap-templates/templates/app/helper.js +++ b/src/app/zap-templates/templates/app/helper.js @@ -21,12 +21,21 @@ const templateUtil = require(zapPath + 'generator/template-util.js') const zclHelper = require(zapPath + 'generator/helper-zcl.js') const queryCommand = require(zapPath + 'db/query-command.js') const zclQuery = require(zapPath + 'db/query-zcl.js') +const queryEvents = require(zapPath + 'db/query-event.js') const cHelper = require(zapPath + 'generator/helper-c.js') const string = require(zapPath + 'util/string.js') +const dbEnum = require(zapPath + '../src-shared/db-enum.js') const StringHelper = require('../../common/StringHelper.js'); const ChipTypesHelper = require('../../common/ChipTypesHelper.js'); +zclHelper['isEvent'] = function(db, event_name, packageId) { + return queryEvents + .selectAllEvents(db, packageId) + .then(events => events.find(event => event.name == event_name)) + .then(events => events ? 'event' : dbEnum.zclType.unknown); +} + // This list of attributes is taken from section '11.2. Global Attributes' of the // Data Model specification. const kGlobalAttributes = [ @@ -398,6 +407,10 @@ async function zapTypeToClusterObjectType(type, isDecodable, options) return ns + 'Structs::' + type + '::' + (isDecodable ? 'DecodableType' : 'Type'); } + if (await typeChecker('isEvent')) { + return ns + 'Events::' + type + '::' + (isDecodable ? 'DecodableType' : 'Type'); + } + return zclHelper.asUnderlyingZclType.call({ global : this.global }, type, options); } @@ -690,6 +703,22 @@ function hasProperty(obj, prop) return prop in obj; } +async function zcl_events_fields_by_event_name(name, options) +{ + const { db, sessionId } = this.global; + const packageId = await templateUtil.ensureZclPackageId(this) + + const promise = queryEvents.selectAllEvents(db, packageId) + .then(events => events.find(event => event.name == name)) + .then(evt => queryEvents.selectEventFieldsByEventId(db, evt.id)) + .then(fields => fields.map(field => { + field.label = field.name; + return field; + })) + .then(fields => templateUtil.collectBlocks(fields, options, this)) + return templateUtil.templatePromise(this.global, promise) +} + // // Module exports // @@ -710,3 +739,4 @@ exports.getResponseCommandName = getResponseCommandName; exports.isWeaklyTypedEnum = isWeaklyTypedEnum; exports.getPythonFieldDefault = getPythonFieldDefault; exports.incrementDepth = incrementDepth; +exports.zcl_events_fields_by_event_name = zcl_events_fields_by_event_name;