blob: 3ed81a92d8c18a8863785a1c621719b93fa7490b [file]
/**
*
* Copyright (c) 2020 Silicon Labs
*
* 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.
*
*
* @jest-environment node
*/
const testUtil = require('./test-util')
const env = require('../src-electron/util/env')
const zclLoader = require('../src-electron/zcl/zcl-loader')
const dbApi = require('../src-electron/db/db-api')
const queryDeviceType = require('../src-electron/db/query-device-type')
const queryFeature = require('../src-electron/db/query-feature')
const querySession = require('../src-electron/db/query-session')
const queryConfig = require('../src-electron/db/query-config')
const queryZcl = require('../src-electron/db/query-zcl')
const util = require('../src-electron/util/util')
const conformEvaluator = require('../src-electron/validation/conformance-expression-evaluator')
const conformChecker = require('../src-electron/validation/conformance-checker')
const testQuery = require('./test-query')
const httpServer = require('../src-electron/server/http-server')
const restApi = require('../src-shared/rest-api')
const axios = require('axios')
const dbEnum = require('../src-shared/db-enum')
let db
let ctx
let pkgId
let sid
let eptId
let MA_dimmablelight
let deviceTypeName = 'MA-dimmablelight'
let axiosInstance = null
let clusters = null
let onOffCluster = null
beforeAll(async () => {
const { port, baseUrl } = testUtil.testServer(__filename)
env.setDevelopmentEnv()
axiosInstance = axios.create({ baseURL: baseUrl })
let file = env.sqliteTestFile('feature')
db = await dbApi.initDatabaseAndLoadSchema(
file,
env.schemaFile(),
env.zapVersion()
)
// load Matter packages for testing features
ctx = await zclLoader.loadZcl(db, env.builtinMatterZclMetafile())
pkgId = ctx.packageId
await httpServer.initHttpServer(db, port)
let userSession = await querySession.ensureZapUserAndSession(
db,
'USER',
'SESSION'
)
sid = userSession.sessionId
await util.ensurePackagesAndPopulateSessionOptions(
db,
sid,
{
zcl: env.builtinMatterZclMetafile(),
template: testUtil.testTemplate.matter3,
partitions: 2
},
null,
pkgId
)
/** test with MA-dimmablelight device type, the only device type
associated with features in current xml file
it should have 3 device type features */
MA_dimmablelight = await queryDeviceType.selectDeviceTypeByCodeAndName(
db,
pkgId,
257,
deviceTypeName
)
let sessionPartitionInfo =
await querySession.selectSessionPartitionInfoFromDeviceType(
db,
sid,
MA_dimmablelight.id
)
eptId = await queryConfig.insertEndpointType(
db,
sessionPartitionInfo[0],
'testEndpointType',
MA_dimmablelight.id,
MA_dimmablelight.code,
0,
true
)
clusters = await testQuery.getAllEndpointTypeClusterState(db, eptId)
onOffCluster = clusters.find((cluster) => cluster.clusterName == 'On/Off')
}, testUtil.timeout.medium())
afterAll(
() => httpServer.shutdownHttpServer().then(() => dbApi.closeDatabase(db)),
testUtil.timeout.medium()
)
test(
'Get device type features by device type ref',
async () => {
let deviceTypeFeatures = await queryFeature.getFeaturesByDeviceTypeRefs(
db,
[MA_dimmablelight.id],
eptId
)
expect(deviceTypeFeatures.length).toBe(3)
let expectedDeviceTypeFeatures = [
{
cluster: 'On/Off',
name: 'Lighting',
conformance: 'M'
},
{
cluster: 'Level Control',
name: 'OnOff',
conformance: 'M'
},
{
cluster: 'Level Control',
name: 'Lighting',
conformance: 'M'
}
]
deviceTypeFeatures.forEach((feature) => {
expect(feature.deviceType == deviceTypeName)
let expected = expectedDeviceTypeFeatures.find(
(f) =>
f.cluster == feature.cluster &&
f.name == feature.name &&
f.conformance == feature.conformance
)
expect(expected).toBeTruthy()
})
},
testUtil.timeout.long()
)
test(
'Evaluate customized conformance expression and operand extraction',
() => {
// cover elements with all upper and lower case and combinations
const elementMap = {
HS: true,
Lt: true,
oo: false,
xY: false,
Primary1X: true
}
let conformanceExpressions = [
// 1. test basic conformance abbreviations
{ expression: 'M', expected: 'mandatory', operands: ['M'] },
{ expression: 'O', expected: 'optional', operands: ['O'] },
{ expression: 'P', expected: 'provisional', operands: ['P'] },
{ expression: 'D', expected: 'notSupported', operands: ['D'] },
{ expression: 'X', expected: 'notSupported', operands: ['X'] },
// 2. test simple mandatory conformance expression
{ expression: 'HS', expected: 'mandatory', operands: ['HS'] },
{ expression: 'oo', expected: 'notSupported', operands: ['oo'] },
{ expression: 'HS & Lt', expected: 'mandatory', operands: ['HS', 'Lt'] },
{
expression: 'HS & oo',
expected: 'notSupported',
operands: ['HS', 'oo']
},
{ expression: 'HS | oo', expected: 'mandatory', operands: ['HS', 'oo'] },
{
expression: 'HS & Lt & oo',
expected: 'notSupported',
operands: ['HS', 'Lt', 'oo']
},
{
expression: 'HS | Lt | oo',
expected: 'mandatory',
operands: ['HS', 'Lt', 'oo']
},
{
expression: 'HS & (Lt | oo)',
expected: 'mandatory',
operands: ['HS', 'Lt', 'oo']
},
{
expression: 'HS & (oo | xY)',
expected: 'notSupported',
operands: ['HS', 'oo', 'xY']
},
{
expression: '(HS & oo) | Lt',
expected: 'mandatory',
operands: ['HS', 'oo', 'Lt']
},
{
expression: 'HS & (Lt | oo | xY)',
expected: 'mandatory',
operands: ['HS', 'Lt', 'oo', 'xY']
},
{
expression: 'oo | (HS & Lt & xY)',
expected: 'notSupported',
operands: ['oo', 'HS', 'Lt', 'xY']
},
// 3. test NOT (!)
{ expression: '!HS', expected: 'notSupported', operands: ['HS'] },
{ expression: '!oo', expected: 'mandatory', operands: ['oo'] },
{
expression: '!HS & Lt',
expected: 'notSupported',
operands: ['HS', 'Lt']
},
{ expression: 'HS & !oo', expected: 'mandatory', operands: ['HS', 'oo'] },
{
expression: '!(HS & Lt)',
expected: 'notSupported',
operands: ['HS', 'Lt']
},
{
expression: '!(HS | oo)',
expected: 'notSupported',
operands: ['HS', 'oo']
},
{
expression: '!(HS & oo)',
expected: 'mandatory',
operands: ['HS', 'oo']
},
// 4. test optional conformance expression with '[]'
{ expression: '[HS]', expected: 'optional', operands: ['HS'] },
{ expression: '[oo]', expected: 'notSupported', operands: ['oo'] },
{ expression: '[!HS]', expected: 'notSupported', operands: ['HS'] },
{ expression: '[!oo]', expected: 'optional', operands: ['oo'] },
{ expression: '[HS & Lt]', expected: 'optional', operands: ['HS', 'Lt'] },
{
expression: '[HS & oo]',
expected: 'notSupported',
operands: ['HS', 'oo']
},
{ expression: '[HS | oo]', expected: 'optional', operands: ['HS', 'oo'] },
{
expression: '[!(HS & Lt)]',
expected: 'notSupported',
operands: ['HS', 'Lt']
},
{
expression: '[!(HS | oo)]',
expected: 'notSupported',
operands: ['HS', 'oo']
},
{
expression: '[!(HS & oo)]',
expected: 'optional',
operands: ['HS', 'oo']
},
// 5. test otherwise conformance expression
{ expression: 'P, O', expected: 'provisional', operands: ['P', 'O'] },
{ expression: 'P, M', expected: 'provisional', operands: ['P', 'M'] },
{ expression: 'P, HS', expected: 'provisional', operands: ['P', 'HS'] },
{ expression: 'P, [HS]', expected: 'provisional', operands: ['P', 'HS'] },
{ expression: 'HS, O', expected: 'mandatory', operands: ['HS', 'O'] },
{ expression: 'oo, O', expected: 'optional', operands: ['oo', 'O'] },
{ expression: 'oo, [HS]', expected: 'optional', operands: ['oo', 'HS'] },
{
expression: 'oo, [xY]',
expected: 'notSupported',
operands: ['oo', 'xY']
},
{ expression: '[HS], D', expected: 'optional', operands: ['HS', 'D'] },
{
expression: '[oo], D',
expected: 'notSupported',
operands: ['oo', 'D']
},
{
expression: 'HS, [Lt | oo | xY]',
expected: 'mandatory',
operands: ['HS', 'Lt', 'oo', 'xY']
},
{
expression: 'oo, [Lt | oo | xY]',
expected: 'optional',
operands: ['oo', 'Lt', 'oo', 'xY']
},
{
expression: 'oo, [xY | oo]',
expected: 'notSupported',
operands: ['oo', 'xY', 'oo']
},
{
expression: 'HS & Lt, [oo]',
expected: 'mandatory',
operands: ['HS', 'Lt', 'oo']
},
{
expression: 'HS & oo, [Lt]',
expected: 'optional',
operands: ['HS', 'oo', 'Lt']
},
{
expression: 'HS & oo, [xY]',
expected: 'notSupported',
operands: ['HS', 'oo', 'xY']
},
{
expression: 'HS | oo, [Lt & xY]',
expected: 'mandatory',
operands: ['HS', 'oo', 'Lt', 'xY']
},
{
expression: 'oo | xY, [HS & Lt]',
expected: 'optional',
operands: ['oo', 'xY', 'HS', 'Lt']
},
{
expression: 'xY | oo, [HS & xY]',
expected: 'notSupported',
operands: ['xY', 'oo', 'HS', 'xY']
},
// 6. test operands containing numbers
{
expression: 'Primary1X',
expected: 'mandatory',
operands: ['Primary1X']
},
{
expression: 'Primary1X & HS',
expected: 'mandatory',
operands: ['Primary1X', 'HS']
},
{
expression: 'Primary1X | oo',
expected: 'mandatory',
operands: ['Primary1X', 'oo']
},
// 7. test conformance with desc operands
{ expression: 'desc', expected: 'desc', operands: ['desc'] },
{ expression: 'HS & desc', expected: 'desc', operands: ['HS', 'desc'] },
{
expression: 'HS | (!xY && desc)',
expected: 'desc',
operands: ['HS', 'xY', 'desc']
},
{ expression: 'P, desc', expected: 'desc', operands: ['P', 'desc'] }
]
conformanceExpressions.forEach((expression) => {
let result = conformEvaluator.evaluateConformanceExpression(
expression.expression,
elementMap
)
expect(result).toBe(expression.expected)
let operands = conformEvaluator.getOperandsFromExpression(
expression.expression
)
expect(operands).toEqual(expression.operands)
})
},
testUtil.timeout.short()
)
test(
'Translate conformance expression to natural language text',
() => {
const conformanceExpressions = [
{ expression: '', translation: '' },
{ expression: 'M', translation: 'mandatory' },
{ expression: 'O', translation: 'optional' },
{ expression: 'P', translation: 'provisional' },
{ expression: 'D', translation: 'deprecated' },
{ expression: 'X', translation: 'disallowed' },
{
expression: 'desc',
translation: dbEnum.conformanceVal.described
},
{
expression: 'HS',
translation: 'mandatory if HS is enabled, otherwise it is not supported'
},
{
expression: '[HS]',
translation: 'optional if HS is enabled, otherwise it is not supported'
},
{
expression: 'HS & LT',
translation:
'mandatory if HS is enabled and LT is enabled, otherwise it is not supported'
},
{
expression: 'HS | LT',
translation:
'mandatory if HS is enabled or LT is enabled, otherwise it is not supported'
},
{
expression: '!HS',
translation:
'mandatory if HS is not enabled, otherwise it is not supported'
},
{
expression: '[!HS]',
translation:
'optional if HS is not enabled, otherwise it is not supported'
},
{
expression: 'HS & !LT',
translation:
'mandatory if HS is enabled and LT is not enabled, otherwise it is not supported'
},
{
expression: 'HS | LT | OO',
translation:
'mandatory if HS is enabled or LT is enabled or OO is enabled, otherwise it is not supported'
},
{
expression: '[HS & LT & OO]',
translation:
'optional if HS is enabled and LT is enabled and OO is enabled, otherwise it is not supported'
},
{
expression: '(HS & LT) | OO',
translation:
'mandatory if ( HS is enabled and LT is enabled ) or OO is enabled, otherwise it is not supported'
},
{
expression: 'HS | (LT & OO)',
translation:
'mandatory if HS is enabled or ( LT is enabled and OO is enabled ), otherwise it is not supported'
},
{
expression: 'HS, O',
translation: 'mandatory if HS is enabled, otherwise it is optional'
},
{
expression: 'HS, [LT]',
translation:
'mandatory if HS is enabled, otherwise it is optional if LT is enabled, otherwise it is not supported'
},
{
expression: 'HS & LT, [!OO]',
translation:
'mandatory if HS is enabled and LT is enabled, otherwise it is optional if OO is not enabled, otherwise it is not supported'
},
{
expression: 'P, O',
translation:
'provisional for now. When not provisional in the future, it is optional'
},
{
expression: 'P, HS',
translation:
'provisional for now. When not provisional in the future, it is mandatory if HS is enabled, otherwise it is not supported'
},
{
expression: 'P, [HS & LT]',
translation:
'provisional for now. When not provisional in the future, it is optional if HS is enabled and LT is enabled, otherwise it is not supported'
},
{
expression: 'desc, O',
translation:
dbEnum.conformanceVal.described + ', otherwise it is optional'
},
{
expression: '[HS & LT], D',
translation:
'optional if HS is enabled and LT is enabled, otherwise it is deprecated'
},
{
expression: 'HS & LT, [OO & XY], D',
translation:
'mandatory if HS is enabled and LT is enabled, otherwise it is optional if OO is enabled and XY is enabled, otherwise it is deprecated'
},
{
expression: '[HoldTime & (PIR | (!PIR & !US & !PHY))], D',
translation:
'optional if HoldTime is enabled and ( PIR is enabled or ( PIR is not enabled and US is not enabled and PHY is not enabled ) ), otherwise it is deprecated'
},
{
expression: 'Matter & (HS | LT | OO)',
translation:
'mandatory if Matter is enabled and ( HS is enabled or LT is enabled or OO is enabled ), otherwise it is not supported'
},
{
expression: '[!(LT | DF)]',
translation:
'optional if not ( LT is enabled or DF is enabled ), otherwise it is not supported'
}
]
conformanceExpressions.forEach((expression) => {
let result = conformEvaluator.translateConformanceExpression(
expression.expression
)
expect(result).toBe(expression.translation)
})
},
testUtil.timeout.short()
)
test(
'Check if an element has conformance with desc operands',
() => {
const elements = [
{ name: 'Element1', conformance: 'desc' },
{ name: 'Element2', conformance: 'P, desc' },
{ name: 'Element3', conformance: '[desc & XY]' },
{ name: 'Element4', conformance: 'desc, [HS]' },
{ name: 'Element5', conformance: 'desc | optional' },
{ name: 'Element6', conformance: 'notSupported' },
{ name: 'Element7', conformance: 'description' }
]
let descOperands = conformEvaluator.filterRelatedDescElements(
elements,
'desc'
)
expect(descOperands).toEqual([
{ name: 'Element1', conformance: 'desc' },
{ name: 'Element2', conformance: 'P, desc' },
{ name: 'Element3', conformance: '[desc & XY]' },
{ name: 'Element4', conformance: 'desc, [HS]' },
{ name: 'Element5', conformance: 'desc | optional' }
])
const featureCode = 'HS'
let relatedDescOperands = conformEvaluator.filterRelatedDescElements(
elements,
featureCode
)
expect(relatedDescOperands).toEqual([
{ name: 'Element4', conformance: 'desc, [HS]' }
])
},
testUtil.timeout.short()
)
test(
'Check elements with conformance needs to be updated and generate warnings',
() => {
/* simulate toggling features in Color Control cluster
to test if elements with wrong conformance are checked and returned,
and test if generated warnings are correct */
// define part of elements in Color Control cluster
let elements = {
attributes: [
{ name: 'CurrentHue', conformance: 'HS', included: false },
{ name: 'DriftCompensation', conformance: 'O', included: false },
{ name: 'ColorMode', conformance: 'M', included: true },
{ name: 'EnhancedCurrentHue', conformance: 'EHUE', included: true },
{ name: 'ColorLoopActive', conformance: 'CL', included: true },
{ name: 'CurrentX', conformance: 'XY', included: true },
{ name: 'ColorTemperatureMireds', conformance: 'CT', included: true },
{
name: 'AttributeDependingOnUNKNOWN',
conformance: 'UNKNOWN',
included: false
}
],
commands: [
{ name: 'MoveToHue', conformance: 'HS', isEnabled: false },
{ name: 'EnhancedMoveToHue', conformance: 'EHUE', isEnabled: false },
{ name: 'ColorLoopSet', conformance: 'CL', isEnabled: false },
{ name: 'MoveColorTemperature', conformance: 'CT', isEnabled: true },
{ name: 'MoveToColor', conformance: 'XY', isEnabled: true },
{
name: 'CommandDependingOnUNKNOWN',
conformance: 'UNKNOWN',
isEnabled: false
}
],
events: [
{ name: 'event1', conformance: 'HS', included: false },
{ name: 'event2', conformance: 'O', included: false }
]
}
let featureMap = {
HS: false,
EHUE: false,
CL: false,
XY: true,
CT: true,
UNKNOWN: false
}
let deviceType = 'Matter Extended Color Light'
let cluster = 'Color Control'
let featureBit = 0
let featureHS = {
cluster: cluster,
name: 'Hue/Saturation',
code: 'HS',
conformance: 'O',
deviceTypes: [deviceType],
bit: featureBit
}
let featureXY = {
cluster: cluster,
name: 'XY',
code: 'XY',
conformance: 'M',
deviceTypes: [deviceType],
bit: featureBit
}
let featureUnknown = {
cluster: cluster,
name: 'Unknown',
code: 'UNKNOWN',
conformance: 'Feature1 | Feature2',
deviceTypes: [deviceType],
bit: featureBit
}
let clusterFeatures = [featureHS, featureXY, featureUnknown]
let endpointId = 1
let result = {}
let expectedWarning = ''
let warningPrefix = `âš  Check Feature Compliance on endpoint: ${endpointId}, cluster: ${cluster}, `
let featureBitMessage = `(bit ${featureBit} in featureMap attribute)`
// 1. test enable an optional feature
featureMap['HS'] = 1
result = conformChecker.checkElementConformance(
elements,
featureMap,
featureHS,
endpointId,
clusterFeatures
)
// no warnings should be generated
expect(result.displayWarning).toBeFalsy()
expect(result.disableChange).toBeFalsy()
expect(result.warningMessage).toBe('')
// elements with conformance 'HS' should be enabled
// and their values should be set to true
expect(result.attributesToUpdate.length).toBe(1)
expect(result.attributesToUpdate[0].name).toBe('CurrentHue')
expect(result.attributesToUpdate[0].value).toBeTruthy()
expect(result.commandsToUpdate.length).toBe(1)
expect(result.commandsToUpdate[0].name).toBe('MoveToHue')
expect(result.commandsToUpdate[0].value).toBeTruthy()
expect(result.eventsToUpdate.length).toBe(1)
expect(result.eventsToUpdate[0].name).toBe('event1')
expect(result.eventsToUpdate[0].value).toBeTruthy()
featureMap['HS'] = 0
// 2. test disable a mandatory feature
featureMap['XY'] = 0
result = conformChecker.checkElementConformance(
elements,
featureMap,
featureXY,
endpointId,
clusterFeatures
)
// should throw and display warning
expectedWarning =
warningPrefix +
`feature: ${featureXY.name} (${featureXY.code}) ${featureBitMessage} should be enabled, ` +
`as it is mandatory for device type: ${deviceType}.`
expect(result.displayWarning).toBeTruthy()
expect(result.disableChange).toBeFalsy()
expect(result.warningMessage).toBe(expectedWarning)
// attributes and commands with conformance 'XY' should be disabled
// and their values should be set to false
expect(result.attributesToUpdate.length).toBe(1)
expect(result.attributesToUpdate[0].name).toBe('CurrentX')
expect(result.attributesToUpdate[0].value).toBeFalsy()
expect(result.commandsToUpdate.length).toBe(1)
expect(result.commandsToUpdate[0].name).toBe('MoveToColor')
expect(result.commandsToUpdate[0].value).toBeFalsy()
expect(result.eventsToUpdate.length).toBe(0)
featureMap['XY'] = 1
// 3. test enable a feature with unknown conformance
featureMap['UNKNOWN'] = 1
result = conformChecker.checkElementConformance(
elements,
featureMap,
featureUnknown,
endpointId,
clusterFeatures
)
expectedWarning =
warningPrefix +
`feature: ${featureUnknown.name} (${featureUnknown.code}) ${featureBitMessage} cannot be enabled ` +
`as its conformance depends on the following operands with unknown values: Feature1, Feature2.`
// should display warning and disable the change
// no attributes commands, or events should be updated
expect(result.displayWarning).toBeTruthy()
expect(result.disableChange).toBeTruthy()
expect(result.warningMessage[0]).toBe(expectedWarning)
expect(result.attributesToUpdate.length).toBe(0)
expect(result.commandsToUpdate.length).toBe(0)
expect(result.eventsToUpdate.length).toBe(0)
featureMap['UNKNOWN'] = 0
// 4. test enable a feature with desc elements with conformance
// containing desc and updated feature code, expect same warning flags as 3
let descElement = {
name: 'DescElement',
conformance: 'HS & desc',
included: false
}
elements.attributes.push(descElement)
featureMap['HS'] = 1
result = conformChecker.checkElementConformance(
elements,
featureMap,
featureHS,
endpointId,
clusterFeatures
)
expectedWarning =
warningPrefix +
`feature: ${featureHS.name} (${featureHS.code}) ${featureBitMessage} ` +
`cannot be enabled as attribute ${descElement.name} ` +
`depend on the feature and their conformance are too complex for ZAP to process, or they include 'desc'.`
expect(result.displayWarning).toBeTruthy()
expect(result.disableChange).toBeTruthy()
expect(result.warningMessage[0]).toBe(expectedWarning)
expect(result.attributesToUpdate.length).toBe(0)
expect(result.commandsToUpdate.length).toBe(0)
expect(result.eventsToUpdate.length).toBe(0)
featureMap['HS'] = 0
// 5. test enable a feature with unknown conformance and relevant desc elements
// should have 2 warnings
elements.attributes.find((attr) => attr.name == 'DescElement').conformance =
'desc, [UNKNOWN]'
featureMap['UNKNOWN'] = 1
result = conformChecker.checkElementConformance(
elements,
featureMap,
featureUnknown,
endpointId,
clusterFeatures
)
expect(result.displayWarning).toBeTruthy()
expect(result.disableChange).toBeTruthy()
expect(result.warningMessage.length).toBe(2)
expect(result.attributesToUpdate.length).toBe(0)
expect(result.commandsToUpdate.length).toBe(0)
expect(result.eventsToUpdate.length).toBe(0)
},
testUtil.timeout.short()
)
test(
'Test API for getting FeatureMap attribute value',
async () => {
// get relevant data from the On/Off cluster and pass it to the API
let attributes =
await queryZcl.selectAttributesByClusterIdAndSideIncludingGlobal(
db,
onOffCluster.clusterId,
[pkgId],
onOffCluster.side
)
let featureMapAttribute = attributes.find(
(attribute) =>
attribute.name == dbEnum.featureMapAttribute.name &&
attribute.code == dbEnum.featureMapAttribute.code
)
let resp = await axiosInstance.get(restApi.uri.featureMapAttribute, {
params: {
attributeId: featureMapAttribute.id,
clusterId: onOffCluster.clusterId,
endpointTypeId: eptId
}
})
let featureMapValue = parseInt(resp.data.defaultValue)
/* The featureMap value should be 1 because, in the Dimmable Light device type, On/Off cluster,
only the Lighting (LT) feature on bit 0 is enabled by default. */
expect(featureMapValue).toBe(1)
},
testUtil.timeout.long()
)
test(
'Test function to get endpoint type cluster ID from feature data or by querying the database',
async () => {
// if featureData has no clusterRef, it should query DB with the given clusterRef parameter
// and return endpoint type cluster ID of the On/Off cluster
let featureData = null
let endpointTypeClusterId =
await conformChecker.getEndpointTypeClusterIdFromFeatureData(
db,
featureData,
eptId,
onOffCluster.clusterId
)
expect(endpointTypeClusterId).toBe(onOffCluster.endpointTypeClusterId)
let levelControlCluster = clusters.find(
(cluster) => cluster.clusterName == 'Level Control'
)
featureData = { clusterRef: levelControlCluster.clusterId }
// if featureData has clusterRef, it should take precedence over the clusterRef parameter
// and the DB query should return endpoint type cluster ID of the Level Control cluster
endpointTypeClusterId =
await conformChecker.getEndpointTypeClusterIdFromFeatureData(
db,
featureData,
eptId,
onOffCluster.clusterId
)
expect(endpointTypeClusterId).toBe(
levelControlCluster.endpointTypeClusterId
)
},
testUtil.timeout.medium()
)
test(
'Test function to get a text summary of enabled/disabled state for element operands in a conformance expression',
() => {
let featureMap = { feature1: true, feature2: false }
let elementMap = {
...featureMap,
attribute1: true,
attribute2: false,
command1: true,
command2: false
}
let conformanceExpression = 'feature1 & !feature2, (attribute1 | !command2)'
let result = conformChecker.getStateOfOperands(
conformanceExpression,
elementMap,
featureMap
)
expect(result).toBe(
'feature: feature1 is enabled, feature: feature2 is disabled, element: attribute1 is enabled, element: command2 is disabled'
)
},
testUtil.timeout.short()
)
test(
'Test function to check features need to be updated or have conformance changes due to the updated feature',
() => {
// simulate enabling feature A
let updatedFeatureCode = 'A'
let elementMap = {
A: true,
B: false,
C: true,
D: false,
E: false,
F: false,
G: false,
H: false
}
let clusterFeatures = [
{ code: 'A', conformance: 'O' },
{ code: 'B', conformance: 'A' },
{ code: 'C', conformance: 'A, O' },
{ code: 'D', conformance: 'A & B' },
{ code: 'E', conformance: 'A | B' },
{ code: 'F', conformance: '[A & C]' },
// Conformance of G and H are unrelated to A,
// so they should not appear in returned values
{ code: 'G', conformance: 'B' },
{ code: 'H', conformance: 'O' }
]
let { updatedFeatures, changedConformFeatures } =
conformEvaluator.checkFeaturesToUpdate(
updatedFeatureCode,
clusterFeatures,
elementMap
)
// B and E becomes mandatory but are not enabled, so they should be updated
expect(updatedFeatures).toEqual({ B: true, E: true })
// Conformance of B, C, and E changed from notSupported to mandatory
// Conformance of F changed from notSupported to optional
// C becomes mandatory but it is already enabled, so it should not be updated
// F becomes optional so no update is needed
expect(changedConformFeatures).toEqual([
{ code: 'B', conformance: 'A' },
{ code: 'C', conformance: 'A, O' },
{ code: 'E', conformance: 'A | B' },
{ code: 'F', conformance: '[A & C]' }
])
},
testUtil.timeout.short()
)