diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 7818b4e2e..20a379ecc 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -99,17 +99,29 @@ private: TValue mNullValue; public: + // To be used like: + // while (it.next()) { + // it.value(); it.key(); + // } class Iterator { public: - Iterator(const LruCache& cache): mCache(cache), mIterator(mCache.mSet->begin()) { + Iterator(const LruCache& cache): + mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) { } bool next() { if (mIterator == mCache.mSet->end()) { return false; } - std::advance(mIterator, 1); - return mIterator != mCache.mSet->end(); + if (!mBeginReturned) { + // mIterator has been initialized to the beginning and + // hasn't been returned. Do not advance: + mBeginReturned = true; + } else { + std::advance(mIterator, 1); + } + bool ret = (mIterator != mCache.mSet->end()); + return ret; } const TValue& value() const { @@ -122,6 +134,7 @@ public: private: const LruCache& mCache; typename LruCacheSet::iterator mIterator; + bool mBeginReturned; }; }; diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp index 2ed84d700..580b98058 100644 --- a/libutils/tests/LruCache_test.cpp +++ b/libutils/tests/LruCache_test.cpp @@ -293,4 +293,110 @@ TEST_F(LruCacheTest, CallbackOnClear) { EXPECT_EQ(3, callback.callbackCount); } +TEST_F(LruCacheTest, IteratorCheck) { + LruCache cache(100); + + cache.put(1, 4); + cache.put(2, 5); + cache.put(3, 6); + EXPECT_EQ(3U, cache.size()); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + int v = it.value(); + // Check we haven't seen the value before. + EXPECT_TRUE(returnedValues.find(v) == returnedValues.end()); + returnedValues.insert(v); + } + EXPECT_EQ(std::unordered_set({4, 5, 6}), returnedValues); +} + +TEST_F(LruCacheTest, EmptyCacheIterator) { + // Check that nothing crashes... + LruCache cache(100); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + returnedValues.insert(it.value()); + } + EXPECT_EQ(std::unordered_set(), returnedValues); +} + +TEST_F(LruCacheTest, OneElementCacheIterator) { + // Check that nothing crashes... + LruCache cache(100); + cache.put(1, 2); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + returnedValues.insert(it.value()); + } + EXPECT_EQ(std::unordered_set({ 2 }), returnedValues); +} + +TEST_F(LruCacheTest, OneElementCacheRemove) { + LruCache cache(100); + cache.put(1, 2); + + cache.remove(1); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + returnedValues.insert(it.value()); + } + EXPECT_EQ(std::unordered_set({ }), returnedValues); +} + +TEST_F(LruCacheTest, Remove) { + LruCache cache(100); + cache.put(1, 4); + cache.put(2, 5); + cache.put(3, 6); + + cache.remove(2); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + returnedValues.insert(it.value()); + } + EXPECT_EQ(std::unordered_set({ 4, 6 }), returnedValues); +} + +TEST_F(LruCacheTest, RemoveYoungest) { + LruCache cache(100); + cache.put(1, 4); + cache.put(2, 5); + cache.put(3, 6); + + cache.remove(3); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + returnedValues.insert(it.value()); + } + EXPECT_EQ(std::unordered_set({ 4, 5 }), returnedValues); +} + +TEST_F(LruCacheTest, RemoveNonMember) { + LruCache cache(100); + cache.put(1, 4); + cache.put(2, 5); + cache.put(3, 6); + + cache.remove(7); + + LruCache::Iterator it(cache); + std::unordered_set returnedValues; + while (it.next()) { + returnedValues.insert(it.value()); + } + EXPECT_EQ(std::unordered_set({ 4, 5, 6 }), returnedValues); +} + }