/* * * Copyright (c) 2020 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 // Helpers for simple payload management namespace { constexpr int kMaxPayloadValue = 100; /** * Derived cache class containing some test helper methods. */ template class TestableCache : public chip::Retransmit::Cache { public: /** * Convenience add when types are trivially copyable, so no actual * reference needs to be created. */ template ::value, int> = 0> CHIP_ERROR AddValue(const KeyType & key, PayloadType payload) { return chip::Retransmit::Cache::Add(key, payload); } }; class IntPayloadTracker { public: void Acquire(int value) { EXPECT_TRUE((value > 0) && value < kMaxPayloadValue); mAcquired.set(static_cast(value)); } void Release(int value) { EXPECT_TRUE((value > 0) && value < kMaxPayloadValue); EXPECT_TRUE(mAcquired.test(static_cast(value))); mAcquired.reset(static_cast(value)); } size_t Count() const { return mAcquired.count(); } bool IsAcquired(int value) const { return mAcquired.test(static_cast(value)); } private: std::bitset mAcquired; }; IntPayloadTracker gPayloadTracker; /** * Helper class defining a matches method for things divisible by a * specific value. */ class DivisibleBy { public: DivisibleBy(int value) : mValue(value) {} bool Matches(int x) const { return (x % mValue) == 0; } private: const int mValue; }; } // namespace template <> int chip::Retransmit::Lifetime::Acquire(int & value) { gPayloadTracker.Acquire(value); return value; } template <> void chip::Retransmit::Lifetime::Release(int & value) { gPayloadTracker.Release(value); value = 0; // make sure it is not used anymore } namespace { TEST(TestCache, TestNoOp) { // unused address cache should not do any Acquire/release at any time EXPECT_EQ(gPayloadTracker.Count(), 0u); { TestableCache test; EXPECT_EQ(gPayloadTracker.Count(), 0u); } EXPECT_EQ(gPayloadTracker.Count(), 0u); } TEST(TestCache, TestDestructorFree) { { TestableCache test; EXPECT_EQ(gPayloadTracker.Count(), 0u); EXPECT_EQ(test.AddValue(1, 1), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(2, 2), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 2u); } // destructor should release the items EXPECT_EQ(gPayloadTracker.Count(), 0u); } TEST(TestCache, OutOfSpace) { { TestableCache test; EXPECT_EQ(gPayloadTracker.Count(), 0u); EXPECT_EQ(test.AddValue(1, 1), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(2, 2), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(3, 4), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(4, 6), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 4u); EXPECT_EQ(test.AddValue(5, 8), CHIP_ERROR_NO_MEMORY); EXPECT_EQ(gPayloadTracker.Count(), 4u); EXPECT_EQ(test.AddValue(6, 10), CHIP_ERROR_NO_MEMORY); EXPECT_EQ(gPayloadTracker.Count(), 4u); } EXPECT_EQ(gPayloadTracker.Count(), 0u); } TEST(TestCache, AddRemove) { TestableCache test; EXPECT_EQ(gPayloadTracker.Count(), 0u); EXPECT_EQ(test.AddValue(1, 1), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(2, 2), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(3, 4), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 3u); EXPECT_EQ(test.AddValue(10, 8), CHIP_ERROR_NO_MEMORY); EXPECT_EQ(gPayloadTracker.Count(), 3u); EXPECT_EQ(test.Remove(2), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 2u); EXPECT_EQ(test.AddValue(10, 8), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 3u); EXPECT_EQ(test.Remove(14), CHIP_ERROR_KEY_NOT_FOUND); EXPECT_EQ(gPayloadTracker.Count(), 3u); EXPECT_EQ(test.Remove(1), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 2u); EXPECT_EQ(test.Remove(3), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 1u); EXPECT_EQ(test.Remove(3), CHIP_ERROR_KEY_NOT_FOUND); EXPECT_EQ(gPayloadTracker.Count(), 1u); EXPECT_EQ(test.Remove(10), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 0u); EXPECT_EQ(test.Remove(10), CHIP_ERROR_KEY_NOT_FOUND); EXPECT_EQ(gPayloadTracker.Count(), 0u); } TEST(TestCache, RemoveMatching) { TestableCache test; EXPECT_EQ(gPayloadTracker.Count(), 0u); EXPECT_EQ(test.AddValue(1, 1), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(2, 2), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(3, 4), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(4, 8), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 4u); test.RemoveMatching(DivisibleBy(2)); EXPECT_EQ(gPayloadTracker.Count(), 2u); // keys 1 and 3 remain EXPECT_TRUE(gPayloadTracker.IsAcquired(1)); EXPECT_TRUE(gPayloadTracker.IsAcquired(4)); EXPECT_EQ(test.Remove(3), CHIP_NO_ERROR); EXPECT_TRUE(gPayloadTracker.IsAcquired(1)); EXPECT_FALSE(gPayloadTracker.IsAcquired(4)); } TEST(TestCache, FindMatching) { TestableCache test; EXPECT_EQ(gPayloadTracker.Count(), 0u); EXPECT_EQ(test.AddValue(1, 1), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(2, 2), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(3, 4), CHIP_NO_ERROR); EXPECT_EQ(test.AddValue(4, 8), CHIP_NO_ERROR); EXPECT_EQ(gPayloadTracker.Count(), 4u); const int * key; const int * value; EXPECT_FALSE(test.Find(DivisibleBy(20), &key, &value)); EXPECT_EQ(key, nullptr); EXPECT_EQ(value, nullptr); // This relies on linear add. May need changing if implementation changes EXPECT_TRUE(test.Find(DivisibleBy(2), &key, &value)); EXPECT_EQ(*key, 2); EXPECT_EQ(*value, 2); EXPECT_EQ(test.Remove(*key), CHIP_NO_ERROR); EXPECT_TRUE(test.Find(DivisibleBy(2), &key, &value)); EXPECT_EQ(*key, 4); EXPECT_EQ(*value, 8); EXPECT_EQ(test.Remove(*key), CHIP_NO_ERROR); EXPECT_FALSE(test.Find(DivisibleBy(2), &key, &value)); EXPECT_EQ(key, nullptr); EXPECT_EQ(value, nullptr); } } // namespace