xref: /aosp_15_r20/frameworks/base/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 package com.android.systemui.graphics
2 
3 import android.content.res.Resources
4 import android.graphics.Bitmap
5 import android.graphics.BitmapFactory
6 import android.graphics.ImageDecoder
7 import android.graphics.drawable.BitmapDrawable
8 import android.graphics.drawable.Drawable
9 import android.graphics.drawable.Icon
10 import android.graphics.drawable.VectorDrawable
11 import android.net.Uri
12 import android.util.Size
13 import androidx.test.ext.junit.runners.AndroidJUnit4
14 import androidx.test.filters.SmallTest
15 import com.android.systemui.res.R
16 import com.android.systemui.SysuiTestCase
17 import com.google.common.truth.Truth.assertThat
18 import java.io.ByteArrayInputStream
19 import java.io.ByteArrayOutputStream
20 import java.io.File
21 import java.io.FileInputStream
22 import java.io.FileOutputStream
23 import kotlinx.coroutines.test.TestScope
24 import kotlinx.coroutines.test.UnconfinedTestDispatcher
25 import kotlinx.coroutines.test.runTest
26 import org.junit.After
27 import org.junit.Before
28 import org.junit.Test
29 import org.junit.runner.RunWith
30 
31 @SmallTest
32 @kotlinx.coroutines.ExperimentalCoroutinesApi
33 @RunWith(AndroidJUnit4::class)
34 class ImageLoaderTest : SysuiTestCase() {
35 
36     private val testDispatcher = UnconfinedTestDispatcher()
37     private val testScope = TestScope(testDispatcher)
38     private val imageLoader = ImageLoader(context, testDispatcher)
39 
40     private lateinit var imgFile: File
41 
42     @Before
setUpnull43     fun setUp() {
44         val context = context.createPackageContext("com.android.systemui.tests", 0)
45         val bitmap =
46             BitmapFactory.decodeResource(
47                 context.resources,
48                 com.android.systemui.tests.R.drawable.romainguy_rockaway
49             )
50 
51         imgFile = File.createTempFile("image", ".png", context.cacheDir)
52         imgFile.deleteOnExit()
53         bitmap.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(imgFile))
54     }
55 
56     @After
tearDownnull57     fun tearDown() {
58         imgFile.delete()
59     }
60 
61     @Test
invalidResource_drawable_returnsNullnull62     fun invalidResource_drawable_returnsNull() =
63         testScope.runTest { assertThat(imageLoader.loadDrawable(ImageLoader.Res(-1))).isNull() }
64 
65     @Test
invalidResource_bitmap_returnsNullnull66     fun invalidResource_bitmap_returnsNull() =
67         testScope.runTest { assertThat(imageLoader.loadBitmap(ImageLoader.Res(-1))).isNull() }
68 
69     @Test
invalidUri_returnsNullnull70     fun invalidUri_returnsNull() =
71         testScope.runTest {
72             assertThat(imageLoader.loadBitmap(ImageLoader.Uri("this.is/bogus"))).isNull()
73         }
74 
75     @Test
invalidFile_returnsNullnull76     fun invalidFile_returnsNull() =
77         testScope.runTest {
78             assertThat(imageLoader.loadBitmap(ImageLoader.File("this is broken!"))).isNull()
79         }
80 
81     @Test
invalidIcon_loadDrawable_returnsNullnull82     fun invalidIcon_loadDrawable_returnsNull() =
83         testScope.runTest {
84             assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
85         }
86 
87     @Test
invalidIcon_loadSize_returnsNullnull88     fun invalidIcon_loadSize_returnsNull() =
89         testScope.runTest {
90             assertThat(imageLoader.loadSize(Icon.createWithFilePath("this is broken"), context))
91                 .isNull()
92         }
93 
94     @Test
invalidIS_returnsNullnull95     fun invalidIS_returnsNull() =
96         testScope.runTest {
97             assertThat(
98                     imageLoader.loadDrawable(
99                         ImageLoader.InputStream(ByteArrayInputStream(ByteArray(0)))
100                     )
101                 )
102                 .isNull()
103         }
104 
105     @Test
validBitmapResource_loadDrawable_returnsBitmapDrawablenull106     fun validBitmapResource_loadDrawable_returnsBitmapDrawable() =
107         testScope.runTest {
108             val context = context.createPackageContext("com.android.systemui.tests", 0)
109             val bitmap =
110                 BitmapFactory.decodeResource(
111                     context.resources,
112                     com.android.systemui.tests.R.drawable.romainguy_rockaway
113                 )
114             assertThat(bitmap).isNotNull()
115             val loadedDrawable =
116                 imageLoader.loadDrawable(
117                     ImageLoader.Res(
118                         com.android.systemui.tests.R.drawable.romainguy_rockaway,
119                         context
120                     )
121                 )
122             assertBitmapEqualToDrawable(loadedDrawable, bitmap)
123         }
124 
125     @Test
validBitmapResource_loadBitmap_returnsBitmapDrawablenull126     fun validBitmapResource_loadBitmap_returnsBitmapDrawable() =
127         testScope.runTest {
128             val bitmap =
129                 BitmapFactory.decodeResource(
130                     context.resources,
131                     R.drawable.dessert_zombiegingerbread
132                 )
133             val loadedBitmap =
134                 imageLoader.loadBitmap(ImageLoader.Res(R.drawable.dessert_zombiegingerbread))
135             assertBitmapEqualToBitmap(loadedBitmap, bitmap)
136         }
137 
138     @Test
validBitmapUri_returnsBitmapDrawablenull139     fun validBitmapUri_returnsBitmapDrawable() =
140         testScope.runTest {
141             val bitmap =
142                 BitmapFactory.decodeResource(
143                     context.resources,
144                     R.drawable.dessert_zombiegingerbread
145                 )
146 
147             val uri =
148                 "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
149             val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Uri(uri))
150             assertBitmapEqualToBitmap(loadedBitmap, bitmap)
151         }
152 
153     @Test
validBitmapFile_returnsBitmapDrawablenull154     fun validBitmapFile_returnsBitmapDrawable() =
155         testScope.runTest {
156             val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
157             val loadedBitmap = imageLoader.loadBitmap(ImageLoader.File(imgFile))
158             assertBitmapEqualToBitmap(loadedBitmap, bitmap)
159         }
160 
161     @Test
validInputStream_returnsBitmapDrawablenull162     fun validInputStream_returnsBitmapDrawable() =
163         testScope.runTest {
164             val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
165             val loadedBitmap =
166                 imageLoader.loadBitmap(ImageLoader.InputStream(FileInputStream(imgFile)))
167             assertBitmapEqualToBitmap(loadedBitmap, bitmap)
168         }
169 
170     @Test
validBitmapIcon_returnsBitmapDrawablenull171     fun validBitmapIcon_returnsBitmapDrawable() =
172         testScope.runTest {
173             val bitmap =
174                 BitmapFactory.decodeResource(
175                     context.resources,
176                     R.drawable.dessert_zombiegingerbread
177                 )
178             val loadedDrawable = imageLoader.loadDrawable(Icon.createWithBitmap(bitmap))
179             assertBitmapEqualToDrawable(loadedDrawable, bitmap)
180         }
181 
182     @Test
validBitmapIcon_loadSize_returnsNullnull183     fun validBitmapIcon_loadSize_returnsNull() =
184         testScope.runTest {
185             val bitmap =
186                 BitmapFactory.decodeResource(
187                     context.resources,
188                     R.drawable.dessert_zombiegingerbread
189                 )
190             assertThat(imageLoader.loadSize(Icon.createWithBitmap(bitmap), context)).isNull()
191         }
192 
193     @Test
validUriIcon_returnsBitmapDrawablenull194     fun validUriIcon_returnsBitmapDrawable() =
195         testScope.runTest {
196             val bitmap =
197                 BitmapFactory.decodeResource(
198                     context.resources,
199                     R.drawable.dessert_zombiegingerbread
200                 )
201             val uri =
202                 "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
203             val loadedDrawable = imageLoader.loadDrawable(Icon.createWithContentUri(Uri.parse(uri)))
204             assertBitmapEqualToDrawable(loadedDrawable, bitmap)
205         }
206 
207     @Test
validUriIcon_returnsSizenull208     fun validUriIcon_returnsSize() =
209         testScope.runTest {
210             val drawable = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
211             val uri =
212                 "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
213             val loadedSize =
214                 imageLoader.loadSize(Icon.createWithContentUri(Uri.parse(uri)), context)
215             assertSizeEqualToDrawableSize(loadedSize, drawable)
216         }
217 
218     @Test
validDataIcon_returnsBitmapDrawablenull219     fun validDataIcon_returnsBitmapDrawable() =
220         testScope.runTest {
221             val bitmap =
222                 BitmapFactory.decodeResource(
223                     context.resources,
224                     R.drawable.dessert_zombiegingerbread
225                 )
226             val bos =
227                 ByteArrayOutputStream(
228                     bitmap.byteCount * 2
229                 ) // Compressed bitmap should be smaller than its source.
230             bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
231 
232             val array = bos.toByteArray()
233             val loadedDrawable = imageLoader.loadDrawable(Icon.createWithData(array, 0, array.size))
234             assertBitmapEqualToDrawable(loadedDrawable, bitmap)
235         }
236 
237     @Test
validDataIcon_loadSize_returnsNullnull238     fun validDataIcon_loadSize_returnsNull() =
239         testScope.runTest {
240             val bitmap =
241                 BitmapFactory.decodeResource(
242                     context.resources,
243                     R.drawable.dessert_zombiegingerbread
244                 )
245             val bos =
246                 ByteArrayOutputStream(
247                     bitmap.byteCount * 2
248                 ) // Compressed bitmap should be smaller than its source.
249             bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
250 
251             val array = bos.toByteArray()
252             assertThat(imageLoader.loadSize(Icon.createWithData(array, 0, array.size), context))
253                 .isNull()
254         }
255 
256     @Test
validResourceIcon_returnsBitmapDrawablenull257     fun validResourceIcon_returnsBitmapDrawable() =
258         testScope.runTest {
259             val bitmap = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
260             val loadedDrawable =
261                 imageLoader.loadDrawable(
262                     Icon.createWithResource(
263                         "com.android.systemui.tests",
264                         R.drawable.dessert_zombiegingerbread
265                     )
266                 )
267             assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
268         }
269 
270     @Test
validResourceIcon_loadSize_returnsNullnull271     fun validResourceIcon_loadSize_returnsNull() =
272         testScope.runTest {
273             assertThat(
274                     imageLoader.loadSize(
275                         Icon.createWithResource(
276                             "com.android.systemui.tests",
277                             R.drawable.dessert_zombiegingerbread
278                         ),
279                         context
280                     )
281                 )
282                 .isNull()
283         }
284 
285     @Test
validSystemResourceIcon_returnsBitmapDrawablenull286     fun validSystemResourceIcon_returnsBitmapDrawable() =
287         testScope.runTest {
288             val bitmap =
289                 Resources.getSystem().getDrawable(android.R.drawable.ic_dialog_alert, context.theme)
290             val loadedDrawable =
291                 imageLoader.loadDrawable(
292                     Icon.createWithResource("android", android.R.drawable.ic_dialog_alert)
293                 )
294             assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
295         }
296 
297     @Test
validSystemResourceIcon_loadSize_returnsNullnull298     fun validSystemResourceIcon_loadSize_returnsNull() =
299         testScope.runTest {
300             assertThat(
301                     imageLoader.loadSize(
302                         Icon.createWithResource("android", android.R.drawable.ic_dialog_alert),
303                         context
304                     )
305                 )
306                 .isNull()
307         }
308 
309     @Test
invalidDifferentPackageResourceIcon_returnsNullnull310     fun invalidDifferentPackageResourceIcon_returnsNull() =
311         testScope.runTest {
312             val loadedDrawable =
313                 imageLoader.loadDrawable(
314                     Icon.createWithResource(
315                         "noooope.wrong.package",
316                         R.drawable.dessert_zombiegingerbread
317                     )
318                 )
319             assertThat(loadedDrawable).isNull()
320         }
321 
322     @Test
invalidDifferentPackageResourceIcon_loadSize_returnsNullnull323     fun invalidDifferentPackageResourceIcon_loadSize_returnsNull() =
324         testScope.runTest {
325             assertThat(
326                     imageLoader.loadDrawable(
327                         Icon.createWithResource(
328                             "noooope.wrong.package",
329                             R.drawable.dessert_zombiegingerbread
330                         )
331                     )
332                 )
333                 .isNull()
334         }
335 
336     @Test
validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRationull337     fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
338         testScope.runTest {
339             val loadedDrawable =
340                 imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 160)
341             val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
342             assertThat(loadedBitmap.width).isEqualTo(160)
343             assertThat(loadedBitmap.height).isEqualTo(106)
344         }
345 
346     @Test
validBitmapResource_heightMoreRestricted_downsizesKeepingAspectRationull347     fun validBitmapResource_heightMoreRestricted_downsizesKeepingAspectRatio() =
348         testScope.runTest {
349             val loadedDrawable =
350                 imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 50)
351             val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
352             assertThat(loadedBitmap.width).isEqualTo(74)
353             assertThat(loadedBitmap.height).isEqualTo(50)
354         }
355 
356     @Test
validBitmapResource_onlyWidthRestricted_downsizesKeepingAspectRationull357     fun validBitmapResource_onlyWidthRestricted_downsizesKeepingAspectRatio() =
358         testScope.runTest {
359             val loadedDrawable =
360                 imageLoader.loadDrawable(
361                     ImageLoader.File(imgFile),
362                     maxWidth = 160,
363                     maxHeight = ImageLoader.DO_NOT_RESIZE
364                 )
365             val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
366             assertThat(loadedBitmap.width).isEqualTo(160)
367             assertThat(loadedBitmap.height).isEqualTo(106)
368         }
369 
370     @Test
validBitmapResource_onlyHeightRestricted_downsizesKeepingAspectRationull371     fun validBitmapResource_onlyHeightRestricted_downsizesKeepingAspectRatio() =
372         testScope.runTest {
373             val loadedDrawable =
374                 imageLoader.loadDrawable(
375                     ImageLoader.Res(R.drawable.bubble_thumbnail),
376                     maxWidth = ImageLoader.DO_NOT_RESIZE,
377                     maxHeight = 120
378                 )
379             val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
380             assertThat(loadedBitmap.width).isEqualTo(123)
381             assertThat(loadedBitmap.height).isEqualTo(120)
382         }
383 
384     @Test
validVectorDrawable_loadDrawable_successfullyLoadednull385     fun validVectorDrawable_loadDrawable_successfullyLoaded() =
386         testScope.runTest {
387             val loadedDrawable = imageLoader.loadDrawable(ImageLoader.Res(R.drawable.ic_settings))
388             assertThat(loadedDrawable).isNotNull()
389             assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
390         }
391 
392     @Test
validVectorDrawable_loadBitmap_returnsNullnull393     fun validVectorDrawable_loadBitmap_returnsNull() =
394         testScope.runTest {
395             val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Res(R.drawable.ic_settings))
396             assertThat(loadedBitmap).isNull()
397         }
398 
399     @Test
validVectorDrawableIcon_loadDrawable_successfullyLoadednull400     fun validVectorDrawableIcon_loadDrawable_successfullyLoaded() =
401         testScope.runTest {
402             val loadedDrawable =
403                 imageLoader.loadDrawable(Icon.createWithResource(context, R.drawable.ic_settings))
404             assertThat(loadedDrawable).isNotNull()
405             assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
406         }
407 
408     @Test
hardwareAllocator_returnsHardwareBitmapnull409     fun hardwareAllocator_returnsHardwareBitmap() =
410         testScope.runTest {
411             val loadedDrawable =
412                 imageLoader.loadDrawable(
413                     ImageLoader.File(imgFile),
414                     allocator = ImageDecoder.ALLOCATOR_HARDWARE
415                 )
416             assertThat(loadedDrawable).isNotNull()
417             assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
418                 .isEqualTo(Bitmap.Config.HARDWARE)
419         }
420 
421     @Test
softwareAllocator_returnsSoftwareBitmapnull422     fun softwareAllocator_returnsSoftwareBitmap() =
423         testScope.runTest {
424             val loadedDrawable =
425                 imageLoader.loadDrawable(
426                     ImageLoader.File(imgFile),
427                     allocator = ImageDecoder.ALLOCATOR_SOFTWARE
428                 )
429             assertThat(loadedDrawable).isNotNull()
430             assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
431                 .isNotEqualTo(Bitmap.Config.HARDWARE)
432         }
433 
assertBitmapInDrawablenull434     private fun assertBitmapInDrawable(drawable: Drawable?): Bitmap {
435         assertThat(drawable).isNotNull()
436         assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
437         return (drawable as BitmapDrawable).bitmap
438     }
439 
assertBitmapEqualToDrawablenull440     private fun assertBitmapEqualToDrawable(actual: Drawable?, expected: Bitmap) {
441         val actualBitmap = assertBitmapInDrawable(actual)
442         assertBitmapEqualToBitmap(actualBitmap, expected)
443     }
444 
assertBitmapEqualToBitmapnull445     private fun assertBitmapEqualToBitmap(actual: Bitmap?, expected: Bitmap) {
446         assertThat(actual).isNotNull()
447         assertThat(actual?.width).isEqualTo(expected.width)
448         assertThat(actual?.height).isEqualTo(expected.height)
449     }
450 
assertSizeEqualToDrawableSizenull451     private fun assertSizeEqualToDrawableSize(actual: Size?, expected: Drawable) {
452         assertThat(actual).isNotNull()
453         assertThat(actual?.width).isEqualTo(expected.intrinsicWidth)
454         assertThat(actual?.height).isEqualTo(expected.intrinsicHeight)
455     }
456 }
457