/* * Copyright (c) 2021 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 #include class TestSimpleSubscriptionResumptionStorage : public ::testing::Test { public: static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } }; class SimpleSubscriptionResumptionStorageTest : public chip::app::SimpleSubscriptionResumptionStorage { public: CHIP_ERROR TestSave(chip::TLV::TLVWriter & writer, chip::app::SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) { return Save(writer, subscriptionInfo); } static constexpr size_t TestMaxSubscriptionSize() { return MaxSubscriptionSize(); } }; struct TestSubscriptionInfo : public chip::app::SubscriptionResumptionStorage::SubscriptionInfo { bool operator==(const SubscriptionInfo & that) const { if ((mNodeId != that.mNodeId) || (mFabricIndex != that.mFabricIndex) || (mSubscriptionId != that.mSubscriptionId) || (mMinInterval != that.mMinInterval) || (mMaxInterval != that.mMaxInterval) || (mFabricFiltered != that.mFabricFiltered)) { return false; } if ((mAttributePaths.AllocatedSize() != that.mAttributePaths.AllocatedSize()) || (mEventPaths.AllocatedSize() != that.mEventPaths.AllocatedSize())) { return false; } for (size_t i = 0; i < mAttributePaths.AllocatedSize(); i++) { if ((mAttributePaths[i].mEndpointId != that.mAttributePaths[i].mEndpointId) || (mAttributePaths[i].mClusterId != that.mAttributePaths[i].mClusterId) || (mAttributePaths[i].mAttributeId != that.mAttributePaths[i].mAttributeId)) { return false; } } for (size_t i = 0; i < mEventPaths.AllocatedSize(); i++) { if ((mEventPaths[i].mEndpointId != that.mEventPaths[i].mEndpointId) || (mEventPaths[i].mClusterId != that.mEventPaths[i].mClusterId) || (mEventPaths[i].mEventId != that.mEventPaths[i].mEventId) || (mEventPaths[i].mIsUrgentEvent != that.mEventPaths[i].mIsUrgentEvent)) { return false; } } return true; } }; TEST_F(TestSimpleSubscriptionResumptionStorage, TestSubscriptionCount) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); // Write some subscriptions and see the counts are correct chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo = { .mNodeId = 6666, .mFabricIndex = 46 }; for (size_t i = 0; i < (CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2); i++) { subscriptionInfo.mSubscriptionId = static_cast(i); subscriptionStorage.Save(subscriptionInfo); } // Make sure iterator counts correctly auto * iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), std::make_unsigned_t(CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2)); // Verify subscriptions manually count correctly size_t count = 0; while (iterator->Next(subscriptionInfo)) { count++; } iterator->Release(); EXPECT_EQ(count, std::make_unsigned_t(CHIP_IM_MAX_NUM_SUBSCRIPTIONS / 2)); // Delete all and verify iterator counts 0 EXPECT_EQ(subscriptionStorage.DeleteAll(46), CHIP_NO_ERROR); iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 0u); // Verify subscriptions manually count correctly count = 0; while (iterator->Next(subscriptionInfo)) { count++; } iterator->Release(); EXPECT_EQ(count, 0u); } TEST_F(TestSimpleSubscriptionResumptionStorage, TestSubscriptionMaxCount) { // Force large MacCount value and check that Init resets it properly, and deletes extra subs: chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; // First set a large MaxCount before Init uint16_t countMaxToSave = 2 * CHIP_IM_MAX_NUM_SUBSCRIPTIONS; EXPECT_EQ(storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMaxToSave, sizeof(uint16_t)), CHIP_NO_ERROR); // Then write something beyond CHIP_IM_MAX_NUM_SUBSCRIPTIONS chip::Platform::ScopedMemoryBuffer junkBytes; junkBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize() / 2); ASSERT_NE(junkBytes.Get(), nullptr); EXPECT_EQ(storage.SyncSetKeyValue( chip::DefaultStorageKeyAllocator::SubscriptionResumption(CHIP_IM_MAX_NUM_SUBSCRIPTIONS + 1).KeyName(), junkBytes.Get(), static_cast(subscriptionStorage.TestMaxSubscriptionSize() / 2)), CHIP_NO_ERROR); subscriptionStorage.Init(&storage); // First check the MaxCount is reset to CHIP_IM_MAX_NUM_SUBSCRIPTIONS uint16_t countMax = 0; uint16_t len = sizeof(countMax); EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len), CHIP_NO_ERROR); EXPECT_EQ(countMax, CHIP_IM_MAX_NUM_SUBSCRIPTIONS); // Then check the fake sub is no more EXPECT_FALSE(storage.SyncDoesKeyExist( chip::DefaultStorageKeyAllocator::SubscriptionResumption(CHIP_IM_MAX_NUM_SUBSCRIPTIONS + 1).KeyName())); } TEST_F(TestSimpleSubscriptionResumptionStorage, TestSubscriptionState) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { .mNodeId = 1111, .mFabricIndex = 41, .mSubscriptionId = 1, .mMinInterval = 1, .mMaxInterval = 11, .mFabricFiltered = true, }; subscriptionInfo1.mAttributePaths.Calloc(2); subscriptionInfo1.mAttributePaths[0].mEndpointId = 1; subscriptionInfo1.mAttributePaths[0].mClusterId = 1; subscriptionInfo1.mAttributePaths[0].mAttributeId = 1; subscriptionInfo1.mAttributePaths[1].mEndpointId = 2; subscriptionInfo1.mAttributePaths[1].mClusterId = 2; subscriptionInfo1.mAttributePaths[1].mAttributeId = 2; chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo2 = { .mNodeId = 2222, .mFabricIndex = 42, .mSubscriptionId = 2, .mMinInterval = 2, .mMaxInterval = 12, .mFabricFiltered = false, }; subscriptionInfo2.mEventPaths.Calloc(2); subscriptionInfo2.mEventPaths[0].mEndpointId = 3; subscriptionInfo2.mEventPaths[0].mClusterId = 3; subscriptionInfo2.mEventPaths[0].mEventId = 3; subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = false; subscriptionInfo2.mEventPaths[1].mEndpointId = 4; subscriptionInfo2.mEventPaths[1].mClusterId = 4; subscriptionInfo2.mEventPaths[1].mEventId = 4; subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = true; chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo3 = { .mNodeId = 3333, .mFabricIndex = 43, .mSubscriptionId = 3, .mMinInterval = 3, .mMaxInterval = 13, .mFabricFiltered = true, }; subscriptionInfo3.mAttributePaths.Calloc(2); subscriptionInfo3.mAttributePaths[0].mEndpointId = 5; subscriptionInfo3.mAttributePaths[0].mClusterId = 5; subscriptionInfo3.mAttributePaths[0].mAttributeId = 5; subscriptionInfo3.mAttributePaths[1].mEndpointId = 6; subscriptionInfo3.mAttributePaths[1].mClusterId = 6; subscriptionInfo3.mAttributePaths[1].mAttributeId = 6; subscriptionInfo3.mEventPaths.Calloc(2); subscriptionInfo3.mEventPaths[0].mEndpointId = 7; subscriptionInfo3.mEventPaths[0].mClusterId = 7; subscriptionInfo3.mEventPaths[0].mEventId = 7; subscriptionInfo2.mEventPaths[0].mIsUrgentEvent = true; subscriptionInfo3.mEventPaths[1].mEndpointId = 8; subscriptionInfo3.mEventPaths[1].mClusterId = 8; subscriptionInfo3.mEventPaths[1].mEventId = 8; subscriptionInfo2.mEventPaths[1].mIsUrgentEvent = false; EXPECT_EQ(subscriptionStorage.Save(subscriptionInfo1), CHIP_NO_ERROR); EXPECT_EQ(subscriptionStorage.Save(subscriptionInfo2), CHIP_NO_ERROR); EXPECT_EQ(subscriptionStorage.Save(subscriptionInfo3), CHIP_NO_ERROR); auto * iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 3u); // Verify subscriptions manually count correctly TestSubscriptionInfo subscriptionInfo; EXPECT_TRUE(iterator->Next(subscriptionInfo)); EXPECT_EQ(subscriptionInfo, subscriptionInfo1); EXPECT_TRUE(iterator->Next(subscriptionInfo)); EXPECT_EQ(subscriptionInfo, subscriptionInfo2); EXPECT_TRUE(iterator->Next(subscriptionInfo)); EXPECT_EQ(subscriptionInfo, subscriptionInfo3); // Verify at end of list EXPECT_FALSE(iterator->Next(subscriptionInfo)); iterator->Release(); // Delete fabric 1 and subscription 2 and check only 3 remains. subscriptionStorage.Delete(subscriptionInfo1.mNodeId, subscriptionInfo1.mFabricIndex, subscriptionInfo1.mSubscriptionId); subscriptionStorage.DeleteAll(subscriptionInfo2.mFabricIndex); iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 1u); EXPECT_TRUE(iterator->Next(subscriptionInfo)); EXPECT_EQ(subscriptionInfo, subscriptionInfo3); // Verify at end of list EXPECT_FALSE(iterator->Next(subscriptionInfo)); iterator->Release(); // Delete 3 also, and see that both count is 0 and MaxCount is removed from storage subscriptionStorage.DeleteAll(subscriptionInfo3.mFabricIndex); iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 0u); EXPECT_FALSE(iterator->Next(subscriptionInfo)); iterator->Release(); uint16_t countMax = 0; uint16_t len = sizeof(countMax); EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len), CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); } static constexpr chip::TLV::Tag kTestValue1Tag = chip::TLV::ContextTag(30); static constexpr chip::TLV::Tag kTestValue2Tag = chip::TLV::ContextTag(31); TEST_F(TestSimpleSubscriptionResumptionStorage, TestSubscriptionStateUnexpectedFields) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); // Write additional entries at the end of TLV and see it still loads correctly chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { .mNodeId = 4444, .mFabricIndex = 44, .mSubscriptionId = 4, .mMinInterval = 4, .mMaxInterval = 14, .mFabricFiltered = true, }; subscriptionInfo1.mAttributePaths.Calloc(2); subscriptionInfo1.mAttributePaths[0].mEndpointId = 9; subscriptionInfo1.mAttributePaths[0].mClusterId = 9; subscriptionInfo1.mAttributePaths[0].mAttributeId = 9; subscriptionInfo1.mAttributePaths[1].mEndpointId = 10; subscriptionInfo1.mAttributePaths[1].mClusterId = 10; subscriptionInfo1.mAttributePaths[1].mAttributeId = 10; chip::Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(subscriptionStorage.TestMaxSubscriptionSize()); ASSERT_NE(backingBuffer.Get(), nullptr); chip::TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), subscriptionStorage.TestMaxSubscriptionSize()); EXPECT_EQ(subscriptionStorage.TestSave(writer, subscriptionInfo1), CHIP_NO_ERROR); // Additional stuff chip::TLV::TLVType containerType; EXPECT_EQ(writer.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, containerType), CHIP_NO_ERROR); uint32_t value1 = 1; uint32_t value2 = 2; EXPECT_EQ(writer.Put(kTestValue1Tag, value1), CHIP_NO_ERROR); EXPECT_EQ(writer.Put(kTestValue2Tag, value2), CHIP_NO_ERROR); EXPECT_EQ(writer.EndContainer(containerType), CHIP_NO_ERROR); const auto len = writer.GetLengthWritten(); writer.Finalize(backingBuffer); EXPECT_EQ(storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), backingBuffer.Get(), static_cast(len)), CHIP_NO_ERROR); // Now read back and verify auto * iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 1u); TestSubscriptionInfo subscriptionInfo; EXPECT_TRUE(iterator->Next(subscriptionInfo)); EXPECT_EQ(subscriptionInfo, subscriptionInfo1); iterator->Release(); } TEST_F(TestSimpleSubscriptionResumptionStorage, TestSubscriptionStateTooBigToLoad) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); // Write additional too-big data at the end of TLV and see it fails to loads and entry deleted chip::app::SubscriptionResumptionStorage::SubscriptionInfo subscriptionInfo1 = { .mNodeId = 5555, .mFabricIndex = 45, .mSubscriptionId = 5, .mMinInterval = 5, .mMaxInterval = 15, .mFabricFiltered = false, }; subscriptionInfo1.mAttributePaths.Calloc(2); subscriptionInfo1.mAttributePaths[0].mEndpointId = 11; subscriptionInfo1.mAttributePaths[0].mClusterId = 11; subscriptionInfo1.mAttributePaths[0].mAttributeId = 11; subscriptionInfo1.mAttributePaths[1].mEndpointId = 12; subscriptionInfo1.mAttributePaths[1].mClusterId = 12; subscriptionInfo1.mAttributePaths[1].mAttributeId = 12; chip::Platform::ScopedMemoryBuffer backingBuffer; backingBuffer.Calloc(subscriptionStorage.TestMaxSubscriptionSize() * 2); ASSERT_NE(backingBuffer.Get(), nullptr); chip::TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), subscriptionStorage.TestMaxSubscriptionSize() * 2); EXPECT_EQ(subscriptionStorage.TestSave(writer, subscriptionInfo1), CHIP_NO_ERROR); // Additional too-many bytes chip::TLV::TLVType containerType; EXPECT_EQ(writer.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, containerType), CHIP_NO_ERROR); // Write MaxSubscriptionSize() to guarantee Load failure chip::Platform::ScopedMemoryBuffer additionalBytes; additionalBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize()); ASSERT_NE(additionalBytes.Get(), nullptr); EXPECT_EQ(writer.PutBytes(kTestValue1Tag, additionalBytes.Get(), static_cast(subscriptionStorage.TestMaxSubscriptionSize())), CHIP_NO_ERROR); EXPECT_EQ(writer.EndContainer(containerType), CHIP_NO_ERROR); const auto len = writer.GetLengthWritten(); writer.Finalize(backingBuffer); EXPECT_EQ(storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), backingBuffer.Get(), static_cast(len)), CHIP_NO_ERROR); // Now read back and verify auto * iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 1u); TestSubscriptionInfo subscriptionInfo; EXPECT_FALSE(iterator->Next(subscriptionInfo)); EXPECT_EQ(iterator->Count(), 0u); iterator->Release(); } TEST_F(TestSimpleSubscriptionResumptionStorage, TestSubscriptionStateJunkData) { chip::TestPersistentStorageDelegate storage; SimpleSubscriptionResumptionStorageTest subscriptionStorage; subscriptionStorage.Init(&storage); chip::Platform::ScopedMemoryBuffer junkBytes; junkBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize() / 2); ASSERT_NE(junkBytes.Get(), nullptr); EXPECT_EQ(storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), junkBytes.Get(), static_cast(subscriptionStorage.TestMaxSubscriptionSize() / 2)), CHIP_NO_ERROR); // Now read back and verify auto * iterator = subscriptionStorage.IterateSubscriptions(); EXPECT_EQ(iterator->Count(), 1u); TestSubscriptionInfo subscriptionInfo; EXPECT_FALSE(iterator->Next(subscriptionInfo)); EXPECT_EQ(iterator->Count(), 0u); iterator->Release(); }