/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkDiscardableMemory.h" #include "SkScaledImageCache.h" #include "Test.h" namespace { static void* gGlobalAddress; struct TestingKey : public SkScaledImageCache::Key { void* fPtr; intptr_t fValue; TestingKey(intptr_t value) : fPtr(&gGlobalAddress), fValue(value) { this->init(sizeof(fPtr) + sizeof(fValue)); } }; struct TestingRec : public SkScaledImageCache::Rec { TestingRec(const TestingKey& key, uint32_t value) : fKey(key), fValue(value) {} TestingKey fKey; intptr_t fValue; virtual const Key& getKey() const SK_OVERRIDE { return fKey; } virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + sizeof(fValue); } }; } static const int COUNT = 10; static const int DIM = 256; static void test_cache(skiatest::Reporter* reporter, SkScaledImageCache& cache, bool testPurge) { SkScaledImageCache::ID id; for (int i = 0; i < COUNT; ++i) { TestingKey key(i); const TestingRec* rec = (const TestingRec*)cache.findAndLock(key); REPORTER_ASSERT(reporter, NULL == rec); TestingRec* newRec = SkNEW_ARGS(TestingRec, (key, i)); const TestingRec* addedRec = (const TestingRec*)cache.addAndLock(newRec); REPORTER_ASSERT(reporter, NULL != addedRec); const TestingRec* foundRec = (const TestingRec*)cache.findAndLock(key); REPORTER_ASSERT(reporter, foundRec == addedRec); REPORTER_ASSERT(reporter, foundRec->fValue == i); cache.unlock(foundRec); cache.unlock(addedRec); } if (testPurge) { // stress test, should trigger purges for (size_t i = 0; i < COUNT * 100; ++i) { TestingKey key(i); SkScaledImageCache::ID id = cache.addAndLock(SkNEW_ARGS(TestingRec, (key, i))); REPORTER_ASSERT(reporter, NULL != id); cache.unlock(id); } } // test the originals after all that purging for (int i = 0; i < COUNT; ++i) { id = cache.findAndLock(TestingKey(i)); if (id) { cache.unlock(id); } } cache.setTotalByteLimit(0); } #include "SkDiscardableMemoryPool.h" static SkDiscardableMemoryPool* gPool; static SkDiscardableMemory* pool_factory(size_t bytes) { SkASSERT(gPool); return gPool->create(bytes); } DEF_TEST(ImageCache, reporter) { static const size_t defLimit = DIM * DIM * 4 * COUNT + 1024; // 1K slop { SkScaledImageCache cache(defLimit); test_cache(reporter, cache, true); } { SkAutoTUnref pool( SkDiscardableMemoryPool::Create(defLimit, NULL)); gPool = pool.get(); SkScaledImageCache cache(pool_factory); test_cache(reporter, cache, true); } { SkScaledImageCache cache(SkDiscardableMemory::Create); test_cache(reporter, cache, false); } } DEF_TEST(ImageCache_doubleAdd, r) { // Adding the same key twice should be safe. SkScaledImageCache cache(4096); TestingKey key(1); SkScaledImageCache::ID id1 = cache.addAndLock(SkNEW_ARGS(TestingRec, (key, 2))); SkScaledImageCache::ID id2 = cache.addAndLock(SkNEW_ARGS(TestingRec, (key, 3))); // We don't really care if id1 == id2 as long as unlocking both works. cache.unlock(id1); cache.unlock(id2); // Lookup can return either value. const TestingRec* rec = (const TestingRec*)cache.findAndLock(key); REPORTER_ASSERT(r, NULL != rec); REPORTER_ASSERT(r, 2 == rec->fValue || 3 == rec->fValue); cache.unlock(rec); }