package com.bumptech.glide.load.engine.bitmap_recycle; import static android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; import static android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE; import static android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.graphics.Bitmap; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowBitmap; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 18) public class LruBitmapPoolTest { private static final int MAX_SIZE = 10; private static final Set<Bitmap.Config> ALLOWED_CONFIGS = Collections.singleton(Bitmap.Config.ARGB_8888); private MockStrategy strategy; private LruBitmapPool pool; @Before public void setUp() throws Exception { strategy = new MockStrategy(); pool = new LruBitmapPool(MAX_SIZE, strategy, ALLOWED_CONFIGS); } @Test public void testICanAddAndGetABitmap() { fillPool(pool, 1); pool.put(createMutableBitmap()); assertNotNull(pool.get(100, 100, Bitmap.Config.ARGB_8888)); } @Test public void testImmutableBitmapsAreNotAdded() { Bitmap bitmap = createMutableBitmap(); Shadows.shadowOf(bitmap).setMutable(false); pool.put(bitmap); assertThat(strategy.bitmaps).isEmpty(); } @Test public void testItIsSizeLimited() { fillPool(pool, MAX_SIZE + 2); assertEquals(2, strategy.numRemoves); } @Test public void testBitmapLargerThanPoolIsNotAdded() { strategy = new MockStrategy() { @Override public int getSize(Bitmap bitmap) { return 4; } }; pool = new LruBitmapPool(3, strategy, ALLOWED_CONFIGS); pool.put(createMutableBitmap()); assertEquals(0, strategy.numRemoves); assertEquals(0, strategy.numPuts); } @Test public void testClearMemoryRemovesAllBitmaps() { fillPool(pool, MAX_SIZE); pool.clearMemory(); assertEquals(MAX_SIZE, strategy.numRemoves); } @Test public void testEvictedBitmapsAreRecycled() { fillPool(pool, MAX_SIZE); List<Bitmap> bitmaps = new ArrayList<>(MAX_SIZE); for (Bitmap b : strategy.bitmaps) { bitmaps.add(b); } pool.clearMemory(); for (Bitmap b : bitmaps) { assertTrue(b.isRecycled()); } } @Test public void testTrimMemoryUiHiddenOrLessRemovesHalfOfBitmaps() { testTrimMemory(MAX_SIZE, TRIM_MEMORY_UI_HIDDEN, MAX_SIZE / 2); } @Test public void testTrimMemoryUiHiddenOrLessRemovesNoBitmapsIfPoolLessThanHalfFull() { testTrimMemory(MAX_SIZE / 2, TRIM_MEMORY_UI_HIDDEN, 0); } @Test public void testTrimMemoryBackgroundOrGreaterRemovesAllBitmaps() { for (int trimLevel : new int[] { TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_COMPLETE }) { testTrimMemory(MAX_SIZE, trimLevel, MAX_SIZE); } } @Test public void testPassesArgb888ToStrategyAsConfigForRequestsWithNullConfigsOnGet() { LruPoolStrategy strategy = mock(LruPoolStrategy.class); LruBitmapPool pool = new LruBitmapPool(100, strategy, ALLOWED_CONFIGS); Bitmap expected = createMutableBitmap(); when(strategy.get(anyInt(), anyInt(), eq(Bitmap.Config.ARGB_8888))).thenReturn(expected); Bitmap result = pool.get(100, 100, null); assertEquals(expected, result); } @Test public void testPassesArgb8888ToStrategyAsConfigForRequestsWithNullConfigsOnGetDirty() { LruPoolStrategy strategy = mock(LruPoolStrategy.class); LruBitmapPool pool = new LruBitmapPool(100, strategy, ALLOWED_CONFIGS); Bitmap expected = createMutableBitmap(); when(strategy.get(anyInt(), anyInt(), eq(Bitmap.Config.ARGB_8888))).thenReturn(expected); Bitmap result = pool.getDirty(100, 100, null); assertEquals(expected, result); } private void testTrimMemory(int fillSize, int trimLevel, int expectedSize) { MockStrategy strategy = new MockStrategy(); LruBitmapPool pool = new LruBitmapPool(MAX_SIZE, strategy, ALLOWED_CONFIGS); fillPool(pool, fillSize); pool.trimMemory(trimLevel); assertEquals("Failed level=" + trimLevel, expectedSize, strategy.numRemoves); } @Test public void testCanIncreaseSizeDynamically() { int sizeMultiplier = 2; pool.setSizeMultiplier(2); fillPool(pool, MAX_SIZE * sizeMultiplier); assertEquals(0, strategy.numRemoves); } @Test public void testCanDecreaseSizeDynamically() { fillPool(pool, MAX_SIZE); assertEquals(0, strategy.numRemoves); float sizeMultiplier = 0.5f; pool.setSizeMultiplier(sizeMultiplier); assertEquals(Math.round(MAX_SIZE * sizeMultiplier), strategy.numRemoves); } @Test public void testCanResetSizeDynamically() { int sizeMultiplier = 2; pool.setSizeMultiplier(sizeMultiplier); fillPool(pool, MAX_SIZE * sizeMultiplier); pool.setSizeMultiplier(1); assertEquals(Math.round(MAX_SIZE * sizeMultiplier) - MAX_SIZE, strategy.numRemoves); } @Test public void testCanGetCurrentMaxSize() { assertEquals(MAX_SIZE, pool.getMaxSize()); } @Test public void testMaxSizeChangesAfterSizeMultiplier() { pool.setSizeMultiplier(2); assertEquals(2 * MAX_SIZE, pool.getMaxSize()); } @Test public void testBitmapsWithDisallowedConfigsAreIgnored() { pool = new LruBitmapPool(100, strategy, Collections.singleton(Bitmap.Config.ARGB_4444)); Bitmap bitmap = createMutableBitmap(Bitmap.Config.RGB_565); pool.put(bitmap); assertEquals(0, strategy.numPuts); } @Test public void testBitmapsWithAllowedNullConfigsAreAllowed() { pool = new LruBitmapPool(100, strategy, Collections.<Bitmap.Config>singleton(null)); Bitmap bitmap = createMutableBitmap(); Shadows.shadowOf(bitmap).setConfig(null); pool.put(bitmap); assertEquals(1, strategy.numPuts); } private void fillPool(LruBitmapPool pool, int fillCount) { for (int i = 0; i < fillCount; i++) { pool.put(createMutableBitmap()); } } private Bitmap createMutableBitmap() { return createMutableBitmap(Bitmap.Config.ARGB_8888); } private Bitmap createMutableBitmap(Bitmap.Config config) { Bitmap bitmap = ShadowBitmap.createBitmap(100, 100, config); Shadows.shadowOf(bitmap).setMutable(true); return bitmap; } private static class MockStrategy implements LruPoolStrategy { private LinkedList<Bitmap> bitmaps = new LinkedList<>(); private int numRemoves; private int numPuts; @Override public void put(Bitmap bitmap) { numPuts++; bitmaps.add(bitmap); } @Override public Bitmap get(int width, int height, Bitmap.Config config) { return bitmaps.removeLast(); } @Override public Bitmap removeLast() { numRemoves++; return bitmaps.removeLast(); } @Override public String logBitmap(Bitmap bitmap) { return null; } @Override public String logBitmap(int width, int height, Bitmap.Config config) { return null; } @Override public int getSize(Bitmap bitmap) { return 1; } } }