Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[YAML] Add events support #13542

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;