1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkDiscardableMemory.h"
9#include "SkResourceCache.h"
10#include "Test.h"
11
12namespace {
13static void* gGlobalAddress;
14struct TestingKey : public SkResourceCache::Key {
15    intptr_t    fValue;
16
17    TestingKey(intptr_t value, uint64_t sharedID = 0) : fValue(value) {
18        this->init(&gGlobalAddress, sharedID, sizeof(fValue));
19    }
20};
21struct TestingRec : public SkResourceCache::Rec {
22    TestingRec(const TestingKey& key, uint32_t value) : fKey(key), fValue(value) {}
23
24    TestingKey  fKey;
25    intptr_t    fValue;
26
27    const Key& getKey() const override { return fKey; }
28    size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); }
29    const char* getCategory() const override { return "test_cache"; }
30    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
31
32    static bool Visitor(const SkResourceCache::Rec& baseRec, void* context) {
33        const TestingRec& rec = static_cast<const TestingRec&>(baseRec);
34        intptr_t* result = (intptr_t*)context;
35
36        *result = rec.fValue;
37        return true;
38    }
39};
40}
41
42static const int COUNT = 10;
43static const int DIM = 256;
44
45static void test_cache(skiatest::Reporter* reporter, SkResourceCache& cache, bool testPurge) {
46    for (int i = 0; i < COUNT; ++i) {
47        TestingKey key(i);
48        intptr_t value = -1;
49
50        REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value));
51        REPORTER_ASSERT(reporter, -1 == value);
52
53        cache.add(new TestingRec(key, i));
54
55        REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
56        REPORTER_ASSERT(reporter, i == value);
57    }
58
59    if (testPurge) {
60        // stress test, should trigger purges
61        for (int i = 0; i < COUNT * 100; ++i) {
62            TestingKey key(i);
63            cache.add(new TestingRec(key, i));
64        }
65    }
66
67    // test the originals after all that purging
68    for (int i = 0; i < COUNT; ++i) {
69        intptr_t value;
70        (void)cache.find(TestingKey(i), TestingRec::Visitor, &value);
71    }
72
73    cache.setTotalByteLimit(0);
74}
75
76static void test_cache_purge_shared_id(skiatest::Reporter* reporter, SkResourceCache& cache) {
77    for (int i = 0; i < COUNT; ++i) {
78        TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
79        cache.add(new TestingRec(key, i));
80    }
81
82    // Ensure that everyone is present
83    for (int i = 0; i < COUNT; ++i) {
84        TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
85        intptr_t value = -1;
86
87        REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
88        REPORTER_ASSERT(reporter, value == i);
89    }
90
91    // Now purge the ones that had a non-zero sharedID (the odd-indexed ones)
92    cache.purgeSharedID(1);
93
94    // Ensure that only the even ones are still present
95    for (int i = 0; i < COUNT; ++i) {
96        TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
97        intptr_t value = -1;
98
99        if (i & 1) {
100            REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value));
101        } else {
102            REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
103            REPORTER_ASSERT(reporter, value == i);
104        }
105    }
106}
107
108#include "SkDiscardableMemoryPool.h"
109
110static SkDiscardableMemoryPool* gPool;
111static SkDiscardableMemory* pool_factory(size_t bytes) {
112    SkASSERT(gPool);
113    return gPool->create(bytes);
114}
115
116DEF_TEST(ImageCache, reporter) {
117    static const size_t defLimit = DIM * DIM * 4 * COUNT + 1024;    // 1K slop
118
119    {
120        SkResourceCache cache(defLimit);
121        test_cache(reporter, cache, true);
122    }
123    {
124        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(defLimit));
125        gPool = pool.get();
126        SkResourceCache cache(pool_factory);
127        test_cache(reporter, cache, true);
128    }
129    {
130        SkResourceCache cache(SkDiscardableMemory::Create);
131        test_cache(reporter, cache, false);
132    }
133    {
134        SkResourceCache cache(defLimit);
135        test_cache_purge_shared_id(reporter, cache);
136    }
137}
138
139DEF_TEST(ImageCache_doubleAdd, r) {
140    // Adding the same key twice should be safe.
141    SkResourceCache cache(4096);
142
143    TestingKey key(1);
144
145    cache.add(new TestingRec(key, 2));
146    cache.add(new TestingRec(key, 3));
147
148    // Lookup can return either value.
149    intptr_t value = -1;
150    REPORTER_ASSERT(r, cache.find(key, TestingRec::Visitor, &value));
151    REPORTER_ASSERT(r, 2 == value || 3 == value);
152}
153