blob: 3d7f5bfd663e18f165ac20a08be615c5acb1f5cb [file] [log] [blame]
/*
* 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 <app/SimpleSubscriptionResumptionStorage.h>
#include <lib/core/StringBuilderAdapters.h>
#include <lib/support/DefaultStorageKeyAllocator.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <pw_unit_test/framework.h>
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<chip::SubscriptionId>(i);
subscriptionStorage.Save(subscriptionInfo);
}
// Make sure iterator counts correctly
auto * iterator = subscriptionStorage.IterateSubscriptions();
EXPECT_EQ(iterator->Count(), std::make_unsigned_t<int>(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<int>(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<uint8_t> 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<uint16_t>(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<uint8_t> 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<uint16_t>(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<uint8_t> 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<uint8_t> additionalBytes;
additionalBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize());
ASSERT_NE(additionalBytes.Get(), nullptr);
EXPECT_EQ(writer.PutBytes(kTestValue1Tag, additionalBytes.Get(),
static_cast<uint32_t>(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<uint16_t>(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<uint8_t> junkBytes;
junkBytes.Calloc(subscriptionStorage.TestMaxSubscriptionSize() / 2);
ASSERT_NE(junkBytes.Get(), nullptr);
EXPECT_EQ(storage.SyncSetKeyValue(chip::DefaultStorageKeyAllocator::SubscriptionResumption(0).KeyName(), junkBytes.Get(),
static_cast<uint16_t>(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();
}