blob: fefdbca67250ec9c6779513c049ebf90e5f466f5 [file] [log] [blame]
/*
*
* 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 <lib/support/UnitTestRegistration.h>
#include <transport/retransmit/Cache.h>
#include <bitset>
#include <nlunit-test.h>
// Helpers for simple payload management
namespace {
constexpr int kMaxPayloadValue = 100;
/**
* Derived cache class containing some test helper methods.
*/
template <typename KeyType, typename PayloadType, size_t N>
class TestableCache : public chip::Retransmit::Cache<KeyType, PayloadType, N>
{
public:
/**
* Convenience add when types are trivially copyable, so no actual
* reference needs to be created.
*/
template <std::enable_if_t<std::is_trivially_copyable<PayloadType>::value, int> = 0>
CHIP_ERROR AddValue(const KeyType & key, PayloadType payload)
{
return chip::Retransmit::Cache<KeyType, PayloadType, N>::Add(key, payload);
}
};
class IntPayloadTracker
{
public:
void Init(nlTestSuite * suite) { mSuite = suite; }
void Acquire(int value)
{
NL_TEST_ASSERT(mSuite, (value > 0) && value < kMaxPayloadValue);
mAcquired.set(static_cast<size_t>(value));
}
void Release(int value)
{
NL_TEST_ASSERT(mSuite, (value > 0) && value < kMaxPayloadValue);
NL_TEST_ASSERT(mSuite, mAcquired.test(static_cast<size_t>(value)));
mAcquired.reset(static_cast<size_t>(value));
}
size_t Count() const { return mAcquired.count(); }
bool IsAcquired(int value) const { return mAcquired.test(static_cast<size_t>(value)); }
private:
nlTestSuite * mSuite;
std::bitset<kMaxPayloadValue> 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<int>::Acquire(int & value)
{
gPayloadTracker.Acquire(value);
return value;
}
template <>
void chip::Retransmit::Lifetime<int>::Release(int & value)
{
gPayloadTracker.Release(value);
value = 0; // make sure it is not used anymore
}
namespace {
void TestNoOp(nlTestSuite * inSuite, void * inContext)
{
// unused address cache should not do any Acquire/release at any time
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
{
TestableCache<int, int, 20> test;
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
}
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
}
void TestDestructorFree(nlTestSuite * inSuite, void * inContext)
{
{
TestableCache<int, int, 20> test;
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
NL_TEST_ASSERT(inSuite, test.AddValue(1, 1) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(2, 2) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 2);
}
// destructor should release the items
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
}
void OutOfSpace(nlTestSuite * inSuite, void * inContext)
{
{
TestableCache<int, int, 4> test;
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
NL_TEST_ASSERT(inSuite, test.AddValue(1, 1) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(2, 2) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(3, 4) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(4, 6) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 4);
NL_TEST_ASSERT(inSuite, test.AddValue(5, 8) == CHIP_ERROR_NO_MEMORY);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 4);
NL_TEST_ASSERT(inSuite, test.AddValue(6, 10) == CHIP_ERROR_NO_MEMORY);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 4);
}
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
}
void AddRemove(nlTestSuite * inSuite, void * inContext)
{
TestableCache<int, int, 3> test;
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
NL_TEST_ASSERT(inSuite, test.AddValue(1, 1) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(2, 2) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(3, 4) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 3);
NL_TEST_ASSERT(inSuite, test.AddValue(10, 8) == CHIP_ERROR_NO_MEMORY);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 3);
NL_TEST_ASSERT(inSuite, test.Remove(2) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 2);
NL_TEST_ASSERT(inSuite, test.AddValue(10, 8) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 3);
NL_TEST_ASSERT(inSuite, test.Remove(14) == CHIP_ERROR_KEY_NOT_FOUND);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 3);
NL_TEST_ASSERT(inSuite, test.Remove(1) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 2);
NL_TEST_ASSERT(inSuite, test.Remove(3) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 1);
NL_TEST_ASSERT(inSuite, test.Remove(3) == CHIP_ERROR_KEY_NOT_FOUND);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 1);
NL_TEST_ASSERT(inSuite, test.Remove(10) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
NL_TEST_ASSERT(inSuite, test.Remove(10) == CHIP_ERROR_KEY_NOT_FOUND);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
}
void RemoveMatching(nlTestSuite * inSuite, void * inContext)
{
TestableCache<int, int, 4> test;
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
NL_TEST_ASSERT(inSuite, test.AddValue(1, 1) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(2, 2) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(3, 4) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(4, 8) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 4);
test.RemoveMatching(DivisibleBy(2));
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 2);
// keys 1 and 3 remain
NL_TEST_ASSERT(inSuite, gPayloadTracker.IsAcquired(1));
NL_TEST_ASSERT(inSuite, gPayloadTracker.IsAcquired(4));
NL_TEST_ASSERT(inSuite, test.Remove(3) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.IsAcquired(1));
NL_TEST_ASSERT(inSuite, !gPayloadTracker.IsAcquired(4));
}
void FindMatching(nlTestSuite * inSuite, void * inContext)
{
TestableCache<int, int, 4> test;
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 0);
NL_TEST_ASSERT(inSuite, test.AddValue(1, 1) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(2, 2) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(3, 4) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.AddValue(4, 8) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, gPayloadTracker.Count() == 4);
const int * key;
const int * value;
NL_TEST_ASSERT(inSuite, test.Find(DivisibleBy(20), &key, &value) == false);
NL_TEST_ASSERT(inSuite, key == nullptr);
NL_TEST_ASSERT(inSuite, value == nullptr);
// This relies on linear add. May need changing if implementation changes
NL_TEST_ASSERT(inSuite, test.Find(DivisibleBy(2), &key, &value) == true);
NL_TEST_ASSERT(inSuite, *key == 2);
NL_TEST_ASSERT(inSuite, *value == 2);
NL_TEST_ASSERT(inSuite, test.Remove(*key) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.Find(DivisibleBy(2), &key, &value) == true);
NL_TEST_ASSERT(inSuite, *key == 4);
NL_TEST_ASSERT(inSuite, *value == 8);
NL_TEST_ASSERT(inSuite, test.Remove(*key) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, test.Find(DivisibleBy(2), &key, &value) == false);
NL_TEST_ASSERT(inSuite, key == nullptr);
NL_TEST_ASSERT(inSuite, value == nullptr);
}
} // namespace
// clang-format off
static const nlTest sTests[] =
{
NL_TEST_DEF("NoOp", TestNoOp),
NL_TEST_DEF("DestructorFree", TestDestructorFree),
NL_TEST_DEF("OutOfSpace", OutOfSpace),
NL_TEST_DEF("AddRemove", AddRemove),
NL_TEST_DEF("RemoveMatching", RemoveMatching),
NL_TEST_DEF("FindMatching", FindMatching),
NL_TEST_SENTINEL()
};
// clang-format on
int TestCache()
{
nlTestSuite theSuite = { "Retransmit-Cache", &sTests[0], nullptr, nullptr };
gPayloadTracker.Init(&theSuite);
nlTestRunner(&theSuite, nullptr);
return nlTestRunnerStats(&theSuite);
}
CHIP_REGISTER_TEST_SUITE(TestCache)