Back to Repositories

Testing SonicAudioProcessor Audio Processing Implementation in SmartTube

A comprehensive test suite for the SonicAudioProcessor class in ExoPlayer, focusing on audio sample rate manipulation, speed/pitch modifications, and format handling. This test suite validates core audio processing functionality and configuration changes.

Test Coverage Overview

The test suite provides thorough coverage of SonicAudioProcessor functionality including:

  • Sample rate configuration and reconfiguration scenarios
  • Speed and pitch modification validation
  • Audio format compatibility testing
  • Edge cases for configuration changes
  • Processor state validation

Implementation Analysis

The testing approach utilizes JUnit4 with AndroidJUnit4 runner for Android-specific audio processing validation. Tests are structured around setUp/tearDown patterns with clear separation of configuration and validation steps. The implementation leverages Truth assertions for precise result verification.

Technical Details

Testing tools and configuration:

  • JUnit4 test framework
  • AndroidJUnit4 test runner
  • Truth assertion library
  • ExoPlayer core audio processing components
  • PCM audio format testing

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Systematic test method naming convention
  • Proper test isolation through @Before setup
  • Comprehensive error case handling
  • Clear separation of test scenarios
  • Thorough validation of state changes

yuliskov/smarttube

exoplayer-amzn-2.10.6/library/core/src/test/java/com/google/android/exoplayer2/audio/SonicAudioProcessorTest.java

            
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.exoplayer2.audio;

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

import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

/** Unit test for {@link SonicAudioProcessor}. */
@RunWith(AndroidJUnit4.class)
public final class SonicAudioProcessorTest {

  private SonicAudioProcessor sonicAudioProcessor;

  @Before
  public void setUp() {
    sonicAudioProcessor = new SonicAudioProcessor();
  }

  @Test
  public void testReconfigureWithSameSampleRate() throws Exception {
    // When configured for resampling from 44.1 kHz to 48 kHz, the output sample rate is correct.
    sonicAudioProcessor.setOutputSampleRateHz(48000);
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(48000);
    assertThat(sonicAudioProcessor.isActive()).isTrue();
    // When reconfigured with 48 kHz input, there is no resampling.
    sonicAudioProcessor.configure(48000, 2, C.ENCODING_PCM_16BIT);
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(48000);
    assertThat(sonicAudioProcessor.isActive()).isFalse();
    // When reconfigure with 44.1 kHz input, resampling is enabled again.
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(48000);
    assertThat(sonicAudioProcessor.isActive()).isTrue();
  }

  @Test
  public void testNoSampleRateChange() throws Exception {
    // Configure for resampling 44.1 kHz to 48 kHz.
    sonicAudioProcessor.setOutputSampleRateHz(48000);
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    // Reconfigure to not modify the sample rate.
    sonicAudioProcessor.setOutputSampleRateHz(SonicAudioProcessor.SAMPLE_RATE_NO_CHANGE);
    sonicAudioProcessor.configure(22050, 2, C.ENCODING_PCM_16BIT);
    // The sample rate is unmodified, and the audio processor is not active.
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(22050);
    assertThat(sonicAudioProcessor.isActive()).isFalse();
  }

  @Test
  public void testBecomesActiveAfterConfigure() throws Exception {
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    // Set a new sample rate.
    sonicAudioProcessor.setOutputSampleRateHz(22050);
    // The new sample rate is not active yet.
    assertThat(sonicAudioProcessor.isActive()).isFalse();
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(44100);
  }

  @Test
  public void testSampleRateChangeBecomesActiveAfterConfigure() throws Exception {
    // Configure for resampling 44.1 kHz to 48 kHz.
    sonicAudioProcessor.setOutputSampleRateHz(48000);
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    // Set a new sample rate, which isn't active yet.
    sonicAudioProcessor.setOutputSampleRateHz(22050);
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(48000);
    // The new sample rate takes effect on reconfiguration.
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    assertThat(sonicAudioProcessor.getOutputSampleRateHz()).isEqualTo(22050);
  }

  @Test
  public void testIsActiveWithSpeedChange() throws Exception {
    sonicAudioProcessor.setSpeed(1.5f);
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    sonicAudioProcessor.flush();
    assertThat(sonicAudioProcessor.isActive()).isTrue();
  }

  @Test
  public void testIsActiveWithPitchChange() throws Exception {
    sonicAudioProcessor.setPitch(1.5f);
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    sonicAudioProcessor.flush();
    assertThat(sonicAudioProcessor.isActive()).isTrue();
  }

  @Test
  public void testIsNotActiveWithNoChange() throws Exception {
    sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_16BIT);
    assertThat(sonicAudioProcessor.isActive()).isFalse();
  }

  @Test
  public void testDoesNotSupportNon16BitInput() throws Exception {
    try {
      sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_8BIT);
      fail();
    } catch (AudioProcessor.UnhandledFormatException e) {
      // Expected.
    }
    try {
      sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_24BIT);
      fail();
    } catch (AudioProcessor.UnhandledFormatException e) {
      // Expected.
    }
    try {
      sonicAudioProcessor.configure(44100, 2, C.ENCODING_PCM_32BIT);
      fail();
    } catch (AudioProcessor.UnhandledFormatException e) {
      // Expected.
    }
  }

}