| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2016-2017 Nest Labs, Inc. |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * Unit tests for the Chip Persisted Storage API. |
| * |
| */ |
| |
| #include <gtest/gtest.h> |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/support/DefaultStorageKeyAllocator.h> |
| #include <lib/support/PersistedCounter.h> |
| #include <lib/support/TestPersistentStorageDelegate.h> |
| #include <platform/PersistedStorage.h> |
| |
| using namespace chip; |
| |
| namespace { |
| |
| TEST(TestPersistedCounter, TestOutOfBox) |
| { |
| TestPersistentStorageDelegate storage; |
| PersistedCounter<uint64_t> counter; |
| |
| // When initializing the first time out of the box, we should have a count of 0 and a value of 0x10000 for the next starting |
| // value in persistent storage. |
| EXPECT_EQ(counter.Init(&storage, DefaultStorageKeyAllocator::IMEventNumber(), 0x10000), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0ULL); |
| } |
| |
| TEST(TestPersistedCounter, TestReboot) |
| { |
| chip::TestPersistentStorageDelegate storage; |
| chip::PersistedCounter<uint64_t> counter, counter2; |
| |
| // When initializing the first time out of the box, we should have a count of 0. |
| EXPECT_EQ(counter.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), 0x10000), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0ULL); |
| |
| // Now we "reboot", and we should get a count of 0x10000. |
| EXPECT_EQ(counter2.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), 0x10000), CHIP_NO_ERROR); |
| EXPECT_EQ(counter2.GetValue(), 0x10000ULL); |
| } |
| |
| TEST(TestPersistedCounter, TestkWriteNextCounterStart) |
| { |
| chip::TestPersistentStorageDelegate storage; |
| chip::PersistedCounter<uint64_t> counter; |
| |
| uint64_t storedValue = 0; |
| uint16_t size = sizeof(storedValue); |
| |
| // When initializing the first time out of the box, we should have a count of 0. |
| EXPECT_EQ(counter.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), 0x10000), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0ULL); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(0x10000ULL, storedValue); |
| |
| // Verify that we write out the next starting counter value after |
| // we've exhausted the counter's range. |
| for (int32_t i = 0; i < 0x10000; i++) |
| { |
| EXPECT_EQ(counter.Advance(), CHIP_NO_ERROR); |
| } |
| EXPECT_EQ(counter.GetValue(), 0x10000ULL); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(0x20000ULL, storedValue); |
| |
| for (int32_t i = 0; i < 0x10000; i++) |
| { |
| EXPECT_EQ(counter.Advance(), CHIP_NO_ERROR); |
| } |
| EXPECT_EQ(counter.GetValue(), 0x20000ULL); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(0x30000ULL, storedValue); |
| } |
| |
| TEST(TestPersistedCounter, TestAdvanceMaxCounterValue) |
| { |
| chip::TestPersistentStorageDelegate storage; |
| chip::PersistedCounter<uint8_t> counter; |
| |
| uint8_t storedValue = 0; |
| uint16_t size = sizeof(storedValue); |
| |
| // When initializing the first time out of the box, we should have a count of 0. |
| EXPECT_EQ(counter.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), 1U), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0); |
| |
| for (uint16_t i = 0; i < UINT8_MAX; i++) |
| { |
| // Make sure we can increment to max value without error |
| EXPECT_EQ(counter.Advance(), CHIP_NO_ERROR); |
| } |
| EXPECT_EQ(counter.GetValue(), 255); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint8_t>(storedValue); |
| |
| EXPECT_EQ(storedValue, 0); |
| |
| // Increment one more step to make sure counter continues to increment |
| EXPECT_EQ(counter.Advance(), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0); |
| } |
| |
| TEST(TestPersistedCounter, TestAdvanceBy) |
| { |
| chip::TestPersistentStorageDelegate storage; |
| chip::PersistedCounter<uint64_t> counter; |
| |
| // Test values |
| constexpr uint64_t startingEpochValue = 65535; |
| constexpr uint64_t newValue = 2 * startingEpochValue + 156; |
| constexpr uint64_t newEpochValue = newValue + startingEpochValue; |
| uint64_t storedValue = 0; |
| uint16_t size = sizeof(storedValue); |
| |
| // When initializing the first time out of the box, we should have a count of 0. |
| EXPECT_EQ(counter.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), startingEpochValue), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0ULL); |
| |
| // Set New value |
| EXPECT_EQ(counter.AdvanceBy(newValue), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), newValue); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| |
| EXPECT_EQ(newEpochValue, storedValue); |
| } |
| |
| TEST(TestPersistedCounter, TestAdvanceByMaxCounterValue) |
| { |
| chip::TestPersistentStorageDelegate storage; |
| chip::PersistedCounter<uint64_t> counter; |
| |
| uint64_t storedValue = 0; |
| uint16_t size = sizeof(storedValue); |
| |
| EXPECT_EQ(counter.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), 1ULL), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0ULL); |
| |
| EXPECT_EQ(counter.AdvanceBy(UINT64_MAX), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), UINT64_MAX); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(0ULL, storedValue); |
| |
| // Increment one more step to make sure counter continues to increment |
| EXPECT_EQ(counter.AdvanceBy(1ULL), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), 0ULL); |
| } |
| |
| TEST(TestPersistedCounter, TestAdvanceByRollover) |
| { |
| chip::TestPersistentStorageDelegate storage; |
| chip::PersistedCounter<uint64_t> counter; |
| |
| uint64_t epoch = UINT64_MAX / 4; |
| uint64_t currentEpoch = epoch; |
| uint64_t current = 0; |
| uint64_t storedValue = 0; |
| uint16_t size = sizeof(storedValue); |
| |
| EXPECT_EQ(counter.Init(&storage, chip::DefaultStorageKeyAllocator::IMEventNumber(), epoch), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), current); |
| |
| // Check new Epoch value was persisted |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(currentEpoch, storedValue); |
| |
| // Increase counter to update persisted value |
| current += (currentEpoch + 100); |
| EXPECT_EQ(counter.AdvanceBy(currentEpoch + 100), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), current); |
| |
| // Check new Epoch value was persisted |
| currentEpoch = (currentEpoch * 2 + 100); |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(currentEpoch, storedValue); |
| |
| // Force roll over |
| current += (3 * epoch); |
| EXPECT_EQ(counter.AdvanceBy((3 * epoch)), CHIP_NO_ERROR); |
| EXPECT_EQ(counter.GetValue(), current); |
| |
| // Check new Epoch value was persisted |
| currentEpoch = current + epoch; |
| EXPECT_EQ(storage.SyncGetKeyValue(chip::DefaultStorageKeyAllocator::IMEventNumber().KeyName(), &storedValue, size), |
| CHIP_NO_ERROR); |
| EXPECT_EQ(sizeof(storedValue), size); |
| storedValue = Encoding::LittleEndian::HostSwap<uint64_t>(storedValue); |
| EXPECT_EQ(currentEpoch, storedValue); |
| } |
| |
| } // namespace |