Back to Repositories

Testing Resource Recycling Management in Glide

This test suite validates the ResourceRecycler component in Glide, focusing on resource recycling behavior and timing. It verifies both synchronous and asynchronous resource recycling mechanisms, ensuring proper memory management in the Glide image loading library.

Test Coverage Overview

The test suite provides comprehensive coverage of the ResourceRecycler class functionality.

Key areas tested include:
  • Synchronous resource recycling without frame forcing
  • Asynchronous resource recycling with frame forcing
  • Nested resource recycling behavior with parent-child relationships
Edge cases covered include timing dependencies and main thread operations.

Implementation Analysis

The testing approach utilizes RobolectricTestRunner for Android environment simulation, with careful attention to thread management and timing control.

Key patterns include:
  • Mockito for resource mocking and behavior verification
  • Shadow looper manipulation for controlled execution timing
  • Explicit testing of both forced and non-forced frame scenarios

Technical Details

Testing infrastructure includes:
  • JUnit 4 testing framework
  • Robolectric for Android runtime simulation
  • Mockito for mocking and verification
  • Custom shadow looper manipulation for thread control
  • SDK version configuration for compatibility testing

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Proper test isolation with @Before setup
  • Clear test method naming reflecting test scenarios
  • Comprehensive verification of both positive and negative cases
  • Effective use of mocking to isolate test subjects
  • Careful management of asynchronous operations

bumptech/glide

library/test/src/test/java/com/bumptech/glide/load/engine/ResourceRecyclerTest.java

            
package com.bumptech.glide.load.engine;

import static com.bumptech.glide.RobolectricConstants.ROBOLECTRIC_SDK;
import static com.bumptech.glide.tests.Util.mockResource;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.os.Looper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;

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

  private ResourceRecycler recycler;

  @Before
  public void setUp() {
    recycler = new ResourceRecycler();
  }

  @Test
  public void recycle_withoutForceNextFrame_recyclesResourceSynchronously() {
    Resource<?> resource = mockResource();
    Shadows.shadowOf(Looper.getMainLooper()).pause();
    recycler.recycle(resource, /* forceNextFrame= */ false);
    verify(resource).recycle();
  }

  @Test
  public void recycle_withForceNextFrame_postsRecycle() {
    Resource<?> resource = mockResource();
    Shadows.shadowOf(Looper.getMainLooper()).pause();
    recycler.recycle(resource, /* forceNextFrame= */ true);
    verify(resource, never()).recycle();
    Shadows.shadowOf(Looper.getMainLooper()).runToEndOfTasks();
    verify(resource).recycle();
  }

  @Test
  public void testDoesNotRecycleChildResourceSynchronously() {
    Resource<?> parent = mockResource();
    final Resource<?> child = mockResource();
    doAnswer(
            new Answer<Void>() {
              @Override
              public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
                recycler.recycle(child, /* forceNextFrame= */ false);
                return null;
              }
            })
        .when(parent)
        .recycle();

    Shadows.shadowOf(Looper.getMainLooper()).pause();

    recycler.recycle(parent, /* forceNextFrame= */ false);

    verify(parent).recycle();
    verify(child, never()).recycle();

    Shadows.shadowOf(Looper.getMainLooper()).runOneTask();

    verify(child).recycle();
  }
}