Back to Repositories

Testing PreloadTarget Resource Management in Glide

The PreloadTargetTest suite validates the functionality of Glide’s PreloadTarget class, focusing on resource handling and request management in asynchronous image loading scenarios. This comprehensive test suite ensures proper dimension handling, request state management, and target clearing behavior.

Test Coverage Overview

The test suite provides thorough coverage of PreloadTarget’s core functionality:
  • Size dimension handling and callback verification
  • Resource ready state management with different request conditions
  • Target clearing behavior in various scenarios
  • Asynchronous operation handling with main looper

Implementation Analysis

The testing approach utilizes Robolectric for Android environment simulation and Mockito for dependency mocking. The implementation follows a systematic pattern of testing request states and resource handling, with particular attention to asynchronous behavior through controlled Looper manipulation.

Key patterns include mock verification, request state simulation, and UI thread handling.

Technical Details

Testing tools and configuration:
  • JUnit test framework with Robolectric test runner
  • Mockito for mocking RequestManager and Request objects
  • Shadow Looper manipulation for async testing
  • SDK configuration using ROBOLECTRIC_SDK constant
  • MockitoAnnotations for automatic mock initialization

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Proper test isolation through @Before setup
  • Comprehensive edge case coverage
  • Clear test method naming conveying purpose
  • Effective use of mocking to isolate components
  • Controlled async testing through proper thread management

bumptech/glide

library/test/src/test/java/com/bumptech/glide/request/target/PreloadTargetTest.java

            
package com.bumptech.glide.request.target;

import static com.bumptech.glide.RobolectricConstants.ROBOLECTRIC_SDK;
import static org.mockito.ArgumentMatchers.eq;
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 static org.robolectric.Shadows.shadowOf;

import android.os.Looper;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.Request;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

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

  @Mock private RequestManager requestManager;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
    shadowOf(Looper.getMainLooper()).pause();
  }

  @Test
  public void testCallsSizeReadyWithGivenDimensions() {
    int width = 1234;
    int height = 456;
    PreloadTarget<Object> target = PreloadTarget.obtain(requestManager, width, height);
    SizeReadyCallback cb = mock(SizeReadyCallback.class);
    target.getSize(cb);

    verify(cb).onSizeReady(eq(width), eq(height));
  }

  // This isn't really supposed to happen, but just to double check...
  @Test
  public void onResourceReady_withNullRequest_doesNotClearTarget() {
    PreloadTarget<Object> target = PreloadTarget.obtain(requestManager, 100, 100);
    target.setRequest(null);

    callOnResourceReadyAndRunUiRunnables(target);

    verify(requestManager, never()).clear(target);
  }

  @Test
  public void onResourceReady_withNotYetCompleteRequest_doesNotClearTarget() {
    Request request = mock(Request.class);
    when(request.isComplete()).thenReturn(false);

    PreloadTarget<Object> target = PreloadTarget.obtain(requestManager, 100, 100);
    target.setRequest(request);

    callOnResourceReadyAndRunUiRunnables(target);

    verify(requestManager, never()).clear(target);
  }

  @Test
  public void onResourceReady_withCompleteRequest_postsToClearTarget() {
    Request request = mock(Request.class);
    when(request.isComplete()).thenReturn(true);

    PreloadTarget<Object> target = PreloadTarget.obtain(requestManager, 100, 100);
    target.setRequest(request);

    callOnResourceReadyAndRunUiRunnables(target);

    verify(requestManager).clear(target);
  }

  @Test
  public void onResourceReady_withCompleteRequest_doesNotImmediatelyClearTarget() {
    Request request = mock(Request.class);
    when(request.isComplete()).thenReturn(true);

    PreloadTarget<Object> target = PreloadTarget.obtain(requestManager, 100, 100);
    target.setRequest(request);

    target.onResourceReady(new Object(), /* transition= */ null);

    verify(requestManager, never()).clear(target);
  }

  private void callOnResourceReadyAndRunUiRunnables(Target<Object> target) {
    target.onResourceReady(new Object(), /* transition= */ null);
    shadowOf(Looper.getMainLooper()).idle();
  }
}