Back to Repositories

Testing MarkEnforcingInputStream Implementation in Glide

This test suite validates the MarkEnforcingInputStream implementation in Glide, focusing on input stream marking and reading behaviors with specific byte limits. The tests ensure proper stream handling, mark/reset functionality, and boundary conditions for byte reading operations.

Test Coverage Overview

The test suite provides comprehensive coverage of MarkEnforcingInputStream functionality, including:

  • Byte reading operations up to and beyond mark limits
  • Buffer reading with various size configurations
  • Stream marking and reset behaviors
  • Skip operations with different byte counts
  • Available bytes calculation under different marking scenarios

Implementation Analysis

The testing approach utilizes JUnit and Robolectric frameworks to validate stream behaviors. Tests employ ByteArrayInputStream as the underlying stream implementation, with careful attention to boundary conditions and edge cases. The implementation follows a systematic pattern of testing mark limits, buffer operations, and stream state verification.

Technical Details

  • Testing Framework: JUnit with RobolectricTestRunner
  • Assertion Libraries: Google Truth, JUnit Assert
  • Test Data: Controlled byte arrays with specific sizes (MARK_LIMIT and DATA_SIZE)
  • Mock Objects: ByteArrayInputStream for controlled input scenarios

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Systematic boundary testing with clear test case naming
  • Comprehensive edge case coverage
  • Consistent test structure and organization
  • Clear separation of test scenarios
  • Effective use of test constants and setup

bumptech/glide

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

            
package com.bumptech.glide.util;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

@RunWith(RobolectricTestRunner.class)
public class MarkEnforcingInputStreamTest {
  // An arbitrary number > 0.
  private static final int MARK_LIMIT = 5;
  // Another arbitrary number > MARK_LIMIT.
  private static final int DATA_SIZE = MARK_LIMIT + 1;

  @Test
  public void testReturnsByte_whenReadsUpToMarkLimit_withMoreBytesAvailable() throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));
    is.mark(MARK_LIMIT);

    for (int i = 0; i < MARK_LIMIT; i++) {
      assertThat(is.read()).isAtLeast(0);
    }
  }

  @Test
  public void testReturnsByte_whenReadsUpToMarkLimit_withNoMoreBytesAvailable() throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[MARK_LIMIT]));

    for (int i = 0; i < MARK_LIMIT; i++) {
      assertThat(is.read()).isAtLeast(0);
    }
  }

  @Test
  public void testReturnsEndOfStream_whenReadsSingleBytePastMarkLimit() throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));

    is.mark(MARK_LIMIT);
    for (int i = 0; i < MARK_LIMIT; i++) {
      assertThat(is.read()).isAtLeast(0);
    }

    assertEquals(-1, is.read());
  }

  @Test
  public void
      testOverridesByteCount_whenReadBufferLargerThanMarkLimit_withNonZeroBytesRemainingInMarkLimit()
          throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));

    is.mark(MARK_LIMIT);
    byte[] buffer = new byte[DATA_SIZE];
    assertEquals(MARK_LIMIT, is.read(buffer));
  }

  @Test
  public void
      testReturnsEndOfStream_whenReadBufferLargerThanMarkLimit_withZeroBytesRemainingInMarkLimit()
          throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));
    is.mark(MARK_LIMIT);

    byte[] buffer = new byte[MARK_LIMIT];
    assertEquals(MARK_LIMIT, is.read(buffer));
    assertEquals(-1, is.read(buffer));
  }

  @Test
  public void testDoesNotReadIntoBuffer_withZeroBytesRemainingInMarkLimit() throws IOException {
    byte[] expected = new byte[MARK_LIMIT];
    for (int i = 0; i < MARK_LIMIT; i++) {
      expected[i] = (byte) (i + 1);
    }
    byte[] buffer = new byte[MARK_LIMIT];
    System.arraycopy(expected, 0, buffer, 0, MARK_LIMIT);

    // All zeros.
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));
    is.mark(MARK_LIMIT);
    for (int i = 0; i < MARK_LIMIT; i++) {
      assertThat(is.read()).isAtLeast(0);
    }

    assertEquals(-1, is.read(buffer));

    assertThat(buffer).isEqualTo(expected);
  }

  @Test
  public void testResetUnsetsLimit() throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));
    is.mark(MARK_LIMIT);

    for (int i = 0; i < MARK_LIMIT; i++) {
      assertThat(is.read()).isAtLeast(0);
    }

    is.reset();

    for (int i = 0; i < DATA_SIZE; i++) {
      assertThat(is.read()).isAtLeast(0);
    }
  }

  @Test
  public void
      testOverridesByteCount_whenSkipCountLargerThanMarkLimit_withNonZeroBytesRemainingInMarkLimit()
          throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));
    is.mark(MARK_LIMIT);

    assertEquals(MARK_LIMIT, is.skip(DATA_SIZE));
  }

  @Test
  public void testReturnsEndOfStream_whenSkipping_withZeroBytesRemainingInMarkLimit()
      throws IOException {
    MarkEnforcingInputStream is =
        new MarkEnforcingInputStream(new ByteArrayInputStream(new byte[DATA_SIZE]));
    is.mark(MARK_LIMIT);

    assertEquals(MARK_LIMIT, is.skip(DATA_SIZE));
    assertEquals(0, is.skip(1));
  }

  @Test
  public void testReturnsStreamAvailable_whenMarkIsNotSet() throws IOException {
    ByteArrayInputStream wrapped = new ByteArrayInputStream(new byte[MARK_LIMIT]);
    MarkEnforcingInputStream is = new MarkEnforcingInputStream(wrapped);

    assertEquals(wrapped.available(), is.available());
  }

  @Test
  public void testReturnsStreamAvailable_whenMarkIsSet_withMarkGreaterThanStreamAvailable()
      throws IOException {
    ByteArrayInputStream wrapped = new ByteArrayInputStream(new byte[MARK_LIMIT]);
    MarkEnforcingInputStream is = new MarkEnforcingInputStream(wrapped);
    is.mark(wrapped.available() + 1);

    assertEquals(wrapped.available(), is.available());
  }

  @Test
  public void testReturnsMarkLimitAsAvailable_whenMarkIsSet_withMarkLessThanStreamAvailable()
      throws IOException {
    ByteArrayInputStream wrapped = new ByteArrayInputStream(new byte[MARK_LIMIT]);
    MarkEnforcingInputStream is = new MarkEnforcingInputStream(wrapped);
    int expected = wrapped.available() - 1;
    is.mark(expected);

    assertEquals(expected, is.available());
  }
}