1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cutils/properties.h>
18 #include <dirent.h>
19 #include <errno.h>
20 #include <gtest/gtest.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <utils/Log.h>
25 #include <cstdint>
26 #include "FileBlobCache.h"
27 #include "pipeline/skia/ShaderCache.h"
28 
29 using namespace android::uirenderer::skiapipeline;
30 
31 namespace android {
32 namespace uirenderer {
33 namespace skiapipeline {
34 
35 class ShaderCacheTestUtils {
36 public:
37     /**
38      * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
39      * If set to 0, then deferred save is disabled.
40      */
setSaveDelay(ShaderCache & cache,unsigned int saveDelay)41     static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
42         cache.mDeferredSaveDelay = saveDelay;
43     }
44 
45     /**
46      * "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
47      * Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
48      */
terminate(ShaderCache & cache,bool saveContent)49     static void terminate(ShaderCache& cache, bool saveContent) {
50         std::lock_guard<std::mutex> lock(cache.mMutex);
51         cache.mSavePending = saveContent;
52         cache.saveToDiskLocked();
53         cache.mBlobCache = NULL;
54     }
55 
56     /**
57      *
58      */
59     template <typename T>
validateCache(ShaderCache & cache,std::vector<T> hash)60     static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
61         return cache.validateCache(hash.data(), hash.size() * sizeof(T));
62     }
63 };
64 
65 } /* namespace skiapipeline */
66 } /* namespace uirenderer */
67 } /* namespace android */
68 
69 namespace {
70 
getExternalStorageFolder()71 std::string getExternalStorageFolder() {
72     return getenv("EXTERNAL_STORAGE");
73 }
74 
folderExist(const std::string & folderName)75 bool folderExist(const std::string& folderName) {
76     DIR* dir = opendir(folderName.c_str());
77     if (dir) {
78         closedir(dir);
79         return true;
80     }
81     return false;
82 }
83 
checkShader(const sk_sp<SkData> & shader1,const sk_sp<SkData> & shader2)84 inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
85     return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
86            0 == memcmp(shader1->data(), shader2->data(), shader1->size());
87 }
88 
checkShader(const sk_sp<SkData> & shader,const char * program)89 inline bool checkShader(const sk_sp<SkData>& shader, const char* program) {
90     sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
91     return checkShader(shader, shader2);
92 }
93 
94 template <typename T>
checkShader(const sk_sp<SkData> & shader,std::vector<T> & program)95 bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
96     sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
97     return checkShader(shader, shader2);
98 }
99 
setShader(sk_sp<SkData> & shader,const char * program)100 void setShader(sk_sp<SkData>& shader, const char* program) {
101     shader = SkData::MakeWithCString(program);
102 }
103 
104 template <typename T>
setShader(sk_sp<SkData> & shader,std::vector<T> & buffer)105 void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
106     shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
107 }
108 
109 template <typename T>
genRandomData(std::vector<T> & buffer)110 void genRandomData(std::vector<T>& buffer) {
111     for (auto& data : buffer) {
112         data = T(std::rand());
113     }
114 }
115 
116 #define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
117 
TEST(ShaderCacheTest,testWriteAndRead)118 TEST(ShaderCacheTest, testWriteAndRead) {
119     if (!folderExist(getExternalStorageFolder())) {
120         // don't run the test if external storage folder is not available
121         return;
122     }
123     std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
124     std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
125 
126     // remove any test files from previous test run
127     int deleteFile = remove(cacheFile1.c_str());
128     ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
129     std::srand(0);
130 
131     // read the cache from a file that does not exist
132     ShaderCache::get().setFilename(cacheFile1.c_str());
133     ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
134     ShaderCache::get().initShaderDiskCache();
135 
136     // read a key - should not be found since the cache is empty
137     sk_sp<SkData> outVS;
138     ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
139 
140     // write to the in-memory cache without storing on disk and verify we read the same values
141     sk_sp<SkData> inVS;
142     setShader(inVS, "sassas");
143     ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
144     setShader(inVS, "someVS");
145     ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
146     ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
147     ASSERT_TRUE(checkShader(outVS, "sassas"));
148     ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
149     ASSERT_TRUE(checkShader(outVS, "someVS"));
150 
151     // store content to disk and release in-memory cache
152     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
153 
154     // change to a file that does not exist and verify load fails
155     ShaderCache::get().setFilename(cacheFile2.c_str());
156     ShaderCache::get().initShaderDiskCache();
157     ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
158     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
159 
160     // load again content from disk from an existing file and check the data is read correctly
161     ShaderCache::get().setFilename(cacheFile1.c_str());
162     ShaderCache::get().initShaderDiskCache();
163     sk_sp<SkData> outVS2;
164     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
165     ASSERT_TRUE(checkShader(outVS2, "someVS"));
166 
167     // change data, store to disk, read back again and verify data has been changed
168     setShader(inVS, "ewData1");
169     ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
170     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
171     ShaderCache::get().initShaderDiskCache();
172     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
173     ASSERT_TRUE(checkShader(outVS2, "ewData1"));
174 
175     // write and read big data chunk (50K)
176     size_t dataSize = 50 * 1024;
177     std::vector<uint8_t> dataBuffer(dataSize);
178     genRandomData(dataBuffer);
179     setShader(inVS, dataBuffer);
180     ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
181     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
182     ShaderCache::get().initShaderDiskCache();
183     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
184     ASSERT_TRUE(checkShader(outVS2, dataBuffer));
185 
186     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
187     remove(cacheFile1.c_str());
188 }
189 
TEST(ShaderCacheTest,testCacheValidation)190 TEST(ShaderCacheTest, testCacheValidation) {
191     if (!folderExist(getExternalStorageFolder())) {
192         // don't run the test if external storage folder is not available
193         return;
194     }
195     std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
196     std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
197 
198     // remove any test files from previous test run
199     int deleteFile = remove(cacheFile1.c_str());
200     ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
201     std::srand(0);
202 
203     // generate identity and read the cache from a file that does not exist
204     ShaderCache::get().setFilename(cacheFile1.c_str());
205     ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
206     std::vector<uint8_t> identity(1024);
207     genRandomData(identity);
208     ShaderCache::get().initShaderDiskCache(
209             identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
210 
211     // generate random content in cache and store to disk
212     constexpr size_t numBlob(10);
213     constexpr size_t keySize(1024);
214     constexpr size_t dataSize(50 * 1024);
215 
216     std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob);
217     for (auto& blob : blobVec) {
218         std::vector<uint8_t> keyBuffer(keySize);
219         std::vector<uint8_t> dataBuffer(dataSize);
220         genRandomData(keyBuffer);
221         genRandomData(dataBuffer);
222 
223         sk_sp<SkData> key, data;
224         setShader(key, keyBuffer);
225         setShader(data, dataBuffer);
226 
227         blob = std::make_pair(key, data);
228         ShaderCache::get().store(*key.get(), *data.get());
229     }
230     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
231 
232     // change to a file that does not exist and verify validation fails
233     ShaderCache::get().setFilename(cacheFile2.c_str());
234     ShaderCache::get().initShaderDiskCache();
235     ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
236     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
237 
238     // restore the original file and verify validation succeeds
239     ShaderCache::get().setFilename(cacheFile1.c_str());
240     ShaderCache::get().initShaderDiskCache(
241             identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
242     ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
243     for (const auto& blob : blobVec) {
244         auto outVS = ShaderCache::get().load(*blob.first.get());
245         ASSERT_TRUE(checkShader(outVS, blob.second));
246     }
247 
248     // generate error identity and verify load fails
249     ShaderCache::get().initShaderDiskCache(identity.data(), -1);
250     for (const auto& blob : blobVec) {
251         ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
252     }
253     ShaderCache::get().initShaderDiskCache(
254             nullptr, identity.size() * sizeof(decltype(identity)::value_type));
255     for (const auto& blob : blobVec) {
256         ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
257     }
258 
259     // verify the cache validation again after load fails
260     ShaderCache::get().initShaderDiskCache(
261             identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
262     ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
263     for (const auto& blob : blobVec) {
264         auto outVS = ShaderCache::get().load(*blob.first.get());
265         ASSERT_TRUE(checkShader(outVS, blob.second));
266     }
267 
268     // generate another identity and verify load fails
269     for (auto& data : identity) {
270         data += std::rand();
271     }
272     ShaderCache::get().initShaderDiskCache(
273             identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
274     for (const auto& blob : blobVec) {
275         ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
276     }
277 
278     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
279     remove(cacheFile1.c_str());
280 }
281 
282 }  // namespace
283