Back to Repositories

Testing GroupedLinkedMap Bitmap Recycling Implementation in Glide

This test suite validates the GroupedLinkedMap implementation in Glide’s bitmap recycling system, focusing on key-value storage and retrieval with least recently used (LRU) behavior. The tests ensure proper memory management and caching functionality for bitmap resources.

Test Coverage Overview

The test suite provides comprehensive coverage of GroupedLinkedMap functionality:
  • Null handling for non-existent keys
  • Basic key-value storage and retrieval
  • Multiple value storage for single keys
  • LRU ordering verification
  • Entry addition and access order testing

Implementation Analysis

The testing approach utilizes JUnit with Robolectric for Android environment simulation. Tests employ a systematic pattern of setup-execute-verify, with careful attention to state management and ordering verification. Each test case isolates specific behaviors of the GroupedLinkedMap implementation.

The test structure leverages @Before setup methods and clear test case separation for maintainability.

Technical Details

Testing infrastructure includes:
  • JUnit 4 test framework
  • Robolectric test runner with SDK configuration
  • Google Truth assertion library
  • Custom Key class implementing Poolable interface
  • GroupedLinkedMap implementation under test

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Clear test method naming conveying purpose
  • Isolated test cases with specific assertions
  • Proper setup and teardown management
  • Comprehensive edge case coverage
  • Consistent assertion style using both JUnit and Truth assertions

bumptech/glide

library/test/src/test/java/com/bumptech/glide/load/engine/bitmap_recycle/GroupedLinkedMapTest.java

            
package com.bumptech.glide.load.engine.bitmap_recycle;

import static com.bumptech.glide.RobolectricConstants.ROBOLECTRIC_SDK;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNull;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@Config(sdk = ROBOLECTRIC_SDK)
public class GroupedLinkedMapTest {

  private GroupedLinkedMap<Key, Object> map;

  @Before
  public void setUp() {
    map = new GroupedLinkedMap<>();
  }

  @Test
  public void testReturnsNullForGetWithNoBitmap() {
    Key key = new Key("key", /* width= */ 1, /* height= */ 1);
    assertNull(map.get(key));
  }

  @Test
  public void testCanAddAndRemoveABitmap() {
    Key key = new Key("key", 1, 1);
    Object expected = new Object();

    map.put(key, expected);

    assertThat(map.get(key)).isEqualTo(expected);
  }

  @Test
  public void testCanAddAndRemoveMoreThanOneBitmapForAGivenKey() {
    Key key = new Key("key", 1, 1);
    Integer value = 20;

    int numToAdd = 10;

    for (int i = 0; i < numToAdd; i++) {
      map.put(key, value);
    }

    for (int i = 0; i < numToAdd; i++) {
      assertThat(map.get(key)).isEqualTo(value);
    }
  }

  @Test
  public void testLeastRecentlyRetrievedKeyIsLeastRecentlyUsed() {
    Key firstKey = new Key("key", 1, 1);
    Integer firstValue = 10;
    map.put(firstKey, firstValue);
    map.put(firstKey, firstValue);

    Key secondKey = new Key("key", 2, 2);
    Integer secondValue = 20;
    map.put(secondKey, secondValue);

    map.get(firstKey);

    assertThat(map.removeLast()).isEqualTo(secondValue);
  }

  @Test
  public void testAddingAnEntryDoesNotMakeItMostRecentlyUsed() {
    Key firstKey = new Key("key", 1, 1);
    Integer firstValue = 10;

    map.put(firstKey, firstValue);
    map.put(firstKey, firstValue);

    map.get(firstKey);

    Integer secondValue = 20;
    map.put(new Key("key", 2, 2), secondValue);

    assertThat(map.removeLast()).isEqualTo(secondValue);
  }

  private static final class Key implements Poolable {

    private final String key;
    private final int width;
    private final int height;

    Key(String key, int width, int height) {
      this.key = key;
      this.width = width;
      this.height = height;
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof Key) {
        Key other = (Key) o;
        return key.equals(other.key) && width == other.width && height == other.height;
      }
      return false;
    }

    @Override
    public int hashCode() {
      int result = key != null ? key.hashCode() : 0;
      result = 31 * result + width;
      result = 31 * result + height;
      return result;
    }

    @Override
    public void offer() {
      // Do nothing.
    }
  }
}