diff options
author | Thiago Marcos P. Santos <thiago@mapbox.com> | 2015-12-21 17:53:34 +0200 |
---|---|---|
committer | Thiago Marcos P. Santos <thiago@mapbox.com> | 2015-12-28 14:48:02 +0200 |
commit | e6dc4fe49d9e6ce59d4a888b82460a9ec89e3449 (patch) | |
tree | 86d3e3a6fe82fc56a7e30ef2617d32970f310b9b /test | |
parent | 6d0b01c5fb1e0740ea48566fea200f03b979c52c (diff) | |
download | qtlocation-mapboxgl-e6dc4fe49d9e6ce59d4a888b82460a9ec89e3449.tar.gz |
[tests] Add unit tests for SQLiteCache size limit
Diffstat (limited to 'test')
-rw-r--r-- | test/fixtures/database/cache.db | bin | 4096 -> 0 bytes | |||
-rw-r--r-- | test/fixtures/database/invalid.db | bin | 4096 -> 0 bytes | |||
-rw-r--r-- | test/storage/cache_size.cpp | 232 | ||||
-rw-r--r-- | test/test.gypi | 1 |
4 files changed, 233 insertions, 0 deletions
diff --git a/test/fixtures/database/cache.db b/test/fixtures/database/cache.db Binary files differdeleted file mode 100644 index 04bbdbb34b..0000000000 --- a/test/fixtures/database/cache.db +++ /dev/null diff --git a/test/fixtures/database/invalid.db b/test/fixtures/database/invalid.db Binary files differdeleted file mode 100644 index b068b15465..0000000000 --- a/test/fixtures/database/invalid.db +++ /dev/null diff --git a/test/storage/cache_size.cpp b/test/storage/cache_size.cpp new file mode 100644 index 0000000000..87f033e71c --- /dev/null +++ b/test/storage/cache_size.cpp @@ -0,0 +1,232 @@ +#include "storage.hpp" + +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> +#include <mbgl/storage/sqlite_cache.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/time.hpp> + +#include <memory> +#include <random> + +#include <unistd.h> + +bool tileIsCached(mbgl::SQLiteCache* cache, unsigned id) { + using namespace mbgl; + + auto url = std::string("http://tile") + mbgl::util::toString(id); + bool replied = false; + + std::unique_ptr<Response> response; + auto callback = [&] (std::unique_ptr<Response> res) { + replied = true; + response = std::move(res); + }; + + Resource resource{ Resource::Kind::Tile, url }; + auto req = cache->get(resource, callback); + + while (!replied) { + util::RunLoop::Get()->runOnce(); + } + + return response != nullptr; +} + +void insertTile(mbgl::SQLiteCache* cache, unsigned id, uint64_t size) { + using namespace mbgl; + + auto url = std::string("http://tile") + mbgl::util::toString(id); + + auto response = std::make_shared<Response>(); + response->modified = toSeconds(SystemClock::now()); + response->expires = toSeconds(SystemClock::now()) + Seconds(5000); + response->etag = url; + + auto data = std::make_shared<std::string>(size, 0); + + // Fill data with garbage so SQLite won't try to + // optimize allocation by reusing pages. + static std::mt19937 generator; + std::generate_n(data->begin(), size, generator); + + response->data = data; + + Resource resource{ Resource::Kind::Tile, url }; + cache->put(resource, response, SQLiteCache::Hint::Full); +} + +void refreshTile(mbgl::SQLiteCache* cache, unsigned id) { + using namespace mbgl; + + auto url = std::string("http://tile") + mbgl::util::toString(id); + + auto response = std::make_shared<Response>(); + response->modified = toSeconds(SystemClock::now()); + response->expires = toSeconds(SystemClock::now()) + Seconds(5000); + + Resource resource{ Resource::Kind::Tile, url }; + cache->put(resource, response, SQLiteCache::Hint::Refresh); +} + +uint64_t cacheSize(mbgl::SQLiteCache* cache, unsigned entryCount, uint64_t entrySize) { + uint64_t total = 0; + + for (unsigned i = 0; i < entryCount; ++i) { + if (tileIsCached(cache, i)) { + total += entrySize; + } + } + + return total; +} + +TEST_F(Storage, CacheEntrySizeLimit) { + using namespace mbgl; + + util::RunLoop loop; + SQLiteCache cache(":memory:"); + + const uint64_t entrySize = 5 * 1024 * 1024; // 5 MB + + insertTile(&cache, 0, entrySize); + EXPECT_TRUE(tileIsCached(&cache, 0)); + + insertTile(&cache, 1, entrySize + 1); + EXPECT_FALSE(tileIsCached(&cache, 1)); + + insertTile(&cache, 2, entrySize - 1); + EXPECT_TRUE(tileIsCached(&cache, 2)); + + // Setting a new size should not delete existing entries. + cache.setMaximumCacheEntrySize(entrySize / 2); + EXPECT_TRUE(tileIsCached(&cache, 2)); + + insertTile(&cache, 3, entrySize / 2 - 1); + EXPECT_TRUE(tileIsCached(&cache, 3)); + + insertTile(&cache, 4, entrySize); + EXPECT_FALSE(tileIsCached(&cache, 4)); + + cache.setMaximumCacheEntrySize(entrySize * 2); + insertTile(&cache, 5, entrySize); + EXPECT_TRUE(tileIsCached(&cache, 5)); +} + +TEST_F(Storage, CacheSizeSetNewLimit) { + using namespace mbgl; + + util::RunLoop loop; + SQLiteCache cache(":memory:"); + + const unsigned entryCount = 800; + const uint64_t entrySize = 10 * 1024; // 10 KB + + cache.setMaximumCacheEntrySize(entrySize + 1); + + // Cache size defaults to unlimited, all these + // inserts should work. + for (unsigned i = 0; i < entryCount; ++i) { + insertTile(&cache, i, entrySize); + } + + for (unsigned i = 0; i < entryCount; ++i) { + EXPECT_TRUE(tileIsCached(&cache, i)); + } + + uint64_t expectedCacheSize = entryCount * entrySize; + EXPECT_EQ(cacheSize(&cache, entryCount, entrySize), expectedCacheSize); + + // Setting a new size should remove records until the new + // size limit is satisfied. + cache.setMaximumCacheSize(expectedCacheSize / 2); + EXPECT_LT(cacheSize(&cache, entryCount, entrySize), expectedCacheSize / 2); + + // Cache size 1 should practically clean the cache and + // prevent adding any record, although it makes no sense + // to use such size limit IRL. + cache.setMaximumCacheSize(1); + EXPECT_EQ(cacheSize(&cache, entryCount, entrySize), 0); + + insertTile(&cache, 1000, entrySize); + EXPECT_FALSE(tileIsCached(&cache, 1000)); + + // Zero should be treated as unlimited. + cache.setMaximumCacheSize(0); + + for (unsigned i = 0; i < entryCount; ++i) { + insertTile(&cache, i, entrySize); + } + + EXPECT_EQ(cacheSize(&cache, entryCount, entrySize), expectedCacheSize); +} + +TEST_F(Storage, CacheSizePruneLeastAccessed) { + using namespace mbgl; + + util::RunLoop loop; + SQLiteCache cache(":memory:"); + + const unsigned entryCount = 400; + const uint64_t entrySize = 10 * 1024; // 10 KB + + cache.setMaximumCacheEntrySize(entrySize + 1); + cache.setMaximumCacheSize(entrySize * 350); + + for (unsigned i = 0; i < entryCount; ++i) { + insertTile(&cache, i, entrySize); + + if (i == entryCount / 2) { + // We need to sleep for 1s here because + // that is the time resolution for the + // `accessed` time. Then we 'ping' the + // entry, that should update the + // `accessed` time, so it won't get + // pruned when we need more space. + sleep(1); + + EXPECT_TRUE(tileIsCached(&cache, 7)); + + // Refresh should also update the `accessed` + // time of a tile. + refreshTile(&cache, 9); + } + } + + EXPECT_FALSE(tileIsCached(&cache, 6)); + EXPECT_FALSE(tileIsCached(&cache, 8)); + EXPECT_FALSE(tileIsCached(&cache, 10)); + + EXPECT_TRUE(tileIsCached(&cache, 7)); + EXPECT_TRUE(tileIsCached(&cache, 9)); +} + +TEST_F(Storage, CacheSizeStress) { + using namespace mbgl; + + util::RunLoop loop; + SQLiteCache cache(":memory:"); + + const unsigned entryCount = 2000; + const uint64_t entrySize = 10 * 1024; // 10 KB + + cache.setMaximumCacheEntrySize(entrySize + 1); + cache.setMaximumCacheSize(entrySize * 300); + + for (unsigned i = 0; i < entryCount; ++i) { + insertTile(&cache, i, entrySize); + } + + // Should not be in the cache as they were + // first inserted. + EXPECT_FALSE(tileIsCached(&cache, 0)); + EXPECT_FALSE(tileIsCached(&cache, 99)); + EXPECT_FALSE(tileIsCached(&cache, 199)); + EXPECT_FALSE(tileIsCached(&cache, 299)); + EXPECT_FALSE(tileIsCached(&cache, 399)); + + EXPECT_TRUE(tileIsCached(&cache, entryCount - 1)); + + EXPECT_LT(cacheSize(&cache, entryCount, entrySize), entrySize * 300); +} diff --git a/test/test.gypi b/test/test.gypi index 4a570932ac..6a6ba507c6 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -71,6 +71,7 @@ 'storage/storage.cpp', 'storage/cache_response.cpp', 'storage/cache_revalidate.cpp', + 'storage/cache_size.cpp', 'storage/database.cpp', 'storage/directory_reading.cpp', 'storage/file_reading.cpp', |