From 2209650c767349870516f1c835eb4fa46437c906 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Tue, 24 Jan 2023 19:39:22 -0500 Subject: [PATCH] Added verification for SceneName in logging and applied correction to scene implementation --- src/app/clusters/scenes/SceneTable.h | 48 ++- src/app/clusters/scenes/SceneTableImpl.cpp | 27 +- src/app/clusters/scenes/SceneTableImpl.h | 17 +- src/app/tests/BUILD.gn | 18 ++ src/app/tests/TestSceneTable.cpp | 333 +++++++++++++++++++++ src/lib/support/CommonPersistentData.h | 1 + src/lib/support/TestSceneTable.h | 9 +- 7 files changed, 428 insertions(+), 25 deletions(-) create mode 100644 src/app/tests/TestSceneTable.cpp diff --git a/src/app/clusters/scenes/SceneTable.h b/src/app/clusters/scenes/SceneTable.h index baaa41e9f1dae8..7121cea2abbdc1 100644 --- a/src/app/clusters/scenes/SceneTable.h +++ b/src/app/clusters/scenes/SceneTable.h @@ -20,6 +20,7 @@ #include #include #include +#include namespace chip { namespace scenes { @@ -111,13 +112,12 @@ class SceneTable ExtensionFieldsSets extentsionFieldsSets; TransitionTime100ms transitionTime100 = 0; - SceneData() = default; - SceneData(const char * sceneName, SceneTransitionTime time = 0, TransitionTime100ms time100ms = 0) : + SceneData(const char * sceneName = nullptr, SceneTransitionTime time = 0, TransitionTime100ms time100ms = 0) : sceneTransitionTime(time), transitionTime100(time100ms) { SetName(sceneName); } - SceneData(const char * sceneName, ExtensionFieldsSets fields, SceneTransitionTime time = 0, + SceneData(ExtensionFieldsSets fields, const char * sceneName = nullptr, SceneTransitionTime time = 0, TransitionTime100ms time100ms = 0) : sceneTransitionTime(time), transitionTime100(time100ms) @@ -136,8 +136,13 @@ class SceneTable TLV::TLVType container; ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(1), TLV::kTLVType_Structure, container)); - size_t name_size = strnlen(this->name, kSceneNameMax); - ReturnErrorOnFailure(writer.PutString(TagSceneName(), this->name, static_cast(name_size))); + // assumes a 0 size means the name wasn't used so it doesn't get stored + if (this->name[0] != 0) + { + size_t name_size = strnlen(this->name, kSceneNameMax); + ReturnErrorOnFailure(writer.PutString(TagSceneName(), this->name, static_cast(name_size))); + } + ReturnErrorOnFailure(writer.Put(TagSceneTransitionTime(), static_cast(this->sceneTransitionTime))); ReturnErrorOnFailure(writer.Put(TagSceneTransitionTime100(), static_cast(this->transitionTime100))); ReturnErrorOnFailure(this->extentsionFieldsSets.Serialize(writer)); @@ -150,10 +155,20 @@ class SceneTable ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::ContextTag(1))); ReturnErrorOnFailure(reader.EnterContainer(container)); - ReturnErrorOnFailure(reader.Next(TagSceneName())); - ReturnErrorOnFailure(reader.GetString(this->name, sizeof(this->name))); - size_t name_size = strnlen(this->name, kSceneNameMax); // TODO : verify use of strnlen is ok - this->name[name_size] = 0; // Putting a null terminator + ReturnErrorOnFailure(reader.Next()); + TLV::Tag currTag = reader.GetTag(); + VerifyOrReturnError(TagSceneName() == currTag || TagSceneTransitionTime() == currTag, CHIP_ERROR_WRONG_TLV_TYPE); + + // If there was no error, a name is expected from the storage, if there was an unexpectec TLV element, + if (currTag == TagSceneName()) + { + size_t name_size = reader.GetLength(); + VerifyOrReturnError(name_size <= (kSceneNameMax - 1), CHIP_ERROR_BUFFER_TOO_SMALL); + ReturnErrorOnFailure(reader.GetString(this->name, name_size)); + this->name[name_size] = 0; + } + + // Putting a null terminator ReturnErrorOnFailure(reader.Next(TagSceneTransitionTime())); ReturnErrorOnFailure(reader.Get(this->sceneTransitionTime)); ReturnErrorOnFailure(reader.Next(TagSceneTransitionTime100())); @@ -212,7 +227,10 @@ class SceneTable SceneTableEntry(SceneStorageId id) : storageId(id) {} SceneTableEntry(const SceneStorageId id, const SceneData data) : storageId(id), storageData(data) {} - bool operator==(const SceneTableEntry & other) { return (this->storageId == other.storageId); } + bool operator==(const SceneTableEntry & other) + { + return (this->storageId == other.storageId && this->storageData == other.storageData); + } void operator=(const SceneTableEntry & other) { @@ -229,8 +247,8 @@ class SceneTable SceneTable(const SceneTable &) = delete; SceneTable & operator=(const SceneTable &) = delete; - virtual CHIP_ERROR Init() = 0; - virtual void Finish() = 0; + virtual CHIP_ERROR Init(PersistentStorageDelegate * storage) = 0; + virtual void Finish() = 0; // Data virtual CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry) = 0; @@ -241,6 +259,12 @@ class SceneTable using SceneEntryIterator = CommonIterator; virtual SceneEntryIterator * IterateSceneEntry(FabricIndex fabric_index) = 0; + + // Fabrics + virtual CHIP_ERROR RemoveFabric(FabricIndex fabric_index) = 0; + +protected: + const uint8_t mMaxScenePerFabric = kMaxScenePerFabric; }; } // namespace scenes diff --git a/src/app/clusters/scenes/SceneTableImpl.cpp b/src/app/clusters/scenes/SceneTableImpl.cpp index 552efb232fc1ba..0ef66965c42c0f 100644 --- a/src/app/clusters/scenes/SceneTableImpl.cpp +++ b/src/app/clusters/scenes/SceneTableImpl.cpp @@ -16,7 +16,6 @@ */ #include -#include #include #include @@ -401,6 +400,32 @@ CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntry(FabricIndex fabric_index return CHIP_NO_ERROR; } +CHIP_ERROR DefaultSceneTableImpl::RemoveFabric(FabricIndex fabric_index) +{ + FabricSceneData fabric(fabric_index); + + CHIP_ERROR err = fabric.Load(mStorage); + VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err); + + // Remove scene entries + SceneTableData scene(fabric_index, fabric.first_scene); + size_t scene_count = 0; + + while (scene_count < fabric.scene_count) + { + if (CHIP_NO_ERROR != scene.Load(mStorage)) + { + break; + } + RemoveSceneTableEntry(fabric_index, scene.storageId); + scene.storageId = scene.next; + scene_count++; + } + + // Remove fabric + return fabric.Delete(mStorage); +} + DefaultSceneTableImpl::SceneEntryIterator * DefaultSceneTableImpl::IterateSceneEntry(FabricIndex fabric_index) { VerifyOrReturnError(IsInitialized(), nullptr); diff --git a/src/app/clusters/scenes/SceneTableImpl.h b/src/app/clusters/scenes/SceneTableImpl.h index 5a8988db72de93..f191b19d243d08 100644 --- a/src/app/clusters/scenes/SceneTableImpl.h +++ b/src/app/clusters/scenes/SceneTableImpl.h @@ -40,22 +40,25 @@ class DefaultSceneTableImpl : public SceneTable ~DefaultSceneTableImpl() override {} - CHIP_ERROR Init(PersistentStorageDelegate * storage); - void Finish(); + CHIP_ERROR Init(PersistentStorageDelegate * storage) override; + void Finish() override; // // Scene Data // // By id - CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry); + CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry) override; CHIP_ERROR GetSceneTableEntry(FabricIndex fabric_index, DefaultSceneTableImpl::SceneStorageId scene_id, - SceneTableEntry & entry); - CHIP_ERROR RemoveSceneTableEntry(FabricIndex fabric_index, DefaultSceneTableImpl::SceneStorageId scene_id); + SceneTableEntry & entry) override; + CHIP_ERROR RemoveSceneTableEntry(FabricIndex fabric_index, DefaultSceneTableImpl::SceneStorageId scene_id) override; // Iterators SceneEntryIterator * IterateSceneEntry(FabricIndex fabric_index) override; + // Fabrics + CHIP_ERROR RemoveFabric(FabricIndex fabric_index) override; + protected: class SceneEntryIteratorImpl : public SceneEntryIterator { @@ -76,8 +79,6 @@ class DefaultSceneTableImpl : public SceneTable chip::PersistentStorageDelegate * mStorage = nullptr; ObjectPool mSceneEntryIterators; - - const uint8_t mMaxScenePerFabric = kMaxScenePerFabric; }; // class DefaultSceneTableImpl /** @@ -96,7 +97,7 @@ DefaultSceneTableImpl * GetSceneTable(); * * The `provider` can be set to nullptr if the owner is done with it fully. * - * @param[in] provider pointer to the Scene Table global isntance to use + * @param[in] provider pointer to the Scene Table global instance to use */ void SetSceneTable(DefaultSceneTableImpl * provider); } // namespace scenes diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 8e291bb904feb0..632d01333bc281 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -79,6 +79,22 @@ source_set("ota-requestor-test-srcs") { ] } +source_set("scenes-table-test-srcs") { + sources = [ + "${chip_root}/src/app/clusters/scenes/ExtensionFieldsSets.cpp", + "${chip_root}/src/app/clusters/scenes/ExtensionFieldsSets.h", + "${chip_root}/src/app/clusters/scenes/SceneTable.h", + "${chip_root}/src/app/clusters/scenes/SceneTableImpl.cpp", + "${chip_root}/src/app/clusters/scenes/SceneTableImpl.h", + "${chip_root}/src/app/clusters/scenes/scenes-tokens.h", + ] + + public_deps = [ + "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/lib/core", + ] +} + chip_test_suite("tests") { output_name = "libAppTests" @@ -105,6 +121,7 @@ chip_test_suite("tests") { "TestPendingNotificationMap.cpp", "TestReadInteraction.cpp", "TestReportingEngine.cpp", + "TestSceneTable.cpp", "TestStatusIB.cpp", "TestStatusResponseMessage.cpp", "TestTimedHandler.cpp", @@ -140,6 +157,7 @@ chip_test_suite("tests") { ":binding-test-srcs", ":client-monitoring-test-srcs", ":ota-requestor-test-srcs", + ":scenes-table-test-srcs", "${chip_root}/src/app", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/tests:helpers", diff --git a/src/app/tests/TestSceneTable.cpp b/src/app/tests/TestSceneTable.cpp new file mode 100644 index 00000000000000..383ff35e4e5675 --- /dev/null +++ b/src/app/tests/TestSceneTable.cpp @@ -0,0 +1,333 @@ +/* + * + * 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 +#include +#include +#include + +using FabricIndex = chip::FabricIndex; +using SceneTableEntry = chip::scenes::DefaultSceneTableImpl::SceneTableEntry; +using SceneTableImpl = chip::scenes::DefaultSceneTableImpl; +using SceneStorageId = chip::scenes::DefaultSceneTableImpl::SceneStorageId; +using SceneData = chip::scenes::DefaultSceneTableImpl::SceneData; + +namespace { + +static chip::TestPersistentStorageDelegate testStorage; +static SceneTableImpl sSceneTable; + +// 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(1, 0xAA, 0x101); +static const SceneStorageId sceneId2(1, 0xBB, 0x00); +static const SceneStorageId sceneId3(1, 0xCC, 0x102); +static const SceneStorageId sceneId4(1, 0xBE, 0x00); +static const SceneStorageId sceneId5(1, 0x45, 0x103); +static const SceneStorageId sceneId6(1, 0x65, 0x00); +static const SceneStorageId sceneId7(1, 0x77, 0x101); +static const SceneStorageId sceneId8(1, 0xEE, 0x101); +static const SceneStorageId sceneId9(1, 0xAB, 0x101); + +// Scene data +static const SceneData sceneData1("Scene #1"); +static const SceneData sceneData2("Scene #2", 2, 5); +static const SceneData sceneData3("Scene #3", 25); +static const SceneData sceneData4("Scene num4", 5); +static const SceneData sceneData5(nullptr); +static const SceneData sceneData6("Scene #6", 3, 15); +static const SceneData sceneData7("Scene #7", 20, 5); +static const SceneData sceneData8("Scene num8", 10); +static const SceneData sceneData9("NAME TOO LOOONNG", 30, 15); +static const SceneData sceneData10("Scene #10", 10, 1); +static const SceneData sceneData11("Scene #11", 20, 10); +static const SceneData sceneData12("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); + +void ResetSceneTable(SceneTableImpl * sceneTable) +{ + sceneTable->RemoveFabric(kFabric1); + sceneTable->RemoveFabric(kFabric2); +} + +void TestStoreScenes(nlTestSuite * aSuite, void * aContext) +{ + SceneTableImpl * sceneTable = chip::scenes::GetSceneTable(); + NL_TEST_ASSERT(aSuite, sceneTable); + + // Reset test + ResetSceneTable(sceneTable); + + 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->SetSceneTableEntry(kFabric1, scene9)); + + 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(kFabric1, sceneId4, scene)); + NL_TEST_ASSERT(aSuite, scene == scene4); + 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); + + 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.storageId)); + auto * iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 7); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene1); + iterator->Release(); + + // Remove first + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene1.storageId)); + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 6); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene2); + + // Remove Next + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene3.storageId)); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene4); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene2.storageId)); + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 4); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene4); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene4.storageId)); + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 3); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene6); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene6.storageId)); + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 2); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene7); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene7.storageId)); + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 2); + NL_TEST_ASSERT(aSuite, iterator->Next(scene)); + NL_TEST_ASSERT(aSuite, scene == scene8); + + // Remove last + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, scene8.storageId)); + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 0); + NL_TEST_ASSERT(aSuite, iterator->Next(scene) == false); + + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveSceneTableEntry(kFabric1, scene8.storageId)); + + iterator = sceneTable->IterateSceneEntry(kFabric1); + NL_TEST_ASSERT(aSuite, iterator->Count() == 0); + auto * iterator = sceneTable->IterateSceneEntry(kFabric1); +} + +void TestFabricScenes(nlTestSuite * aSuite, void * aContext) +{ + SceneTableImpl * sceneTable = chip::scenes::GetSceneTable(); + NL_TEST_ASSERT(aSuite, sceneTable); + + // Reset test + ResetSceneTable(sceneTable); + + SceneTableEntry scene; + + 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(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)); + + // Confirm 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 1 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 + +int TestSetup(void * inContext) +{ + VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE); + VerifyOrReturnError(CHIP_NO_ERROR == sSceneTable.Init(&testStorage), FAILURE); + + SetSceneTable(&sSceneTable); +} + +int TestSceneTable() +{ + static nlTest sTests[] = { + NL_TEST_DEF("TestFabricScenes", TestFabricScenes), NL_TEST_DEF("TestStoreScenes", TestStoreScenes), + NL_TEST_DEF("TestOverwriteScenes", TestOverwriteScenes), NL_TEST_DEF("TestIterateScenes", TestIterateScenes), + NL_TEST_DEF("TestRemoveScenes", TestRemoveScenes), NL_TEST_SENTINEL() + }; + + nlTestSuite theSuite = { + "SceneTable", + &sTests[0], + nullptr, + nullptr, + }; + nlTestRunner(&theSuite, nullptr); + return (nlTestRunnerStats(&theSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestSceneTable) \ No newline at end of file diff --git a/src/lib/support/CommonPersistentData.h b/src/lib/support/CommonPersistentData.h index f04ba591959c4e..df0acff14616e4 100644 --- a/src/lib/support/CommonPersistentData.h +++ b/src/lib/support/CommonPersistentData.h @@ -19,6 +19,7 @@ * @file * Contains a class handling creation of linked list of stored persistent data. */ +#pragma once #include #include diff --git a/src/lib/support/TestSceneTable.h b/src/lib/support/TestSceneTable.h index 0a38834dc35c28..fe67352c4fe068 100644 --- a/src/lib/support/TestSceneTable.h +++ b/src/lib/support/TestSceneTable.h @@ -25,10 +25,10 @@ namespace chip { namespace SceneTesting { using FabricIndex = chip::FabricIndex; -using SceneTableEntry = chip::scenes::SceneTableImpl::SceneTableEntry; -using SceneTableImpl = chip::scenes::SceneTableImpl; -using SceneStorageId = chip::scenes::SceneTableImpl::SceneStorageId; -using SceneData = chip::scenes::SceneTableImpl::SceneData; +using SceneTableEntry = chip::scenes::DefaultSceneTableImpl::SceneTableEntry; +using SceneTableImpl = chip::scenes::DefaultSceneTableImpl; +using SceneStorageId = chip::scenes::DefaultSceneTableImpl::SceneStorageId; +using SceneData = chip::scenes::DefaultSceneTableImpl::SceneData; CHIP_ERROR scene_store_test(SceneTableImpl * provider, FabricIndex fabric_index, SceneTableEntry & entry) { @@ -98,6 +98,7 @@ CHIP_ERROR scene_remove_test(SceneTableImpl * provider, FabricIndex fabric_index // Iterator should return false here VerifyOrReturnError(iterator->Next(temp) == false, CHIP_ERROR_INVALID_ACCESS_TOKEN); + iterator->Release(); return err; }