Skip to content

Commit

Permalink
[YAML] Add events support (#13542)
Browse files Browse the repository at this point in the history
  • Loading branch information
vivien-apple authored Jan 13, 2022
1 parent 3c5231c commit 1a6cb95
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 24 deletions.
8 changes: 6 additions & 2 deletions examples/chip-tool/templates/partials/test_cluster.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -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}}();
Expand Down Expand Up @@ -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::app::Clusters::{{asUpperCamelCase cluster}}::Attributes::{{asUpperCamelCase attribute}}::TypeInfo>({{#chip_tests_item_parameters}}{{asLowerCamelCase name}}Argument, {{/chip_tests_item_parameters}}this, {{>staticSuccessResponse}}, {{>staticFailureResponse}}));
{{else if isSubscribeAttribute}}
Expand Down Expand Up @@ -288,7 +292,7 @@ class {{filename}}: public TestCommand
{{/if}}
}

{{#if isSubscribeAttribute}}
{{#if isSubscribe}}
void {{>successResponse}}({{> subscribeResponseArguments attr=attributeObject}})
{
{{#if response.error}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
{{/each}}
VerifyOrReturn(CheckNoMoreListItems<decltype({{actual}})>("{{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
Expand Down
73 changes: 52 additions & 21 deletions src/app/zap-templates/common/ClusterTestGeneration.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand All @@ -96,6 +106,7 @@ function setDefaultTypeForWaitCommand(test)
break;
case 'subscribeAttribute':
test.isAttribute = true;
test.isSubscribe = true;
test.isSubscribeAttribute = true;
break;
default:
Expand All @@ -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;
Expand All @@ -130,6 +154,7 @@ function setDefaultTypeForCommand(test)
case 'subscribeAttribute':
test.commandName = 'Subscribe';
test.isAttribute = true;
test.isSubscribe = true;
test.isSubscribeAttribute = true;
break;

Expand Down Expand Up @@ -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);
}

Expand All @@ -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;
Expand Down Expand Up @@ -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 => {
Expand All @@ -382,24 +407,27 @@ 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);
}

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');
Expand Down Expand Up @@ -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());
Expand All @@ -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;
}));
Expand Down Expand Up @@ -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 ];
Expand Down Expand Up @@ -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 [];
}
Expand Down
20 changes: 19 additions & 1 deletion src/app/zap-templates/common/ClustersHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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,
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -61,4 +67,5 @@ function isTestOnlyCluster(clusterName)
exports.getClusters = getClusters;
exports.getCommands = getCommands;
exports.getAttributes = getAttributes;
exports.getEvents = getEvents;
exports.isTestOnlyCluster = isTestOnlyCluster;
30 changes: 30 additions & 0 deletions src/app/zap-templates/templates/app/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
//
Expand All @@ -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;

0 comments on commit 1a6cb95

Please sign in to comment.