|  /* | 
 |  * 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 "SkResourceCache.h" | 
 | #include "Test.h" | 
 |  | 
 | namespace { | 
 | static void* gGlobalAddress; | 
 | struct TestingKey : public SkResourceCache::Key { | 
 |     intptr_t    fValue; | 
 |  | 
 |     TestingKey(intptr_t value, uint64_t sharedID = 0) : fValue(value) { | 
 |         this->init(&gGlobalAddress, sharedID, sizeof(fValue)); | 
 |     } | 
 | }; | 
 | struct TestingRec : public SkResourceCache::Rec { | 
 |     TestingRec(const TestingKey& key, uint32_t value) : fKey(key), fValue(value) {} | 
 |  | 
 |     TestingKey  fKey; | 
 |     intptr_t    fValue; | 
 |  | 
 |     const Key& getKey() const override { return fKey; } | 
 |     size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); } | 
 |     const char* getCategory() const override { return "test_cache"; } | 
 |     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return NULL; } | 
 |  | 
 |     static bool Visitor(const SkResourceCache::Rec& baseRec, void* context) { | 
 |         const TestingRec& rec = static_cast<const TestingRec&>(baseRec); | 
 |         intptr_t* result = (intptr_t*)context; | 
 |          | 
 |         *result = rec.fValue; | 
 |         return true; | 
 |     } | 
 | }; | 
 | } | 
 |  | 
 | static const int COUNT = 10; | 
 | static const int DIM = 256; | 
 |  | 
 | static void test_cache(skiatest::Reporter* reporter, SkResourceCache& cache, bool testPurge) { | 
 |     for (int i = 0; i < COUNT; ++i) { | 
 |         TestingKey key(i); | 
 |         intptr_t value = -1; | 
 |  | 
 |         REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value)); | 
 |         REPORTER_ASSERT(reporter, -1 == value); | 
 |  | 
 |         cache.add(new TestingRec(key, i)); | 
 |  | 
 |         REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value)); | 
 |         REPORTER_ASSERT(reporter, i == value); | 
 |     } | 
 |  | 
 |     if (testPurge) { | 
 |         // stress test, should trigger purges | 
 |         for (int i = 0; i < COUNT * 100; ++i) { | 
 |             TestingKey key(i); | 
 |             cache.add(new TestingRec(key, i)); | 
 |         } | 
 |     } | 
 |  | 
 |     // test the originals after all that purging | 
 |     for (int i = 0; i < COUNT; ++i) { | 
 |         intptr_t value; | 
 |         (void)cache.find(TestingKey(i), TestingRec::Visitor, &value); | 
 |     } | 
 |  | 
 |     cache.setTotalByteLimit(0); | 
 | } | 
 |  | 
 | static void test_cache_purge_shared_id(skiatest::Reporter* reporter, SkResourceCache& cache) { | 
 |     for (int i = 0; i < COUNT; ++i) { | 
 |         TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID | 
 |         cache.add(new TestingRec(key, i)); | 
 |     } | 
 |  | 
 |     // Ensure that everyone is present | 
 |     for (int i = 0; i < COUNT; ++i) { | 
 |         TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID | 
 |         intptr_t value = -1; | 
 |  | 
 |         REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value)); | 
 |         REPORTER_ASSERT(reporter, value == i); | 
 |     } | 
 |  | 
 |     // Now purge the ones that had a non-zero sharedID (the odd-indexed ones) | 
 |     cache.purgeSharedID(1); | 
 |  | 
 |     // Ensure that only the even ones are still present | 
 |     for (int i = 0; i < COUNT; ++i) { | 
 |         TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID | 
 |         intptr_t value = -1; | 
 |  | 
 |         if (i & 1) { | 
 |             REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value)); | 
 |         } else { | 
 |             REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value)); | 
 |             REPORTER_ASSERT(reporter, value == i); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #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 | 
 |  | 
 |     { | 
 |         SkResourceCache cache(defLimit); | 
 |         test_cache(reporter, cache, true); | 
 |     } | 
 |     { | 
 |         SkAutoTUnref<SkDiscardableMemoryPool> pool( | 
 |                 SkDiscardableMemoryPool::Create(defLimit, NULL)); | 
 |         gPool = pool.get(); | 
 |         SkResourceCache cache(pool_factory); | 
 |         test_cache(reporter, cache, true); | 
 |     } | 
 |     { | 
 |         SkResourceCache cache(SkDiscardableMemory::Create); | 
 |         test_cache(reporter, cache, false); | 
 |     } | 
 |     { | 
 |         SkResourceCache cache(defLimit); | 
 |         test_cache_purge_shared_id(reporter, cache); | 
 |     } | 
 | } | 
 |  | 
 | DEF_TEST(ImageCache_doubleAdd, r) { | 
 |     // Adding the same key twice should be safe. | 
 |     SkResourceCache cache(4096); | 
 |  | 
 |     TestingKey key(1); | 
 |  | 
 |     cache.add(new TestingRec(key, 2)); | 
 |     cache.add(new TestingRec(key, 3)); | 
 |  | 
 |     // Lookup can return either value. | 
 |     intptr_t value = -1; | 
 |     REPORTER_ASSERT(r, cache.find(key, TestingRec::Visitor, &value)); | 
 |     REPORTER_ASSERT(r, 2 == value || 3 == value); | 
 | } |