blob: db95408813a0c1699ec0a2f2e631e25e4a5a5610 [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* 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/util/binding-table.h>
#include <app/util/config.h>
#include <lib/support/DefaultStorageKeyAllocator.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/UnitTestRegistration.h>
#include <nlunit-test.h>
using chip::BindingTable;
namespace {
void TestEmptyBindingTable(nlTestSuite * aSuite, void * aContext)
{
BindingTable table;
chip::TestPersistentStorageDelegate testStorage;
table.SetPersistentStorage(&testStorage);
NL_TEST_ASSERT(aSuite, table.Size() == 0);
NL_TEST_ASSERT(aSuite, table.begin() == table.end());
}
void TestAdd(nlTestSuite * aSuite, void * aContext)
{
BindingTable table;
chip::TestPersistentStorageDelegate testStorage;
table.SetPersistentStorage(&testStorage);
EmberBindingTableEntry unusedEntry;
unusedEntry.type = MATTER_UNUSED_BINDING;
NL_TEST_ASSERT(aSuite, table.Add(unusedEntry) == CHIP_ERROR_INVALID_ARGUMENT);
for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++)
{
NL_TEST_ASSERT(aSuite, table.Add(EmberBindingTableEntry::ForNode(0, i, 0, 0, std::nullopt)) == CHIP_NO_ERROR);
}
NL_TEST_ASSERT(aSuite, table.Add(EmberBindingTableEntry::ForNode(0, 0, 0, 0, std::nullopt)) == CHIP_ERROR_NO_MEMORY);
NL_TEST_ASSERT(aSuite, table.Size() == MATTER_BINDING_TABLE_SIZE);
auto iter = table.begin();
for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++)
{
NL_TEST_ASSERT(aSuite, iter != table.end());
NL_TEST_ASSERT(aSuite, iter->nodeId == i);
NL_TEST_ASSERT(aSuite, iter.GetIndex() == i);
++iter;
}
NL_TEST_ASSERT(aSuite, iter == table.end());
}
void TestRemoveThenAdd(nlTestSuite * aSuite, void * aContext)
{
BindingTable table;
chip::TestPersistentStorageDelegate testStorage;
table.SetPersistentStorage(&testStorage);
NL_TEST_ASSERT(aSuite, table.Add(EmberBindingTableEntry::ForNode(0, 0, 0, 0, std::nullopt)) == CHIP_NO_ERROR);
auto iter = table.begin();
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, iter == table.end());
NL_TEST_ASSERT(aSuite, table.Size() == 0);
NL_TEST_ASSERT(aSuite, table.begin() == table.end());
for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++)
{
NL_TEST_ASSERT(aSuite, table.Add(EmberBindingTableEntry::ForNode(0, i, 0, 0, std::nullopt)) == CHIP_NO_ERROR);
}
iter = table.begin();
++iter;
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, table.Size() == MATTER_BINDING_TABLE_SIZE - 1);
NL_TEST_ASSERT(aSuite, iter->nodeId == 2);
NL_TEST_ASSERT(aSuite, iter.GetIndex() == 2);
auto iterCheck = table.begin();
++iterCheck;
NL_TEST_ASSERT(aSuite, iter == iterCheck);
NL_TEST_ASSERT(aSuite, table.Add(EmberBindingTableEntry::ForNode(0, 1, 0, 0, std::nullopt)) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, table.Size() == MATTER_BINDING_TABLE_SIZE);
iter = table.begin();
for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE - 1; i++)
{
++iter;
}
NL_TEST_ASSERT(aSuite, iter->nodeId == 1);
NL_TEST_ASSERT(aSuite, iter.GetIndex() == 1);
++iter;
NL_TEST_ASSERT(aSuite, iter == table.end());
iter = table.begin();
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, table.Size() == MATTER_BINDING_TABLE_SIZE - 1);
NL_TEST_ASSERT(aSuite, iter == table.begin());
NL_TEST_ASSERT(aSuite, iter.GetIndex() == 2);
NL_TEST_ASSERT(aSuite, iter->nodeId == 2);
NL_TEST_ASSERT(aSuite, table.GetAt(0).type == MATTER_UNUSED_BINDING);
}
void VerifyTableSame(nlTestSuite * aSuite, BindingTable & table, const std::vector<EmberBindingTableEntry> & expected)
{
NL_TEST_ASSERT(aSuite, table.Size() == expected.size());
auto iter1 = table.begin();
auto iter2 = expected.begin();
while (iter2 != expected.end())
{
NL_TEST_ASSERT(aSuite, iter1 != table.end());
NL_TEST_ASSERT(aSuite, *iter1 == *iter2);
++iter1;
++iter2;
}
NL_TEST_ASSERT(aSuite, iter1 == table.end());
}
void VerifyRestored(nlTestSuite * aSuite, chip::TestPersistentStorageDelegate & storage,
const std::vector<EmberBindingTableEntry> & expected)
{
BindingTable restoredTable;
restoredTable.SetPersistentStorage(&storage);
NL_TEST_ASSERT(aSuite, restoredTable.LoadFromStorage() == CHIP_NO_ERROR);
VerifyTableSame(aSuite, restoredTable, expected);
}
void TestPersistentStorage(nlTestSuite * aSuite, void * aContext)
{
chip::TestPersistentStorageDelegate testStorage;
BindingTable table;
chip::Optional<chip::ClusterId> cluster = chip::MakeOptional<chip::ClusterId>(static_cast<chip::ClusterId>(UINT16_MAX + 6));
std::vector<EmberBindingTableEntry> expected = {
EmberBindingTableEntry::ForNode(0, 0, 0, 0, std::nullopt),
EmberBindingTableEntry::ForNode(1, 1, 0, 0, cluster.std_optional()),
EmberBindingTableEntry::ForGroup(2, 2, 0, std::nullopt),
EmberBindingTableEntry::ForGroup(3, 3, 0, cluster.std_optional()),
};
table.SetPersistentStorage(&testStorage);
NL_TEST_ASSERT(aSuite, table.Add(expected[0]) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, table.Add(expected[1]) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, table.Add(expected[2]) == CHIP_NO_ERROR);
NL_TEST_ASSERT(aSuite, table.Add(expected[3]) == CHIP_NO_ERROR);
VerifyRestored(aSuite, testStorage, expected);
// Verify storage untouched if add fails
testStorage.AddPoisonKey(chip::DefaultStorageKeyAllocator::BindingTableEntry(4).KeyName());
NL_TEST_ASSERT(aSuite, table.Add(EmberBindingTableEntry::ForNode(4, 4, 0, 0, std::nullopt)) != CHIP_NO_ERROR);
VerifyRestored(aSuite, testStorage, expected);
testStorage.ClearPoisonKeys();
// Verify storage untouched if removing head fails
testStorage.AddPoisonKey(chip::DefaultStorageKeyAllocator::BindingTable().KeyName());
auto iter = table.begin();
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) != CHIP_NO_ERROR);
VerifyTableSame(aSuite, table, expected);
testStorage.ClearPoisonKeys();
VerifyRestored(aSuite, testStorage, expected);
// Verify storage untouched if removing other nodes fails
testStorage.AddPoisonKey(chip::DefaultStorageKeyAllocator::BindingTableEntry(0).KeyName());
iter = table.begin();
++iter;
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) != CHIP_NO_ERROR);
VerifyTableSame(aSuite, table, expected);
testStorage.ClearPoisonKeys();
VerifyRestored(aSuite, testStorage, expected);
// Verify removing head
iter = table.begin();
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) == CHIP_NO_ERROR);
VerifyTableSame(aSuite, table, { expected[1], expected[2], expected[3] });
VerifyRestored(aSuite, testStorage, { expected[1], expected[2], expected[3] });
// Verify removing other nodes
++iter;
NL_TEST_ASSERT(aSuite, table.RemoveAt(iter) == CHIP_NO_ERROR);
VerifyTableSame(aSuite, table, { expected[1], expected[3] });
VerifyRestored(aSuite, testStorage, { expected[1], expected[3] });
}
} // namespace
int TestBindingTable()
{
static nlTest sTests[] = {
NL_TEST_DEF("TestEmptyBindingTable", TestEmptyBindingTable),
NL_TEST_DEF("TestAdd", TestAdd),
NL_TEST_DEF("TestRemoveThenAdd", TestRemoveThenAdd),
NL_TEST_DEF("TestPersistentStorage", TestPersistentStorage),
NL_TEST_SENTINEL(),
};
nlTestSuite theSuite = {
"BindingTable",
&sTests[0],
nullptr,
nullptr,
};
nlTestRunner(&theSuite, nullptr);
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestBindingTable)