Back to Repositories

Testing Disk Cache Operations Implementation in Glide

This test suite validates the DiskLruCacheWrapper implementation in Glide, focusing on disk caching operations and file system interactions. It ensures reliable cache storage and retrieval while handling edge cases like failed writes and directory deletions.

Test Coverage Overview

The test suite provides comprehensive coverage of disk caching operations including:

  • Basic cache insertion and retrieval operations
  • Failed write scenarios and error handling
  • Cache commitment validation
  • Directory deletion and recovery scenarios
  • Windows-specific file handling considerations

Implementation Analysis

The testing approach utilizes JUnit with Robolectric for Android environment simulation. It implements systematic test patterns with setUp/tearDown cycles and careful file system cleanup. The tests use mock objects and byte array verification to ensure cache integrity.

Technical Details

  • Testing Framework: JUnit with RobolectricTestRunner
  • Android Testing: ApplicationProvider for context access
  • File Operations: Custom utility methods for file handling
  • Mock Framework: Mockito for object mocking
  • Configuration: Robolectric SDK configuration for Android simulation

Best Practices Demonstrated

The test suite exemplifies robust testing practices including proper resource cleanup, isolation of test cases, and comprehensive error handling. It demonstrates careful consideration of platform-specific issues (Windows vs Unix) and implements proper test lifecycle management through @Before and @After annotations.

bumptech/glide

library/test/src/test/java/com/bumptech/glide/load/engine/cache/DiskLruCacheWrapperTest.java

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

import static com.bumptech.glide.RobolectricConstants.ROBOLECTRIC_SDK;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.mock;

import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.signature.ObjectKey;
import com.bumptech.glide.tests.Util;
import java.io.File;
import java.io.IOException;
import org.junit.After;
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 DiskLruCacheWrapperTest {
  private DiskCache cache;
  private byte[] data;
  private ObjectKey key;
  private File dir;

  @Before
  public void setUp() {
    dir = ApplicationProvider.getApplicationContext().getCacheDir();
    cache = DiskLruCacheWrapper.create(dir, 10 * 1024 * 1024);
    key = new ObjectKey("test" + Math.random());
    data = new byte[] {1, 2, 3, 4, 5, 6};
  }

  @After
  public void tearDown() {
    try {
      cache.clear();
    } finally {
      deleteRecursive(dir);
    }
  }

  private static void deleteRecursive(File file) {
    if (file.isDirectory()) {
      File[] files = file.listFiles();
      if (files != null) {
        for (File f : files) {
          deleteRecursive(f);
        }
      }
    }
    // GC before delete() to release files on Windows (https://stackoverflow.com/a/4213208/253468)
    System.gc();
    if (!file.delete() && file.exists()) {
      throw new RuntimeException("Failed to delete: " + file);
    }
  }

  @Test
  public void testCanInsertAndGet() throws IOException {
    cache.put(
        key,
        new DiskCache.Writer() {
          @Override
          public boolean write(@NonNull File file) {
            try {
              Util.writeFile(file, data);
            } catch (IOException e) {
              fail(e.toString());
            }
            return true;
          }
        });

    byte[] received = Util.readFile(cache.get(key), data.length);

    assertArrayEquals(data, received);
  }

  @Test
  public void testDoesNotCommitIfWriterReturnsFalse() {
    cache.put(
        key,
        new DiskCache.Writer() {
          @Override
          public boolean write(@NonNull File file) {
            return false;
          }
        });

    assertNull(cache.get(key));
  }

  @Test
  public void testDoesNotCommitIfWriterWritesButReturnsFalse() {
    cache.put(
        key,
        new DiskCache.Writer() {
          @Override
          public boolean write(@NonNull File file) {
            try {
              Util.writeFile(file, data);
            } catch (IOException e) {
              fail(e.toString());
            }
            return false;
          }
        });

    assertNull(cache.get(key));
  }

  @Test
  public void testEditIsAbortedIfWriterThrows() throws IOException {
    try {
      cache.put(
          key,
          new DiskCache.Writer() {
            @Override
            public boolean write(@NonNull File file) {
              throw new RuntimeException("test");
            }
          });
    } catch (RuntimeException e) {
      // Expected.
    }

    cache.put(
        key,
        new DiskCache.Writer() {
          @Override
          public boolean write(@NonNull File file) {
            try {
              Util.writeFile(file, data);
            } catch (IOException e) {
              fail(e.toString());
            }
            return true;
          }
        });

    byte[] received = Util.readFile(cache.get(key), data.length);

    assertArrayEquals(data, received);
  }

  // Tests #2465.
  @Test
  public void clearDiskCache_afterOpeningDiskCache_andDeleteDirectoryOutsideGlide_doesNotThrow() {
    assumeTrue("A file handle is likely open, so cannot delete dir", !Util.isWindows());
    DiskCache cache = DiskLruCacheWrapper.create(dir, 1024 * 1024);
    cache.get(mock(Key.class));
    deleteRecursive(dir);
    cache.clear();
  }

  // Tests #2465.
  @Test
  public void get_afterDeleteDirectoryOutsideGlideAndClose_doesNotThrow() {
    assumeTrue("A file handle is likely open, so cannot delete dir", !Util.isWindows());
    DiskCache cache = DiskLruCacheWrapper.create(dir, 1024 * 1024);
    cache.get(mock(Key.class));
    deleteRecursive(dir);
    cache.clear();

    cache.get(mock(Key.class));
  }
}