1 /*
2  * Copyright (C) 2016 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 package com.android.documentsui;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertSame;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.content.ComponentCallbacks2;
25 import android.graphics.Bitmap;
26 import android.graphics.Point;
27 import android.net.Uri;
28 
29 import androidx.test.filters.SmallTest;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import com.android.documentsui.ThumbnailCache.Result;
33 import com.android.documentsui.base.UserId;
34 import com.android.documentsui.testing.Bitmaps;
35 
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 
40 @RunWith(AndroidJUnit4.class)
41 @SmallTest
42 public class ThumbnailCacheTest {
43 
44     private static final Uri URI_0 = Uri.parse("content://authority/document/0");
45     private static final Uri URI_1 = Uri.parse("content://authority/document/1");
46 
47     private static final UserId USER_ID_0 = UserId.of(0);
48     private static final UserId USER_ID_1 = UserId.of(1);
49 
50     private static final Point SMALL_SIZE = new Point(1, 1);
51     private static final Point MID_SIZE = new Point(2, 2);
52     private static final Point LARGE_SIZE = new Point(3, 3);
53 
54     private static final Bitmap SMALL_BITMAP = Bitmaps.createTestBitmap(1, 1);
55     private static final Bitmap MIDSIZE_BITMAP = Bitmaps.createTestBitmap(2, 2);
56     private static final Bitmap LARGE_BITMAP = Bitmaps.createTestBitmap(3, 3);
57 
58     private static final long LAST_MODIFIED = 100;
59 
60     private static final int CACHE_SIZE_LIMIT =
61             MIDSIZE_BITMAP.getByteCount() + LARGE_BITMAP.getByteCount();
62 
63     private ThumbnailCache mCache;
64 
65     @Before
setUp()66     public void setUp() {
67         mCache = new ThumbnailCache(CACHE_SIZE_LIMIT);
68     }
69 
70     @Test
testMiss()71     public void testMiss() {
72         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
73 
74         Result result = mCache.getThumbnail(URI_1, USER_ID_0, MID_SIZE);
75 
76         assertMiss(result);
77     }
78 
79     @Test
testMiss_DifferentUser()80     public void testMiss_DifferentUser() {
81         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
82         Result result = mCache.getThumbnail(URI_0, USER_ID_1, MID_SIZE);
83         assertMiss(result);
84     }
85 
86     @Test
testHit_Exact()87     public void testHit_Exact() {
88         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
89 
90         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
91 
92         assertHitExact(result);
93         assertSame(MIDSIZE_BITMAP, result.getThumbnail());
94     }
95 
96     @Test
testHit_Smaller()97     public void testHit_Smaller() {
98         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
99 
100         Result result = mCache.getThumbnail(URI_0, USER_ID_0, LARGE_SIZE);
101 
102         assertHitSmaller(result);
103         assertSame(MIDSIZE_BITMAP, result.getThumbnail());
104     }
105 
106     @Test
testHit_Larger()107     public void testHit_Larger() {
108         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
109 
110         Result result = mCache.getThumbnail(URI_0, USER_ID_0, SMALL_SIZE);
111 
112         assertHitLarger(result);
113         assertSame(MIDSIZE_BITMAP, result.getThumbnail());
114     }
115 
116     @Test
testHit_Larger_HasBothSize()117     public void testHit_Larger_HasBothSize() {
118         mCache.putThumbnail(URI_0, USER_ID_0, LARGE_SIZE, LARGE_BITMAP, LAST_MODIFIED);
119         mCache.putThumbnail(URI_0, USER_ID_0, SMALL_SIZE, SMALL_BITMAP, LAST_MODIFIED);
120 
121         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
122 
123         assertHitLarger(result);
124         assertSame(LARGE_BITMAP, result.getThumbnail());
125     }
126 
127     @Test
testHit_Exact_MultiplePut()128     public void testHit_Exact_MultiplePut() {
129         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
130 
131         Bitmap localBitmap = Bitmaps.createTestBitmap(MID_SIZE.x, MID_SIZE.y);
132         long localLastModified = LAST_MODIFIED + 100;
133         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, localBitmap, localLastModified);
134 
135         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
136 
137         assertHitExact(result);
138         assertSame(localBitmap, result.getThumbnail());
139     }
140 
141     @Test
testHit_EqualLastModified()142     public void testHit_EqualLastModified() {
143         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
144 
145         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
146 
147         assertEquals(LAST_MODIFIED, result.getLastModified());
148     }
149 
150     @Test
testEvictOldest_SizeExceeded()151     public void testEvictOldest_SizeExceeded() {
152         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
153         mCache.putThumbnail(URI_1, USER_ID_0, SMALL_SIZE, SMALL_BITMAP, LAST_MODIFIED);
154         mCache.putThumbnail(URI_1, USER_ID_0, LARGE_SIZE, LARGE_BITMAP, LAST_MODIFIED);
155 
156         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
157 
158         assertMiss(result);
159     }
160 
161     @Test
testCacheShrink_OnTrimMemory_Moderate()162     public void testCacheShrink_OnTrimMemory_Moderate() {
163         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
164         mCache.putThumbnail(URI_0, USER_ID_0, SMALL_SIZE, SMALL_BITMAP, LAST_MODIFIED);
165         mCache.putThumbnail(URI_0, USER_ID_0, LARGE_SIZE, LARGE_BITMAP, LAST_MODIFIED);
166 
167         mCache.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
168 
169         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
170         assertMiss(result);
171     }
172 
173     @Test
testCacheShrink_OnTrimMemory_Background()174     public void testCacheShrink_OnTrimMemory_Background() {
175         mCache.putThumbnail(URI_0, USER_ID_0, LARGE_SIZE, LARGE_BITMAP, LAST_MODIFIED);
176         mCache.putThumbnail(URI_0, USER_ID_0, SMALL_SIZE, SMALL_BITMAP, LAST_MODIFIED);
177 
178         mCache.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
179 
180         // Math here (size of each pixel omitted):
181         // Limit = midSize + largeSize = 2 * 2 + 3 * 3 = 13, so after all putThumbnail the cache is
182         // full.
183         //
184         // HalfLimit = Limit / 2 = 5. After evicting largeSize bitmap, cache size decreases to 4,
185         // which is smaller than 5. Then smallSize bitmap remains.
186         Result result = mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE);
187         assertHitSmaller(result);
188         assertSame(SMALL_BITMAP, result.getThumbnail());
189     }
190 
191     @Test
testRemoveUri()192     public void testRemoveUri() {
193         mCache.putThumbnail(URI_0, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
194         mCache.putThumbnail(URI_0, USER_ID_0, SMALL_SIZE, SMALL_BITMAP, LAST_MODIFIED);
195         mCache.putThumbnail(URI_1, USER_ID_0, MID_SIZE, MIDSIZE_BITMAP, LAST_MODIFIED);
196 
197         mCache.removeUri(URI_0, USER_ID_0);
198 
199         assertMiss(mCache.getThumbnail(URI_0, USER_ID_0, MID_SIZE));
200         assertHitExact(mCache.getThumbnail(URI_1, USER_ID_0, MID_SIZE));
201     }
202 
assertMiss(Result result)203     private static void assertMiss(Result result) {
204         assertEquals(Result.CACHE_MISS, result.getStatus());
205         assertFalse(result.isExactHit());
206         assertFalse(result.isHit());
207     }
208 
assertHitExact(Result result)209     private static void assertHitExact(Result result) {
210         assertEquals(Result.CACHE_HIT_EXACT, result.getStatus());
211         assertTrue(result.isExactHit());
212         assertTrue(result.isHit());
213     }
214 
assertHitSmaller(Result result)215     private static void assertHitSmaller(Result result) {
216         assertEquals(Result.CACHE_HIT_SMALLER, result.getStatus());
217         assertFalse(result.isExactHit());
218         assertTrue(result.isHit());
219     }
220 
assertHitLarger(Result result)221     private static void assertHitLarger(Result result) {
222         assertEquals(Result.CACHE_HIT_LARGER, result.getStatus());
223         assertFalse(result.isExactHit());
224         assertTrue(result.isHit());
225     }
226 }
227