package com.bumptech.glide.load.engine.prefill;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.engine.cache.MemoryCache;
import com.bumptech.glide.tests.Util.CreateBitmap;
import com.bumptech.glide.util.Util;
import com.google.common.collect.Range;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 18)
public class BitmapPreFillerTest {
private static final int DEFAULT_BITMAP_WIDTH = 100;
private static final int DEFAULT_BITMAP_HEIGHT = 50;
private static final int BITMAPS_IN_POOL = 10;
private static final int BITMAPS_IN_CACHE = 10;
private final Bitmap.Config defaultBitmapConfig = PreFillType.DEFAULT_CONFIG;
private final Bitmap defaultBitmap =
Bitmap.createBitmap(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT, defaultBitmapConfig);
private final int defaultBitmapSize = Util.getBitmapByteSize(defaultBitmap);
private final int poolSize = BITMAPS_IN_CACHE * defaultBitmapSize;
private final int cacheSize = BITMAPS_IN_POOL * defaultBitmapSize;
@Mock BitmapPool pool;
@Mock MemoryCache cache;
private BitmapPreFiller bitmapPreFiller;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(pool.getMaxSize()).thenReturn(poolSize);
when(pool.getDirty(anyInt(), anyInt(), any(Bitmap.Config.class)))
.thenAnswer(new CreateBitmap());
when(cache.getMaxSize()).thenReturn(cacheSize);
bitmapPreFiller = new BitmapPreFiller(cache, pool, DecodeFormat.DEFAULT);
}
@Test
public void testAllocationOrderContainsEnoughSizesToFillPoolAndMemoryCache() {
PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(new PreFillType[] {
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build() });
assertEquals(BITMAPS_IN_POOL + BITMAPS_IN_CACHE, allocationOrder.getSize());
}
@Test
public void testAllocationOrderThatDoesNotFitExactlyIntoGivenSizeRoundsDown() {
PreFillType[] sizes = new PreFillType[] {
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build(),
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build(),
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2)
.setConfig(defaultBitmapConfig).build(), };
PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(sizes);
int byteSize = 0;
while (!allocationOrder.isEmpty()) {
PreFillType current = allocationOrder.remove();
byteSize +=
Util.getBitmapByteSize(current.getWidth(), current.getHeight(), current.getConfig());
}
int expectedSize = 0;
int maxSize = poolSize + cacheSize;
for (PreFillType current : sizes) {
int currentSize =
Util.getBitmapByteSize(current.getWidth(), current.getHeight(), current.getConfig());
expectedSize += currentSize * (maxSize / (3 * currentSize));
}
assertEquals(expectedSize, byteSize);
}
@Test
public void testAllocationOrderDoesNotOverFillWithMultipleSizes() {
PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(new PreFillType[] {
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build(),
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build(),
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2)
.setConfig(defaultBitmapConfig).build() });
int byteSize = 0;
while (!allocationOrder.isEmpty()) {
PreFillType current = allocationOrder.remove();
byteSize +=
Util.getBitmapByteSize(current.getWidth(), current.getHeight(), current.getConfig());
}
assertThat(byteSize).isIn(Range.atMost(poolSize + cacheSize));
}
@Test
public void testAllocationOrderDoesNotOverFillWithMultipleSizesAndWeights() {
PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(new PreFillType[] {
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).setWeight(4).build(),
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build(),
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 3)
.setConfig(defaultBitmapConfig).setWeight(3).build() });
int byteSize = 0;
while (!allocationOrder.isEmpty()) {
PreFillType current = allocationOrder.remove();
byteSize +=
Util.getBitmapByteSize(current.getWidth(), current.getHeight(), current.getConfig());
}
assertThat(byteSize).isIn(Range.atMost(poolSize + cacheSize));
}
@Test
public void testAllocationOrderContainsSingleSizeIfSingleSizeIsProvided() {
PreFillQueue allocationOrder = bitmapPreFiller.generateAllocationOrder(new PreFillType[] {
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build() });
while (!allocationOrder.isEmpty()) {
PreFillType size = allocationOrder.remove();
assertEquals(DEFAULT_BITMAP_WIDTH, size.getWidth());
assertEquals(DEFAULT_BITMAP_HEIGHT, size.getHeight());
assertEquals(defaultBitmapConfig, size.getConfig());
}
}
@Test
public void testAllocationOrderSplitsEvenlyBetweenEqualSizesWithEqualWeights() {
PreFillType smallWidth =
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build();
PreFillType smallHeight =
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2)
.setConfig(defaultBitmapConfig).build();
PreFillQueue allocationOrder =
bitmapPreFiller.generateAllocationOrder(new PreFillType[] { smallWidth, smallHeight, });
int numSmallWidth = 0, numSmallHeight = 0;
while (!allocationOrder.isEmpty()) {
PreFillType current = allocationOrder.remove();
if (smallWidth.equals(current)) {
numSmallWidth++;
} else if (smallHeight.equals(current)) {
numSmallHeight++;
} else {
fail("Unexpected size, size: " + current);
}
}
assertEquals(numSmallWidth, numSmallHeight);
}
@Test
public void testAllocationOrderSplitsByteSizeEvenlyBetweenUnEqualSizesWithEqualWeights() {
PreFillType smallWidth =
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build();
PreFillType normal = new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build();
PreFillQueue allocationOrder =
bitmapPreFiller.generateAllocationOrder(new PreFillType[] { smallWidth, normal });
int numSmallWidth = 0, numNormal = 0;
while (!allocationOrder.isEmpty()) {
PreFillType current = allocationOrder.remove();
if (smallWidth.equals(current)) {
numSmallWidth++;
} else if (normal.equals(current)) {
numNormal++;
} else {
fail("Unexpected size, size: " + current);
}
}
assertEquals(2 * numNormal, numSmallWidth);
}
@Test
public void testAllocationOrderSplitsByteSizeUnevenlyBetweenEqualSizesWithUnequalWeights() {
PreFillType doubleWeight =
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).setWeight(2).build();
PreFillType normal = new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2)
.setConfig(defaultBitmapConfig).build();
PreFillQueue allocationOrder =
bitmapPreFiller.generateAllocationOrder(new PreFillType[] { doubleWeight, normal });
int numDoubleWeight = 0, numNormal = 0;
while (!allocationOrder.isEmpty()) {
PreFillType current = allocationOrder.remove();
if (doubleWeight.equals(current)) {
numDoubleWeight++;
} else if (normal.equals(current)) {
numNormal++;
} else {
fail("Unexpected size, size: " + current);
}
}
assertEquals(2 * numNormal, numDoubleWeight);
}
@Test
public void testAllocationOrderRoundRobinsDifferentSizes() {
when(pool.getMaxSize()).thenReturn(defaultBitmapSize);
when(cache.getMaxSize()).thenReturn(defaultBitmapSize);
PreFillType smallWidth =
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH / 2, DEFAULT_BITMAP_HEIGHT)
.setConfig(defaultBitmapConfig).build();
PreFillType smallHeight =
new PreFillType.Builder(DEFAULT_BITMAP_WIDTH, DEFAULT_BITMAP_HEIGHT / 2)
.setConfig(defaultBitmapConfig).build();
PreFillQueue allocationOrder =
bitmapPreFiller.generateAllocationOrder(new PreFillType[] { smallWidth, smallHeight, });
List<PreFillType> attributes = new ArrayList<>();
while (!allocationOrder.isEmpty()) {
attributes.add(allocationOrder.remove());
}
// Either width, height, width, height or height, width, height, width.
try {
assertThat(attributes).containsExactly(smallWidth, smallHeight, smallWidth, smallHeight)
.inOrder();
} catch (AssertionError e) {
assertThat(attributes).containsExactly(smallHeight, smallWidth, smallHeight, smallWidth)
.inOrder();
}
}
@Test
public void testSetsConfigOnBuildersToDefaultIfNotSet() {
PreFillType.Builder builder = mock(PreFillType.Builder.class);
when(builder.build())
.thenReturn(new PreFillType.Builder(100).setConfig(Bitmap.Config.RGB_565).build());
bitmapPreFiller.preFill(builder);
InOrder order = inOrder(builder);
order.verify(builder).setConfig(DecodeFormat.DEFAULT == DecodeFormat.PREFER_ARGB_8888
? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
order.verify(builder).build();
}
@Test
public void testDoesNotSetConfigOnBuildersIfConfigIsAlreadySet() {
PreFillType.Builder builder = mock(PreFillType.Builder.class);
when(builder.getConfig()).thenReturn(Bitmap.Config.ARGB_4444);
when(builder.build())
.thenReturn(new PreFillType.Builder(100).setConfig(Bitmap.Config.ARGB_4444).build());
bitmapPreFiller.preFill(builder);
verify(builder, never()).setConfig(any(Bitmap.Config.class));
}
}