blob: 3f9400fe109e032ad8b9e91809d30a9fa8aac482 [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/ExtensionFieldSetsImpl.h>
#include <lib/core/TLV.h>
#include <lib/support/Span.h>
#include <lib/support/UnitTestRegistration.h>
#include <nlunit-test.h>
using namespace chip;
namespace TestEFS {
enum class TagTestEFS : uint8_t
{
kEFS = 1,
kTestArray,
};
static constexpr size_t kPersistentSceneBufferMax = 256;
// Test Cluster ID
constexpr chip::ClusterId kOnOffClusterId = 0x0006;
constexpr chip::ClusterId kLevelControlClusterId = 0x0008;
constexpr chip::ClusterId kColorControlClusterId = 0x0300;
constexpr uint8_t kOnOffSize = 1;
constexpr uint8_t kLevelControlSize = 3;
constexpr uint8_t kColorControlSize = 14;
static uint8_t onOffBuffer[scenes::kMaxFieldBytesPerCluster] = "0";
static uint8_t levelControlBuffer[scenes::kMaxFieldBytesPerCluster] = "123";
static uint8_t colorControlBuffer[scenes::kMaxFieldBytesPerCluster] = "abcdefghijklmn";
static const scenes::ExtensionFieldSet EFS1(kOnOffClusterId, onOffBuffer, kOnOffSize);
static const scenes::ExtensionFieldSet EFS2(kLevelControlClusterId, levelControlBuffer, kLevelControlSize);
static const scenes::ExtensionFieldSet EFS3(kColorControlClusterId, colorControlBuffer, kColorControlSize);
static scenes::ExtensionFieldSetsImpl sEFSets;
void TestInsertExtensionFieldSet(nlTestSuite * aSuite, void * aContext)
{
scenes::ExtensionFieldSetsImpl * EFS = &sEFSets;
scenes::ExtensionFieldSetsImpl testEFS1;
scenes::ExtensionFieldSetsImpl testEFS2;
scenes::ExtensionFieldSetsImpl testEFS3;
scenes::ExtensionFieldSetsImpl tempTestEFS;
scenes::ExtensionFieldSet tempEFS;
uint8_t empty_buffer[scenes::kMaxFieldBytesPerCluster] = { 0 };
uint8_t double_size_buffer[scenes::kMaxFieldBytesPerCluster + 1];
ByteSpan bufferSpan(double_size_buffer);
memset(double_size_buffer, static_cast<uint8_t>(1), sizeof(double_size_buffer));
NL_TEST_ASSERT(aSuite, true == EFS->IsEmpty());
// Test creators of single ExtensionFieldSet
NL_TEST_ASSERT(aSuite, EFS1.mID == kOnOffClusterId);
NL_TEST_ASSERT(aSuite, EFS1.mUsedBytes == kOnOffSize);
NL_TEST_ASSERT(aSuite, !memcmp(onOffBuffer, EFS1.mBytesBuffer, EFS1.mUsedBytes));
NL_TEST_ASSERT(aSuite, EFS2.mID == kLevelControlClusterId);
NL_TEST_ASSERT(aSuite, EFS2.mUsedBytes == kLevelControlSize);
NL_TEST_ASSERT(aSuite, !memcmp(levelControlBuffer, EFS2.mBytesBuffer, EFS2.mUsedBytes));
NL_TEST_ASSERT(aSuite, EFS3.mID == kColorControlClusterId);
NL_TEST_ASSERT(aSuite, EFS3.mUsedBytes == kColorControlSize);
NL_TEST_ASSERT(aSuite, !memcmp(colorControlBuffer, EFS3.mBytesBuffer, EFS3.mUsedBytes));
// operator tests single EFS
tempEFS = EFS1;
NL_TEST_ASSERT(aSuite, tempEFS == EFS1);
tempEFS = EFS2;
NL_TEST_ASSERT(aSuite, tempEFS == EFS2);
tempEFS = EFS3;
NL_TEST_ASSERT(aSuite, tempEFS == EFS3);
// Test clear EFS
tempEFS.Clear();
NL_TEST_ASSERT(aSuite, tempEFS.IsEmpty());
NL_TEST_ASSERT(aSuite, tempEFS.mID == kInvalidClusterId);
NL_TEST_ASSERT(aSuite, tempEFS.mUsedBytes == 0);
NL_TEST_ASSERT(aSuite, !memcmp(empty_buffer, tempEFS.mBytesBuffer, sizeof(tempEFS.mBytesBuffer)));
// Test creation of EFS from Array and ByteSpan that are to big
tempEFS = scenes::ExtensionFieldSet(kOnOffClusterId, double_size_buffer, sizeof(double_size_buffer));
NL_TEST_ASSERT(aSuite, tempEFS.mID == kOnOffClusterId);
// Confirm EFS empty
NL_TEST_ASSERT(aSuite, tempEFS.mUsedBytes == 0);
NL_TEST_ASSERT(aSuite, !memcmp(empty_buffer, tempEFS.mBytesBuffer, sizeof(empty_buffer)));
tempEFS = scenes::ExtensionFieldSet(kLevelControlClusterId, bufferSpan);
NL_TEST_ASSERT(aSuite, tempEFS.mID == kLevelControlClusterId);
// Confirm EFS empty
NL_TEST_ASSERT(aSuite, tempEFS.mUsedBytes == 0);
NL_TEST_ASSERT(aSuite, !memcmp(empty_buffer, tempEFS.mBytesBuffer, sizeof(empty_buffer)));
// Test creation of EFS from truncating an Array
tempEFS = scenes::ExtensionFieldSet(kColorControlClusterId, double_size_buffer, sizeof(tempEFS.mBytesBuffer));
NL_TEST_ASSERT(aSuite, tempEFS.mID == kColorControlClusterId);
// Confirm EFS was written
NL_TEST_ASSERT(aSuite, tempEFS.mUsedBytes == static_cast<uint8_t>(sizeof(tempEFS.mBytesBuffer)));
NL_TEST_ASSERT(aSuite, !memcmp(double_size_buffer, tempEFS.mBytesBuffer, sizeof(tempEFS.mBytesBuffer)));
tempEFS.Clear();
NL_TEST_ASSERT(aSuite, tempEFS.IsEmpty());
// Test insertion of uninitialized EFS
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == EFS->InsertFieldSet(tempEFS));
NL_TEST_ASSERT(aSuite, 0 == EFS->GetFieldSetCount());
// Test insertion of empty EFS
tempEFS.mID = kOnOffClusterId;
NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == EFS->InsertFieldSet(tempEFS));
NL_TEST_ASSERT(aSuite, 0 == EFS->GetFieldSetCount());
// test operators on multiple EFS struct
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == testEFS1.InsertFieldSet(EFS1));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == testEFS1.InsertFieldSet(EFS2));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == testEFS1.InsertFieldSet(EFS3));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == testEFS2.InsertFieldSet(EFS3));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == testEFS3.InsertFieldSet(EFS1));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == testEFS3.InsertFieldSet(EFS2));
tempTestEFS = testEFS1;
NL_TEST_ASSERT(aSuite, tempTestEFS == testEFS1);
NL_TEST_ASSERT(aSuite, !(tempTestEFS == testEFS2));
NL_TEST_ASSERT(aSuite, !(tempTestEFS == testEFS3));
tempTestEFS = testEFS2;
NL_TEST_ASSERT(aSuite, tempTestEFS == testEFS2);
NL_TEST_ASSERT(aSuite, !(tempTestEFS == testEFS1));
NL_TEST_ASSERT(aSuite, !(tempTestEFS == testEFS3));
tempTestEFS = testEFS3;
NL_TEST_ASSERT(aSuite, tempTestEFS == testEFS3);
NL_TEST_ASSERT(aSuite, !(tempTestEFS == testEFS1));
NL_TEST_ASSERT(aSuite, !(tempTestEFS == testEFS2));
// test clear multipler efs struct
tempTestEFS.Clear();
NL_TEST_ASSERT(aSuite, tempTestEFS.IsEmpty());
NL_TEST_ASSERT(aSuite, 0 == tempTestEFS.GetFieldSetCount());
// Test insert
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->InsertFieldSet(EFS1));
NL_TEST_ASSERT(aSuite, 1 == EFS->GetFieldSetCount());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->InsertFieldSet(EFS2));
NL_TEST_ASSERT(aSuite, 2 == EFS->GetFieldSetCount());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->InsertFieldSet(EFS3));
NL_TEST_ASSERT(aSuite, 3 == EFS->GetFieldSetCount());
// Test get
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 0));
NL_TEST_ASSERT(aSuite, tempEFS == EFS1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 1));
NL_TEST_ASSERT(aSuite, tempEFS == EFS2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 2));
NL_TEST_ASSERT(aSuite, tempEFS == EFS3);
}
void TestSerializeDerializeExtensionFieldSet(nlTestSuite * aSuite, void * aContext)
{
scenes::ExtensionFieldSetsImpl * EFS = &sEFSets;
scenes::ExtensionFieldSetsImpl testSceneEFS;
scenes::ExtensionFieldSet tempEFS;
uint8_t EFS1Buffer[scenes::kMaxFieldBytesPerCluster] = { 0 };
uint8_t EFS2Buffer[scenes::kMaxFieldBytesPerCluster] = { 0 };
uint8_t EFS3Buffer[scenes::kMaxFieldBytesPerCluster] = { 0 };
uint8_t sceneEFSBuffer[kPersistentSceneBufferMax] = { 0 };
uint32_t EFS1_serialized_length = 0;
uint32_t EFS2_serialized_length = 0;
uint32_t EFS3_serialized_length = 0;
uint32_t sceneEFS_serialized_length = 0;
TLV::TLVReader reader;
TLV::TLVWriter writer;
TLV::TLVType outer;
TLV::TLVType outerRead;
// Individual Field Sets serialize / deserialize
writer.Init(EFS1Buffer);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS1.Serialize(writer));
EFS1_serialized_length = writer.GetLengthWritten();
NL_TEST_ASSERT(aSuite, EFS1_serialized_length <= scenes::kMaxFieldBytesPerCluster);
writer.Init(EFS2Buffer);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS2.Serialize(writer));
EFS2_serialized_length = writer.GetLengthWritten();
NL_TEST_ASSERT(aSuite, EFS2_serialized_length <= scenes::kMaxFieldBytesPerCluster);
writer.Init(EFS3Buffer);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS3.Serialize(writer));
EFS3_serialized_length = writer.GetLengthWritten();
NL_TEST_ASSERT(aSuite, EFS3_serialized_length <= scenes::kMaxFieldBytesPerCluster);
reader.Init(EFS1Buffer);
reader.Next(TLV::AnonymousTag());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == tempEFS.Deserialize(reader));
NL_TEST_ASSERT(aSuite, EFS1 == tempEFS);
reader.Init(EFS2Buffer);
reader.Next(TLV::AnonymousTag());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == tempEFS.Deserialize(reader));
NL_TEST_ASSERT(aSuite, EFS2 == tempEFS);
reader.Init(EFS3Buffer);
reader.Next(TLV::AnonymousTag());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == tempEFS.Deserialize(reader));
NL_TEST_ASSERT(aSuite, EFS3 == tempEFS);
// All ExtensionFieldSets serialize / deserialize
writer.Init(sceneEFSBuffer);
writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->Serialize(writer, TLV::ContextTag(TagTestEFS::kEFS)));
writer.EndContainer(outer);
sceneEFS_serialized_length = writer.GetLengthWritten();
NL_TEST_ASSERT(aSuite, sceneEFS_serialized_length <= kPersistentSceneBufferMax);
reader.Init(sceneEFSBuffer);
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 == testSceneEFS.Deserialize(reader, TLV::ContextTag(TagTestEFS::kEFS)));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.ExitContainer(outerRead));
NL_TEST_ASSERT(aSuite, *EFS == testSceneEFS);
}
void TestRemoveExtensionFieldSet(nlTestSuite * aSuite, void * aContext)
{
scenes::ExtensionFieldSetsImpl * EFS = &sEFSets;
scenes::ExtensionFieldSet tempEFS;
// Order in EFS at this point: [EFS1, EFS2, EFS3]
// Removal at beginning
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->RemoveFieldAtPosition(0));
NL_TEST_ASSERT(aSuite, 2 == EFS->GetFieldSetCount());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->InsertFieldSet(EFS1));
NL_TEST_ASSERT(aSuite, 3 == EFS->GetFieldSetCount());
// Verify order
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 0));
NL_TEST_ASSERT(aSuite, tempEFS == EFS2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 1));
NL_TEST_ASSERT(aSuite, tempEFS == EFS3);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 2));
NL_TEST_ASSERT(aSuite, tempEFS == EFS1);
// Order in EFS at this point: [EFS2, EFS3, EFS1]
// Removal at middle
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->RemoveFieldAtPosition(1));
NL_TEST_ASSERT(aSuite, 2 == EFS->GetFieldSetCount());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->InsertFieldSet(EFS3));
NL_TEST_ASSERT(aSuite, 3 == EFS->GetFieldSetCount());
// Verify order
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 0));
NL_TEST_ASSERT(aSuite, tempEFS == EFS2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 1));
NL_TEST_ASSERT(aSuite, tempEFS == EFS1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 2));
NL_TEST_ASSERT(aSuite, tempEFS == EFS3);
// Order in EFS at this point: [EFS2, EFS1, EFS3]
// Removal at end
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->RemoveFieldAtPosition(2));
NL_TEST_ASSERT(aSuite, 2 == EFS->GetFieldSetCount());
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->InsertFieldSet(EFS3));
NL_TEST_ASSERT(aSuite, 3 == EFS->GetFieldSetCount());
// Verify order
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 0));
NL_TEST_ASSERT(aSuite, tempEFS == EFS2);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 1));
NL_TEST_ASSERT(aSuite, tempEFS == EFS1);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == EFS->GetFieldSetAtPosition(tempEFS, 2));
NL_TEST_ASSERT(aSuite, tempEFS == EFS3);
// Emptying the table
EFS->Clear();
NL_TEST_ASSERT(aSuite, true == EFS->IsEmpty());
}
} // namespace TestEFS
/**
* Tear down the test suite.
*/
int TestSetup(void * inContext)
{
VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE);
return SUCCESS;
}
namespace {
/**
* Setup the test suite.
*/
int TestTeardown(void * inContext)
{
chip::Platform::MemoryShutdown();
return SUCCESS;
}
} // namespace
int TestExtensionFieldSets()
{
static nlTest sTests[] = { NL_TEST_DEF("TestInsertExtensionFieldSet", TestEFS::TestInsertExtensionFieldSet),
NL_TEST_DEF("TestSerializeDerializeExtensionFieldSet",
TestEFS::TestSerializeDerializeExtensionFieldSet),
NL_TEST_DEF("TestRemoveExtensionFieldSet", TestEFS::TestRemoveExtensionFieldSet),
NL_TEST_SENTINEL() };
nlTestSuite theSuite = {
"SceneTable",
&sTests[0],
TestSetup,
TestTeardown,
};
nlTestRunner(&theSuite, nullptr);
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestExtensionFieldSets)