/* * * 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 #include #include #include #include #include using chip::BindingTable; namespace { void VerifyTableSame(BindingTable & table, const std::vector & expected) { ASSERT_EQ(table.Size(), expected.size()); auto iter1 = table.begin(); auto iter2 = expected.begin(); while (iter2 != expected.end()) { EXPECT_EQ(*iter1, *iter2); ++iter1; ++iter2; } EXPECT_EQ(iter1, table.end()); } void VerifyRestored(chip::TestPersistentStorageDelegate & storage, const std::vector & expected) { BindingTable restoredTable; restoredTable.SetPersistentStorage(&storage); EXPECT_EQ(restoredTable.LoadFromStorage(), CHIP_NO_ERROR); VerifyTableSame(restoredTable, expected); } TEST(TestBindingTable, TestEmptyBindingTable) { BindingTable table; chip::TestPersistentStorageDelegate testStorage; table.SetPersistentStorage(&testStorage); EXPECT_EQ(table.Size(), 0u); EXPECT_EQ(table.begin(), table.end()); } TEST(TestBindingTable, TestAdd) { BindingTable table; chip::TestPersistentStorageDelegate testStorage; table.SetPersistentStorage(&testStorage); EmberBindingTableEntry unusedEntry; unusedEntry.type = MATTER_UNUSED_BINDING; EXPECT_EQ(table.Add(unusedEntry), CHIP_ERROR_INVALID_ARGUMENT); for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++) { EXPECT_EQ(table.Add(EmberBindingTableEntry::ForNode(0, i, 0, 0, std::nullopt)), CHIP_NO_ERROR); } EXPECT_EQ(table.Add(EmberBindingTableEntry::ForNode(0, 0, 0, 0, std::nullopt)), CHIP_ERROR_NO_MEMORY); EXPECT_EQ(table.Size(), MATTER_BINDING_TABLE_SIZE); auto iter = table.begin(); for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++) { EXPECT_NE(iter, table.end()); EXPECT_EQ(iter->nodeId, i); EXPECT_EQ(iter.GetIndex(), i); ++iter; } EXPECT_EQ(iter, table.end()); } TEST(TestBindingTable, TestRemoveThenAdd) { BindingTable table; chip::TestPersistentStorageDelegate testStorage; table.SetPersistentStorage(&testStorage); EXPECT_EQ(table.Add(EmberBindingTableEntry::ForNode(0, 0, 0, 0, std::nullopt)), CHIP_NO_ERROR); auto iter = table.begin(); EXPECT_EQ(table.RemoveAt(iter), CHIP_NO_ERROR); EXPECT_EQ(iter, table.end()); EXPECT_EQ(table.Size(), 0u); EXPECT_EQ(table.begin(), table.end()); for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++) { EXPECT_EQ(table.Add(EmberBindingTableEntry::ForNode(0, i, 0, 0, std::nullopt)), CHIP_NO_ERROR); } iter = table.begin(); ++iter; EXPECT_EQ(table.RemoveAt(iter), CHIP_NO_ERROR); EXPECT_EQ(table.Size(), MATTER_BINDING_TABLE_SIZE - 1); EXPECT_EQ(iter->nodeId, 2u); EXPECT_EQ(iter.GetIndex(), 2u); auto iterCheck = table.begin(); ++iterCheck; EXPECT_EQ(iter, iterCheck); EXPECT_EQ(table.Add(EmberBindingTableEntry::ForNode(0, 1, 0, 0, std::nullopt)), CHIP_NO_ERROR); EXPECT_EQ(table.Size(), MATTER_BINDING_TABLE_SIZE); iter = table.begin(); for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE - 1; i++) { ++iter; } EXPECT_EQ(iter->nodeId, 1u); EXPECT_EQ(iter.GetIndex(), 1u); ++iter; EXPECT_EQ(iter, table.end()); iter = table.begin(); EXPECT_EQ(table.RemoveAt(iter), CHIP_NO_ERROR); EXPECT_EQ(table.Size(), MATTER_BINDING_TABLE_SIZE - 1); EXPECT_EQ(iter, table.begin()); EXPECT_EQ(iter.GetIndex(), 2u); EXPECT_EQ(iter->nodeId, 2u); EXPECT_EQ(table.GetAt(0).type, MATTER_UNUSED_BINDING); } TEST(TestBindingTable, TestPersistentStorage) { chip::TestPersistentStorageDelegate testStorage; BindingTable table; chip::Optional cluster = chip::MakeOptional(static_cast(UINT16_MAX + 6)); std::vector 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); EXPECT_EQ(table.Add(expected[0]), CHIP_NO_ERROR); EXPECT_EQ(table.Add(expected[1]), CHIP_NO_ERROR); EXPECT_EQ(table.Add(expected[2]), CHIP_NO_ERROR); EXPECT_EQ(table.Add(expected[3]), CHIP_NO_ERROR); VerifyRestored(testStorage, expected); // Verify storage untouched if add fails testStorage.AddPoisonKey(chip::DefaultStorageKeyAllocator::BindingTableEntry(4).KeyName()); EXPECT_NE(table.Add(EmberBindingTableEntry::ForNode(4, 4, 0, 0, std::nullopt)), CHIP_NO_ERROR); VerifyRestored(testStorage, expected); testStorage.ClearPoisonKeys(); // Verify storage untouched if removing head fails testStorage.AddPoisonKey(chip::DefaultStorageKeyAllocator::BindingTable().KeyName()); auto iter = table.begin(); EXPECT_NE(table.RemoveAt(iter), CHIP_NO_ERROR); VerifyTableSame(table, expected); testStorage.ClearPoisonKeys(); VerifyRestored(testStorage, expected); // Verify storage untouched if removing other nodes fails testStorage.AddPoisonKey(chip::DefaultStorageKeyAllocator::BindingTableEntry(0).KeyName()); iter = table.begin(); ++iter; EXPECT_NE(table.RemoveAt(iter), CHIP_NO_ERROR); VerifyTableSame(table, expected); testStorage.ClearPoisonKeys(); VerifyRestored(testStorage, expected); // Verify removing head iter = table.begin(); EXPECT_EQ(table.RemoveAt(iter), CHIP_NO_ERROR); VerifyTableSame(table, { expected[1], expected[2], expected[3] }); VerifyRestored(testStorage, { expected[1], expected[2], expected[3] }); // Verify removing other nodes ++iter; EXPECT_EQ(table.RemoveAt(iter), CHIP_NO_ERROR); VerifyTableSame(table, { expected[1], expected[3] }); VerifyRestored(testStorage, { expected[1], expected[3] }); } } // namespace