summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorThiago Marcos P. Santos <thiago@mapbox.com>2015-12-21 17:53:34 +0200
committerThiago Marcos P. Santos <thiago@mapbox.com>2015-12-28 14:48:02 +0200
commite6dc4fe49d9e6ce59d4a888b82460a9ec89e3449 (patch)
tree86d3e3a6fe82fc56a7e30ef2617d32970f310b9b /test
parent6d0b01c5fb1e0740ea48566fea200f03b979c52c (diff)
downloadqtlocation-mapboxgl-e6dc4fe49d9e6ce59d4a888b82460a9ec89e3449.tar.gz
[tests] Add unit tests for SQLiteCache size limit
Diffstat (limited to 'test')
-rw-r--r--test/fixtures/database/cache.dbbin4096 -> 0 bytes
-rw-r--r--test/fixtures/database/invalid.dbbin4096 -> 0 bytes
-rw-r--r--test/storage/cache_size.cpp232
-rw-r--r--test/test.gypi1
4 files changed, 233 insertions, 0 deletions
diff --git a/test/fixtures/database/cache.db b/test/fixtures/database/cache.db
deleted file mode 100644
index 04bbdbb34b..0000000000
--- a/test/fixtures/database/cache.db
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/database/invalid.db b/test/fixtures/database/invalid.db
deleted file mode 100644
index b068b15465..0000000000
--- a/test/fixtures/database/invalid.db
+++ /dev/null
Binary files differ
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',