Back to Repositories

Testing ContentLengthInputStream Implementation in bumptech/glide

This test suite validates the ContentLengthInputStream functionality in Glide’s utility package, focusing on proper handling of input stream content length and read operations. The tests ensure robust stream processing with various content length scenarios and byte reading patterns.

Test Coverage Overview

The test suite provides comprehensive coverage of ContentLengthInputStream functionality.

Key areas tested include:
  • Content length validation with valid and invalid inputs
  • Stream availability calculations
  • Byte reading operations with different data patterns
  • Error handling for incomplete reads
  • Edge cases with zero-length and high-value byte contents

Implementation Analysis

The testing approach utilizes JUnit with Robolectric for Android environment simulation and Mockito for stream behavior mocking.

Notable patterns include:
  • Mock stream injection for controlled testing
  • Systematic validation of stream states
  • Comprehensive exception handling verification
  • Byte-level operation testing

Technical Details

Testing infrastructure includes:
  • JUnit 4 test framework
  • Robolectric test runner with SDK configuration
  • Mockito for stream behavior simulation
  • ByteArrayInputStream for raw byte testing
  • Google Truth assertion library

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through methodical organization and thorough validation.

Key practices include:
  • Systematic test method naming
  • Comprehensive edge case coverage
  • Proper test isolation using @Before setup
  • Clear assertion patterns
  • Explicit exception testing

bumptech/glide

library/test/src/test/java/com/bumptech/glide/util/ContentLengthInputStreamTest.java

            
package com.bumptech.glide.util;

import static com.bumptech.glide.RobolectricConstants.ROBOLECTRIC_SDK;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
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 ContentLengthInputStreamTest {
  @Mock private InputStream wrapped;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void testAvailable_withZeroReadsAndValidContentLength_returnsContentLength()
      throws IOException {
    int value = 123356;
    InputStream is = ContentLengthInputStream.obtain(wrapped, String.valueOf(value));

    assertThat(is.available()).isEqualTo(value);
  }

  @Test
  public void testAvailable_withNullContentLength_returnsWrappedAvailable() throws IOException {
    InputStream is = ContentLengthInputStream.obtain(wrapped, null /*contentLengthHeader*/);
    int expected = 1234;
    when(wrapped.available()).thenReturn(expected);

    assertThat(is.available()).isEqualTo(expected);
  }

  @Test
  public void testAvailable_withInvalidContentLength_returnsWrappedAvailable() throws IOException {
    InputStream is = ContentLengthInputStream.obtain(wrapped, "invalid_length");
    int expected = 567;
    when(wrapped.available()).thenReturn(expected);

    assertThat(is.available()).isEqualTo(expected);
  }

  @Test
  public void testAvailable_withRead_returnsContentLengthOffsetByRead() throws IOException {
    int contentLength = 999;
    InputStream is = ContentLengthInputStream.obtain(wrapped, String.valueOf(contentLength));
    when(wrapped.read()).thenReturn(1);

    assertThat(is.read()).isEqualTo(1);
    assertThat(is.available()).isEqualTo(contentLength - 1);
  }

  @Test
  public void testAvailable_handlesReadValueOfZero() throws IOException {
    int contentLength = 999;
    InputStream is = ContentLengthInputStream.obtain(wrapped, String.valueOf(contentLength));
    when(wrapped.read()).thenReturn(0);

    assertThat(is.read()).isEqualTo(0);
    assertThat(is.available()).isEqualTo(contentLength - 1);
  }

  @Test
  public void testAvailable_withReadBytes_returnsContentLengthOffsetByNumberOfBytes()
      throws IOException {
    int contentLength = 678;
    InputStream is = ContentLengthInputStream.obtain(wrapped, String.valueOf(contentLength));
    int read = 100;
    when(wrapped.read(any(byte[].class), anyInt(), anyInt())).thenReturn(read);

    assertThat(is.read(new byte[500], 0, 0)).isEqualTo(read);
    assertThat(is.available()).isEqualTo(contentLength - read);
  }

  @Test
  public void testRead_whenReturnsLessThanZeroWithoutReadingAllContent_throwsIOException()
      throws IOException {
    int contentLength = 1;
    InputStream is = ContentLengthInputStream.obtain(wrapped, String.valueOf(contentLength));
    when(wrapped.read()).thenReturn(-1);

    try {
      //noinspection ResultOfMethodCallIgnored
      is.read();
      fail("Failed to throw expected exception");
    } catch (IOException e) {
      // Expected.
    }
  }

  @Test
  public void testReadBytes_whenReturnsLessThanZeroWithoutReadingAllContent_throwsIOException()
      throws IOException {
    int contentLength = 2;
    InputStream is = ContentLengthInputStream.obtain(wrapped, String.valueOf(contentLength));
    when(wrapped.read(any(byte[].class), anyInt(), anyInt())).thenReturn(-1);

    try {
      //noinspection ResultOfMethodCallIgnored
      is.read(new byte[10], 0, 0);
      fail("Failed to throw expected exception");
    } catch (IOException e) {
      // Expected.
    }
  }

  @Test
  public void testRead_whenReturnsLessThanZeroWithInvalidLength_doesNotThrow() throws IOException {
    InputStream is = ContentLengthInputStream.obtain(wrapped, "invalid_length");
    when(wrapped.read()).thenReturn(-1);
    //noinspection ResultOfMethodCallIgnored
    is.read();
  }

  @Test
  public void testReadBytes_whenReturnsLessThanZeroWithInvalidLength_doesNotThrow()
      throws IOException {
    InputStream is = ContentLengthInputStream.obtain(wrapped, "invalid_length");
    when(wrapped.read(any(byte[].class), anyInt(), anyInt())).thenReturn(-1);
    //noinspection ResultOfMethodCallIgnored
    is.read(new byte[10], 0, 0);
  }

  @Test
  public void testRead_readWithZeroes_doesNotThrow() throws IOException {
    ByteArrayInputStream inner = new ByteArrayInputStream(new byte[] {0, 0, 0});
    InputStream is = ContentLengthInputStream.obtain(inner, 3);

    assertThat(is.read()).isEqualTo(0);
    assertThat(is.read()).isEqualTo(0);
    assertThat(is.read()).isEqualTo(0);
    assertThat(is.read()).isEqualTo(-1);
  }

  @Test
  public void testRead_readWithHighValues_doesNotThrow() throws IOException {
    ByteArrayInputStream inner =
        new ByteArrayInputStream(new byte[] {(byte) 0xF0, (byte) 0xA0, (byte) 0xFF});
    InputStream is = ContentLengthInputStream.obtain(inner, 3);

    assertThat(is.read()).isEqualTo(0xF0);
    assertThat(is.read()).isEqualTo(0xA0);
    assertThat(is.read()).isEqualTo(0xFF);
    assertThat(is.read()).isEqualTo(-1);
  }
}