blob: 527fb349bc8a6da0522b180e2f2685bfd10b345e [file] [log] [blame]
/*
*
* Copyright (c) 2023 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.
*/
#include <app/clusters/scenes/SceneTableImpl.h>
#include <lib/core/TLV.h>
#include <lib/support/Span.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/UnitTestRegistration.h>
#include <nlunit-test.h>
using namespace chip;
using SceneTableEntry = scenes::DefaultSceneTableImpl::SceneTableEntry;
using SceneTableImpl = scenes::DefaultSceneTableImpl;
using SceneStorageId = scenes::DefaultSceneTableImpl::SceneStorageId;
using SceneData = scenes::DefaultSceneTableImpl::SceneData;
using ExtensionFieldsSet = scenes::ExtensionFieldsSet;
using TransitionTimeMs = scenes::TransitionTimeMs;
#define ON_OFF_CID 0x0006
#define LV_CTR_CID 0x0008
#define CC_CTR_CID 0x0300
#define TEST_ENDPOINT1 0x0001
#define TEST_ENDPOINT2 0x0099
// ON OFF ATTRIBUTE IDs
#define ON_OFF_ID 0x0000
// LEVEL CONTROL ATTRIBUTE IDs
#define CURRENT_LVL_ID 0x0000
#define CURRENT_FRQ_ID 0x0004
// COLOR CONTROL ATTRIBUTE IDs
#define CURRENT_SAT_ID 0x0001
#define CURRENT_X_ID 0x0003
#define CURRENT_Y_ID 0x0004
#define COLOR_TEMP_MIR_ID 00007
#define EN_CURRENT_HUE_ID 0x4000
#define C_LOOP_ACTIVE_ID 0x4002
#define C_LOOP_DIR_ID 0x4003
#define C_LOOP_TIME_ID 0x4004
namespace {
// Test fabrics, adding more requires to modify the "ResetSceneTable" function
constexpr chip::FabricIndex kFabric1 = 1;
constexpr chip::FabricIndex kFabric2 = 7;
// Scene storage ID
static const SceneStorageId sceneId1(TEST_ENDPOINT1, 0xAA, 0x101);
static const SceneStorageId sceneId2(TEST_ENDPOINT1, 0xBB, 0x00);
static const SceneStorageId sceneId3(TEST_ENDPOINT2, 0xCC, 0x102);
static const SceneStorageId sceneId4(TEST_ENDPOINT2, 0xBE, 0x00);
static const SceneStorageId sceneId5(TEST_ENDPOINT1, 0x45, 0x103);
static const SceneStorageId sceneId6(TEST_ENDPOINT1, 0x65, 0x00);
static const SceneStorageId sceneId7(TEST_ENDPOINT1, 0x77, 0x101);
static const SceneStorageId sceneId8(TEST_ENDPOINT2, 0xEE, 0x101);
static const SceneStorageId sceneId9(TEST_ENDPOINT2, 0xAB, 0x101);
// Scene data
static const SceneData sceneData1(CharSpan("Scene #1", sizeof("Scene #1")));
static const SceneData sceneData2(CharSpan("Scene #2", sizeof("Scene #2")), 2, 5);
static const SceneData sceneData3(CharSpan("Scene #3", sizeof("Scene #3")), 25);
static const SceneData sceneData4(CharSpan("Scene num4", sizeof("Scene num4")), 5);
static const SceneData sceneData5(CharSpan(), 10);
static const SceneData sceneData6(CharSpan("Scene #6", sizeof("Scene #6")), 3, 15);
static const SceneData sceneData7(CharSpan("Scene #7", sizeof("Scene #7")), 20, 5);
static const SceneData sceneData8(CharSpan("NAME TOO LOOONNG", sizeof("Scene num4")), 10);
static const SceneData sceneData9(CharSpan("Scene #9", sizeof("Scene #9")), 30, 15);
static const SceneData sceneData10(CharSpan("Scene #10", sizeof("Scene #10")), 10, 1);
static const SceneData sceneData11(CharSpan("Scene #11", sizeof("Scene #11")), 20, 10);
static const SceneData sceneData12(CharSpan("Scene #12", sizeof("Scene #12")), 30, 5);
// Scenes
SceneTableEntry scene1(sceneId1, sceneData1);
SceneTableEntry scene2(sceneId2, sceneData2);
SceneTableEntry scene3(sceneId3, sceneData3);
SceneTableEntry scene4(sceneId4, sceneData4);
SceneTableEntry scene5(sceneId5, sceneData5);
SceneTableEntry scene6(sceneId6, sceneData6);
SceneTableEntry scene7(sceneId7, sceneData7);
SceneTableEntry scene8(sceneId8, sceneData8);
SceneTableEntry scene9(sceneId9, sceneData9);
SceneTableEntry scene10(sceneId1, sceneData10);
SceneTableEntry scene11(sceneId5, sceneData11);
SceneTableEntry scene12(sceneId8, sceneData12);
// Clusters EFS data
static app::Clusters::Scenes::Structs::ExtensionFieldSet::Type OOextensionFieldSet;
static app::Clusters::Scenes::Structs::ExtensionFieldSet::Type LCextensionFieldSet;
static app::Clusters::Scenes::Structs::ExtensionFieldSet::Type CCextensionFieldSet;
static app::Clusters::Scenes::Structs::AttributeValuePair::Type OOPairs[1];
static app::Clusters::Scenes::Structs::AttributeValuePair::Type LCPairs[2];
static app::Clusters::Scenes::Structs::AttributeValuePair::Type CCPairs[8];
static uint8_t OO_buffer[scenes::kMaxFieldsPerCluster] = { 0 };
static uint8_t LC_buffer[scenes::kMaxFieldsPerCluster] = { 0 };
static uint8_t CC_buffer[scenes::kMaxFieldsPerCluster] = { 0 };
/// @brief Simulates a Handler where Endpoint 1 supports onoff and level control and Endpoint 2 supports onoff and color control
class TestSceneHandler : public scenes::DefaultSceneHandlerImpl
{
public:
TestSceneHandler() = default;
~TestSceneHandler() override {}
// Default function only checks if endpoint and clusters are valid
bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override
{
bool ret = false;
if (endpoint == TEST_ENDPOINT1)
{
if (cluster == ON_OFF_CID || cluster == LV_CTR_CID)
{
ret = true;
}
}
if (endpoint == TEST_ENDPOINT2)
{
if (cluster == ON_OFF_CID || cluster == CC_CTR_CID)
{
ret = true;
}
}
return ret;
}
/// @brief Simulates save from cluster, data is already in an EFS struct but this isn't mandatory
/// @param endpoint target endpoint
/// @param cluster target cluster
/// @param serialyzedBytes data to serialize into EFS
/// @return success if successfully serialized the data, CHIP_ERROR_INVALID_ARGUMENT if endpoint or cluster not supported
CHIP_ERROR SerializeSave(EndpointId endpoint, ClusterId cluster, MutableByteSpan & serialyzedBytes) override
{
CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT;
if (endpoint == TEST_ENDPOINT1)
{
switch (cluster)
{
case ON_OFF_CID:
err = CHIP_NO_ERROR;
memcpy(serialyzedBytes.data(), OO_buffer, scenes::kMaxFieldsPerCluster);
serialyzedBytes.reduce_size(15); // Used memory for OnOff TLV
break;
case LV_CTR_CID:
err = CHIP_NO_ERROR;
memcpy(serialyzedBytes.data(), LC_buffer, scenes::kMaxFieldsPerCluster);
serialyzedBytes.reduce_size(27); // Used memory for Level Control TLV
break;
default:
break;
}
}
if (endpoint == TEST_ENDPOINT2)
{
switch (cluster)
{
case ON_OFF_CID:
err = CHIP_NO_ERROR;
memcpy(serialyzedBytes.data(), OO_buffer, scenes::kMaxFieldsPerCluster);
serialyzedBytes.reduce_size(15); // Used memory for Color Control TLV
break;
case CC_CTR_CID:
err = CHIP_NO_ERROR;
memcpy(serialyzedBytes.data(), CC_buffer, scenes::kMaxFieldsPerCluster);
serialyzedBytes.reduce_size(99); // Used memory for Color Control TLV
break;
default:
break;
}
}
return err;
}
/// @brief Simulates EFS being applied to a scene, here just validates that the data is as expected, no action taken by the
/// "cluster"
/// @param endpoint target endpoint
/// @param cluster target cluster
/// @param serialyzedBytes Data from nvm
/// @param timeMs transition time in ms
/// @return CHIP_NO_ERROR if value as expected, CHIP_ERROR_INVALID_ARGUMENT otherwise
CHIP_ERROR
ApplyScene(EndpointId endpoint, ClusterId cluster, ByteSpan & serialyzedBytes, TransitionTimeMs timeMs) override
{
CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT;
// Takes values from cluster in Endpoint 1
if (endpoint == TEST_ENDPOINT1)
{
switch (cluster)
{
case ON_OFF_CID:
if (!memcmp(serialyzedBytes.data(), OO_buffer, serialyzedBytes.size()))
{
err = CHIP_NO_ERROR;
}
break;
case LV_CTR_CID:
if (!memcmp(serialyzedBytes.data(), LC_buffer, serialyzedBytes.size()))
{
err = CHIP_NO_ERROR;
}
break;
default:
break;
}
}
// Takes values from cluster in Endpoint 2
if (endpoint == TEST_ENDPOINT2)
{
switch (cluster)
{
case ON_OFF_CID:
if (!memcmp(serialyzedBytes.data(), OO_buffer, serialyzedBytes.size()))
{
err = CHIP_NO_ERROR;
}
break;
case CC_CTR_CID:
if (!memcmp(serialyzedBytes.data(), CC_buffer, serialyzedBytes.size()))
{
err = CHIP_NO_ERROR;
}
break;
default:
break;
}
}
return CHIP_NO_ERROR;
}
};
static chip::TestPersistentStorageDelegate testStorage;
static SceneTableImpl sSceneTable;
static TestSceneHandler sHandler;
void ResetSceneTable(SceneTableImpl * sceneTable)
{
sceneTable->RemoveFabric(kFabric1);
sceneTable->RemoveFabric(kFabric2);
}
void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
ClusterId tempCluster = 0;
app::Clusters::Scenes::Structs::ExtensionFieldSet::Type extensionFieldSetOut;
app::Clusters::Scenes::Structs::ExtensionFieldSet::DecodableType extensionFieldSetIn;
app::Clusters::Scenes::Structs::AttributeValuePair::DecodableType aVPair;
TLV::TLVReader reader;
TLV::TLVWriter writer;
TLV::TLVType outer;
TLV::TLVType outerRead;
static const uint8_t OO_av_payload[1] = { 0x01 };
static const uint8_t LC_av_payload[2][2] = { { 0x40, 0x00 }, { 0x01, 0xF0 } };
static const uint8_t CC_av_payload[8][2] = { { 0x00, 0x00 }, { 0x00, 0x00 }, { 0x00, 0x00 }, { 0x00, 0x00 },
{ 0x00, 0x00 }, { 0x00, 0x00 }, { 0x00, 0x00 }, { 0x00, 0x00 } };
OOPairs[0].attributeID.SetValue(ON_OFF_ID);
OOPairs[0].attributeValue = OO_av_payload;
LCPairs[0].attributeID.SetValue(CURRENT_LVL_ID);
LCPairs[0].attributeValue = LC_av_payload[0];
LCPairs[0].attributeValue.reduce_size(1);
LCPairs[1].attributeID.SetValue(CURRENT_FRQ_ID);
LCPairs[1].attributeValue = LC_av_payload[1];
CCPairs[0].attributeID.SetValue(CURRENT_SAT_ID);
CCPairs[0].attributeValue = CC_av_payload[0];
CCPairs[0].attributeValue.reduce_size(1);
CCPairs[1].attributeID.SetValue(CURRENT_X_ID);
CCPairs[1].attributeValue = CC_av_payload[1];
CCPairs[2].attributeID.SetValue(CURRENT_Y_ID);
CCPairs[2].attributeValue = CC_av_payload[2];
CCPairs[3].attributeID.SetValue(COLOR_TEMP_MIR_ID);
CCPairs[3].attributeValue = CC_av_payload[3];
CCPairs[4].attributeID.SetValue(EN_CURRENT_HUE_ID);
CCPairs[4].attributeValue = CC_av_payload[4];
CCPairs[5].attributeID.SetValue(C_LOOP_ACTIVE_ID);
CCPairs[5].attributeValue = CC_av_payload[5];
CCPairs[5].attributeValue.reduce_size(1);
CCPairs[6].attributeID.SetValue(C_LOOP_DIR_ID);
CCPairs[6].attributeValue = CC_av_payload[6];
CCPairs[6].attributeValue.reduce_size(1);
CCPairs[7].attributeID.SetValue(C_LOOP_TIME_ID);
CCPairs[7].attributeValue = CC_av_payload[7];
// Initialize Extension Field sets as if they were received by add commands
OOextensionFieldSet.clusterID = ON_OFF_CID;
OOextensionFieldSet.attributeValueList = OOPairs;
LCextensionFieldSet.clusterID = LV_CTR_CID;
LCextensionFieldSet.attributeValueList = LCPairs;
CCextensionFieldSet.clusterID = CC_CTR_CID;
CCextensionFieldSet.attributeValueList = CCPairs;
ByteSpan OO_list(OO_buffer);
ByteSpan LC_list(LC_buffer);
ByteSpan CC_list(CC_buffer);
uint8_t buffer[scenes::kMaxFieldsPerCluster] = { 0 };
MutableByteSpan buff_span(buffer);
// Serialize Extension Field sets as if they were recovered from memory
writer.Init(OO_buffer);
writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer);
NL_TEST_ASSERT(aSuite,
CHIP_NO_ERROR ==
app::DataModel::Encode(writer,
TLV::ContextTag(to_underlying(
app::Clusters::Scenes::Structs::ExtensionFieldSet::Fields::kAttributeValueList)),
OOextensionFieldSet.attributeValueList));
writer.EndContainer(outer);
writer.Init(LC_buffer);
writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer);
NL_TEST_ASSERT(aSuite,
CHIP_NO_ERROR ==
app::DataModel::Encode(writer,
TLV::ContextTag(to_underlying(
app::Clusters::Scenes::Structs::ExtensionFieldSet::Fields::kAttributeValueList)),
LCextensionFieldSet.attributeValueList));
writer.EndContainer(outer);
writer.Init(CC_buffer);
writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer);
NL_TEST_ASSERT(aSuite,
CHIP_NO_ERROR ==
app::DataModel::Encode(writer,
TLV::ContextTag(to_underlying(
app::Clusters::Scenes::Structs::ExtensionFieldSet::Fields::kAttributeValueList)),
CCextensionFieldSet.attributeValueList));
writer.EndContainer(outer);
// Test Registering SceneHandler
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RegisterHandler(&sHandler));
NL_TEST_ASSERT(aSuite, sceneTable->GetHandlerNum() == 1);
// Setup the On Off Extension field set in the expected state from a command
reader.Init(OO_list);
extensionFieldSetIn.clusterID = ON_OFF_CID;
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.EnterContainer(outerRead));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == extensionFieldSetIn.attributeValueList.Decode(reader));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.ExitContainer(outerRead));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.SerializeAdd(TEST_ENDPOINT1, tempCluster, buff_span, extensionFieldSetIn));
// Verify the handler extracted buffer matches the initial field sets
NL_TEST_ASSERT(aSuite, 0 == memcmp(OO_list.data(), buff_span.data(), buff_span.size()));
NL_TEST_ASSERT(aSuite, tempCluster == ON_OFF_CID);
memset(buffer, 0, buff_span.size());
// Setup the Level Control Extension field set in the expected state from a command
reader.Init(LC_list);
extensionFieldSetIn.clusterID = LV_CTR_CID;
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.EnterContainer(outerRead));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == extensionFieldSetIn.attributeValueList.Decode(reader));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.ExitContainer(outerRead));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.SerializeAdd(TEST_ENDPOINT1, tempCluster, buff_span, extensionFieldSetIn));
// Verify the handler extracted buffer matches the initial field sets
NL_TEST_ASSERT(aSuite, 0 == memcmp(LC_list.data(), buff_span.data(), buff_span.size()));
NL_TEST_ASSERT(aSuite, tempCluster == LV_CTR_CID);
memset(buffer, 0, buff_span.size());
// Setup the Color control Extension field set in the expected state from a command
reader.Init(CC_list);
extensionFieldSetIn.clusterID = CC_CTR_CID;
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.EnterContainer(outerRead));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == extensionFieldSetIn.attributeValueList.Decode(reader));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.ExitContainer(outerRead));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.SerializeAdd(TEST_ENDPOINT2, tempCluster, buff_span, extensionFieldSetIn));
// Verify the handler extracted buffer matches the initial field sets
NL_TEST_ASSERT(aSuite, 0 == memcmp(CC_list.data(), buff_span.data(), buff_span.size()));
NL_TEST_ASSERT(aSuite, tempCluster == CC_CTR_CID);
memset(buffer, 0, buff_span.size());
// Verify Deserializing is properly filling out output extension field set for on off
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.Deserialize(TEST_ENDPOINT1, ON_OFF_CID, OO_list, extensionFieldSetOut));
// Verify Encoding the Extension field set returns the same data as
writer.Init(buff_span);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer));
NL_TEST_ASSERT(aSuite,
CHIP_NO_ERROR ==
app::DataModel::Encode(writer,
TLV::ContextTag(to_underlying(
app::Clusters::Scenes::Structs::ExtensionFieldSet::Fields::kAttributeValueList)),
extensionFieldSetOut.attributeValueList));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == writer.EndContainer(outer));
NL_TEST_ASSERT(aSuite, 0 == memcmp(OO_list.data(), buff_span.data(), buff_span.size()));
memset(buffer, 0, buff_span.size());
// Verify Deserializing is properly filling out output extension field set for level control
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.Deserialize(TEST_ENDPOINT1, LV_CTR_CID, LC_list, extensionFieldSetOut));
// Verify Encoding the Extension field set returns the same data as
writer.Init(buff_span);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer));
NL_TEST_ASSERT(aSuite,
CHIP_NO_ERROR ==
app::DataModel::Encode(writer,
TLV::ContextTag(to_underlying(
app::Clusters::Scenes::Structs::ExtensionFieldSet::Fields::kAttributeValueList)),
extensionFieldSetOut.attributeValueList));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == writer.EndContainer(outer));
NL_TEST_ASSERT(aSuite, 0 == memcmp(LC_list.data(), buff_span.data(), buff_span.size()));
memset(buffer, 0, buff_span.size());
// Verify Deserializing is properly filling out output extension field set for color control
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.Deserialize(TEST_ENDPOINT2, CC_CTR_CID, CC_list, extensionFieldSetOut));
// Verify Encoding the Extension field set returns the same data as
writer.Init(buff_span);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer));
NL_TEST_ASSERT(aSuite,
CHIP_NO_ERROR ==
app::DataModel::Encode(writer,
TLV::ContextTag(to_underlying(
app::Clusters::Scenes::Structs::ExtensionFieldSet::Fields::kAttributeValueList)),
extensionFieldSetOut.attributeValueList));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == writer.EndContainer(outer));
NL_TEST_ASSERT(aSuite, 0 == memcmp(CC_list.data(), buff_span.data(), buff_span.size()));
memset(buffer, 0, buff_span.size());
};
void TestStoreScenes(nlTestSuite * aSuite, void * aContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
NL_TEST_ASSERT(aSuite, sceneTable);
// Reset test
ResetSceneTable(sceneTable);
// Populate scene1's EFS (Endpoint1)
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene1, ON_OFF_CID));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene1, LV_CTR_CID));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == sceneTable->SceneSaveEFS(scene1, CC_CTR_CID));
// Populate scene2's EFS (Endpoint1)
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene2, ON_OFF_CID));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene2, LV_CTR_CID));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == sceneTable->SceneSaveEFS(scene2, CC_CTR_CID));
// Populate scene3's EFS (Endpoint2)
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene3, ON_OFF_CID));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == sceneTable->SceneSaveEFS(scene3, LV_CTR_CID));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene3, CC_CTR_CID));
// Populate scene3's EFS (Endpoint2)
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene4, ON_OFF_CID));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == sceneTable->SceneSaveEFS(scene4, LV_CTR_CID));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene4, CC_CTR_CID));
SceneTableEntry scene;
// Set test
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene2));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene3));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene4));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene5));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene6));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene7));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene8));
// Too many scenes 1 fabric
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_LIST_LENGTH == sceneTable->SetSceneTableEntry(kFabric1, scene9));
// Not Found
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId9, scene));
// Get test
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, scene == scene1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneApplyEFS(kFabric1, scene1.mStorageId));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneApplyEFS(kFabric1, scene2.mStorageId));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
NL_TEST_ASSERT(aSuite, scene == scene3);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneApplyEFS(kFabric1, scene3.mStorageId));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene));
NL_TEST_ASSERT(aSuite, scene == scene4);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneApplyEFS(kFabric1, scene4.mStorageId));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, scene == scene5);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, scene == scene6);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));
NL_TEST_ASSERT(aSuite, scene == scene7);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene));
NL_TEST_ASSERT(aSuite, scene == scene8);
}
void TestOverwriteScenes(nlTestSuite * aSuite, void * aContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
NL_TEST_ASSERT(aSuite, sceneTable);
SceneTableEntry scene;
// Overwriting the first entry
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene10));
// Overwriting in the middle
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene11));
// Overwriting the last entry
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene12));
// Scene 10 has the same sceneId as scene 1, Get->sceneId1 should thus return scene 10, etc.
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, scene == scene10);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, scene == scene11);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene));
NL_TEST_ASSERT(aSuite, scene == scene12);
}
void TestIterateScenes(nlTestSuite * aSuite, void * aContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
NL_TEST_ASSERT(aSuite, sceneTable);
SceneTableEntry scene;
auto * iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator != nullptr);
if (iterator)
{
NL_TEST_ASSERT(aSuite, iterator->Count() == 8);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene10);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene3);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene4);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene11);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene6);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene7);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene12);
NL_TEST_ASSERT(aSuite, iterator->Next(scene) == false);
iterator->Release();
}
}
void TestRemoveScenes(nlTestSuite * aSuite, void * aContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
NL_TEST_ASSERT(aSuite, sceneTable);
SceneTableEntry scene;
// Remove middle
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene5.mStorageId));
auto * iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 7);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene10);
iterator->Release();
// Adde scene in middle, a spot should have been freed
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene9));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 8);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId9, scene));
NL_TEST_ASSERT(aSuite, scene == scene9);
iterator->Release();
// Remove the recently added scene 9
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene9.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 7);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene10);
iterator->Release();
// Remove first
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene1.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 6);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
iterator->Release();
// Remove Next
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene3.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 5);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene4);
iterator->Release();
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene2.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 4);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene4);
iterator->Release();
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene4.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 3);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene6);
iterator->Release();
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene6.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 2);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene7);
iterator->Release();
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene7.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 1);
NL_TEST_ASSERT(aSuite, iterator->Next(scene));
NL_TEST_ASSERT(aSuite, scene == scene12);
iterator->Release();
// Remove last
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene8.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 0);
NL_TEST_ASSERT(aSuite, iterator->Next(scene) == false);
iterator->Release();
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveSceneTableEntry(kFabric1, scene8.mStorageId));
iterator = sceneTable->IterateSceneEntry(kFabric1);
NL_TEST_ASSERT(aSuite, iterator->Count() == 0);
iterator->Release();
}
void TestFabricScenes(nlTestSuite * aSuite, void * aContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
NL_TEST_ASSERT(aSuite, sceneTable);
// Reset test
ResetSceneTable(sceneTable);
SceneTableEntry scene;
// Fabric 1 inserts
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene2));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene3));
// Fabric 2 inserts
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene1));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene2));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene3));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, scene == scene1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
NL_TEST_ASSERT(aSuite, scene == scene3);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene));
NL_TEST_ASSERT(aSuite, scene == scene1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId3, scene));
NL_TEST_ASSERT(aSuite, scene == scene3);
// Remove Fabric 1
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveFabric(kFabric1));
// Verify Fabric 1 removed
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveFabric(kFabric1));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
// Verify Fabric 2 still there
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene));
NL_TEST_ASSERT(aSuite, scene == scene1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene));
NL_TEST_ASSERT(aSuite, scene == scene2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId3, scene));
NL_TEST_ASSERT(aSuite, scene == scene3);
// Remove Fabric 2
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveFabric(kFabric2));
// Verify Fabric 2 removed
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveFabric(kFabric2));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId3, scene));
}
} // namespace
/**
* Tear down the test suite.
*/
int TestSetup(void * inContext)
{
VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE);
VerifyOrReturnError(CHIP_NO_ERROR == sSceneTable.Init(&testStorage), FAILURE);
SetSceneTable(&sSceneTable);
return SUCCESS;
}
/**
* Tear down the test suite.
*/
int TestTeardown(void * inContext)
{
SceneTableImpl * sceneTable = chip::scenes::GetSceneTable();
if (nullptr != sceneTable)
{
sceneTable->Finish();
}
chip::Platform::MemoryShutdown();
return SUCCESS;
}
int TestSceneTable()
{
static nlTest sTests[] = { NL_TEST_DEF("TestStoreScenes", TestHandlerFunctions),
NL_TEST_DEF("TestStoreScenes", TestStoreScenes),
NL_TEST_DEF("TestOverwriteScenes", TestOverwriteScenes),
NL_TEST_DEF("TestIterateScenes", TestIterateScenes),
NL_TEST_DEF("TestRemoveScenes", TestRemoveScenes),
NL_TEST_DEF("TestFabricScenes", TestFabricScenes),
NL_TEST_SENTINEL() };
nlTestSuite theSuite = {
"SceneTable",
&sTests[0],
TestSetup,
TestTeardown,
};
nlTestRunner(&theSuite, nullptr);
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestSceneTable)