diff options
Diffstat (limited to 'test/source/TestLruCache.cpp')
-rw-r--r-- | test/source/TestLruCache.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/test/source/TestLruCache.cpp b/test/source/TestLruCache.cpp new file mode 100644 index 0000000..e659218 --- /dev/null +++ b/test/source/TestLruCache.cpp @@ -0,0 +1,340 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#include "EASTLTest.h" +#include <EASTL/bonus/lru_cache.h> +#include <EASTL/unique_ptr.h> + +namespace TestLruCacheInternal +{ + struct Foo + { + static int count; + + Foo() + : a(count++) + , b(count++) + { } + + Foo(int x, int y) : a(x), b(y) {} + + int a; + int b; + + bool operator==(const Foo &other) + { + return this->a == other.a && this->b == other.b; + } + }; + + int Foo::count = 0; + + class FooCreator + { + public: + FooCreator() : mFooCreatedCount(0) {} + + Foo *Create() + { + mFooCreatedCount++; + return new Foo(); + } + + void Destroy(Foo *f) + { + delete f; + mFooCreatedCount--; + } + + int mFooCreatedCount; + }; +} + + +int TestLruCache() +{ + int nErrorCount = 0; + + // Test simple situation + { + using namespace TestLruCacheInternal; + + eastl::lru_cache<int, Foo> lruCache(3); + + // Empty state + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.size() == 0); + EATEST_VERIFY(lruCache.empty() == true); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.at(1).has_value() == false); + + // Auto create with get call + EATEST_VERIFY(lruCache[0].a == 0); + EATEST_VERIFY(lruCache[0].b == 1); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(0) == true); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + + // Fill structure up to 2 more entries to fill out, also test at() + lruCache.insert(1, Foo(2, 3)); + EATEST_VERIFY(lruCache.at(1).value().a == 2); + EATEST_VERIFY(lruCache.at(1).value().b == 3); + EATEST_VERIFY(lruCache.contains(0) == true); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == false); + EATEST_VERIFY(lruCache.size() == 2); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + + lruCache.insert(2, Foo(4, 5)); + EATEST_VERIFY(lruCache[2].a == 4); + EATEST_VERIFY(lruCache[2].b == 5); + EATEST_VERIFY(lruCache.contains(0) == true); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == true); + EATEST_VERIFY(lruCache.contains(3) == false); + EATEST_VERIFY(lruCache.size() == 3); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + + // Add another entry, at this point 0 is the oldest, so it should be pulled + lruCache.insert(3, Foo(6, 7)); + EATEST_VERIFY(lruCache[3].a == 6); + EATEST_VERIFY(lruCache[3].b == 7); + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == true); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.size() == 3); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + + // Touch the now oldest 1 key + EATEST_VERIFY(lruCache.touch(1) == true); + + // Add another entry, this will be #4 but since 1 was touched, 2 is now the oldest + lruCache.insert(4, Foo(8, 9)); + EATEST_VERIFY(lruCache[4].a == 8); + EATEST_VERIFY(lruCache[4].b == 9); + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.contains(4) == true); + EATEST_VERIFY(lruCache.size() == 3); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + + // Test resize down + EATEST_VERIFY(lruCache.touch(3) == true); // Let's make some key in the middle the most recent + lruCache.resize(1); // Resize down to 1 entry in the cache + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.contains(4) == false); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 1); + + // Let's resize up to a size of 5 now + lruCache.resize(5); + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.contains(4) == false); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 5); + + // Let's try updating + lruCache.assign(3, Foo(0, 0)); + EATEST_VERIFY(lruCache[3] == Foo(0, 0)); + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.contains(4) == false); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 5); + + // add or update existing + lruCache.insert_or_assign(3, Foo(1, 1)); + EATEST_VERIFY(lruCache[3] == Foo(1, 1)); + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.contains(4) == false); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 5); + + // Add or update a new entry + lruCache.insert_or_assign(25, Foo(2, 2)); + EATEST_VERIFY(lruCache[3] == Foo(1, 1)); + EATEST_VERIFY(lruCache[25] == Foo(2, 2)); + EATEST_VERIFY(lruCache.contains(0) == false); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(3) == true); + EATEST_VERIFY(lruCache.contains(4) == false); + EATEST_VERIFY(lruCache.contains(25) == true); + EATEST_VERIFY(lruCache.size() == 2); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 5); + + // clear everything + lruCache.clear(); + EATEST_VERIFY(lruCache.size() == 0); + EATEST_VERIFY(lruCache.empty() == true); + EATEST_VERIFY(lruCache.capacity() == 5); + EATEST_VERIFY(lruCache.contains(3) == false); + + // test unilateral reset + lruCache[1] = Foo(1, 2); + lruCache.reset_lose_memory(); + EATEST_VERIFY(lruCache.size() == 0); + } + + // Test more advanced creation / deletion via callbacks + { + using namespace TestLruCacheInternal; + + FooCreator fooCreator; + + auto createCallback = [&fooCreator](int) { return fooCreator.Create(); }; + auto deleteCallback = [&fooCreator](Foo *f) { fooCreator.Destroy(f); }; + + eastl::lru_cache<int, Foo*> lruCache(3, EASTLAllocatorType("eastl lru_cache"), createCallback, deleteCallback); + + lruCache[1]; + EATEST_VERIFY(fooCreator.mFooCreatedCount == 1); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == false); + + lruCache[2]; + EATEST_VERIFY(fooCreator.mFooCreatedCount == 2); + EATEST_VERIFY(lruCache.size() == 2); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == true); + + // Update 2, which should delete the existing entry + { + auto f = fooCreator.Create(); + EATEST_VERIFY(fooCreator.mFooCreatedCount == 3); + f->a = 20; + f->b = 21; + lruCache.assign(2, f); + EATEST_VERIFY(fooCreator.mFooCreatedCount == 2); + EATEST_VERIFY(lruCache.size() == 2); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == true); + EATEST_VERIFY(lruCache[2]->a == 20); + EATEST_VERIFY(lruCache[2]->b == 21); + } + + lruCache.erase(2); + EATEST_VERIFY(fooCreator.mFooCreatedCount == 1); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.contains(1) == true); + EATEST_VERIFY(lruCache.contains(2) == false); + + lruCache.erase(1); + EATEST_VERIFY(fooCreator.mFooCreatedCount == 0); + EATEST_VERIFY(lruCache.size() == 0); + EATEST_VERIFY(lruCache.empty() == true); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + + // Test insert_or_assign + { + auto f = fooCreator.Create(); + f->a = 22; + f->b = 30; + EATEST_VERIFY(fooCreator.mFooCreatedCount == 1); + + lruCache.insert_or_assign(7, f); + EATEST_VERIFY(lruCache.size() == 1); + EATEST_VERIFY(lruCache.empty() == false); + EATEST_VERIFY(lruCache.capacity() == 3); + EATEST_VERIFY(lruCache.contains(1) == false); + EATEST_VERIFY(lruCache.contains(2) == false); + EATEST_VERIFY(lruCache.contains(7) == true); + EATEST_VERIFY(lruCache.erase(7) == true); + EATEST_VERIFY(fooCreator.mFooCreatedCount == 0); + } + } + + // Test iteration + { + eastl::lru_cache<int, int> lc(5); + lc.insert_or_assign(0,10); + lc.insert_or_assign(1,11); + lc.insert_or_assign(2,12); + lc.insert_or_assign(3,13); + lc.insert_or_assign(4,14); + + { // test manual for-loop + int i = 0; + for (auto b = lc.begin(), e = lc.end(); b != e; b++) + { + auto &p = *b; + VERIFY(i == p.first); + VERIFY(i + 10 == p.second.first); + i++; + } + } + + { // test pairs + int i = 0; + for(auto& p : lc) + { + VERIFY(i == p.first); + VERIFY(i + 10 == p.second.first); + i++; + } + } + + { // test structured bindings + int i = 0; + for(auto& [key, value] : lc) + { + VERIFY(i == key); + VERIFY(i + 10 == value.first); + i++; + } + } + } + + // test initializer_list + { + eastl::lru_cache<int, int> lc = {{0, 10}, {1, 11}, {2, 12}, {3, 13}, {4, 14}, {5, 15}}; + + int i = 0; + for(auto& p : lc) + { + VERIFY(i == p.first); + VERIFY(i + 10 == p.second.first); + i++; + } + } + + return nErrorCount; +} |