diff --git a/library/core/src/test/assets/id3/apic.id3 b/library/core/src/test/assets/id3/apic.id3 new file mode 100644 index 0000000000..06c130bd9a Binary files /dev/null and b/library/core/src/test/assets/id3/apic.id3 differ diff --git a/library/core/src/test/assets/id3/comm_apic.id3 b/library/core/src/test/assets/id3/comm_apic.id3 new file mode 100644 index 0000000000..683c7b191d Binary files /dev/null and b/library/core/src/test/assets/id3/comm_apic.id3 differ diff --git a/library/core/src/test/assets/ogg/continued_packet_at_start b/library/core/src/test/assets/ogg/continued_packet_at_start new file mode 100644 index 0000000000..ed26aa991f Binary files /dev/null and b/library/core/src/test/assets/ogg/continued_packet_at_start differ diff --git a/library/core/src/test/assets/ogg/continued_packet_over_four_pages b/library/core/src/test/assets/ogg/continued_packet_over_four_pages new file mode 100644 index 0000000000..c5d407a978 Binary files /dev/null and b/library/core/src/test/assets/ogg/continued_packet_over_four_pages differ diff --git a/library/core/src/test/assets/ogg/continued_packet_over_two_pages b/library/core/src/test/assets/ogg/continued_packet_over_two_pages new file mode 100644 index 0000000000..176a7e8401 Binary files /dev/null and b/library/core/src/test/assets/ogg/continued_packet_over_two_pages differ diff --git a/library/core/src/test/assets/ogg/eof_header b/library/core/src/test/assets/ogg/eof_header new file mode 100644 index 0000000000..6b12292e66 Binary files /dev/null and b/library/core/src/test/assets/ogg/eof_header differ diff --git a/library/core/src/test/assets/ogg/flac_header b/library/core/src/test/assets/ogg/flac_header new file mode 100644 index 0000000000..35082319c2 Binary files /dev/null and b/library/core/src/test/assets/ogg/flac_header differ diff --git a/library/core/src/test/assets/ogg/four_packets_with_empty_page b/library/core/src/test/assets/ogg/four_packets_with_empty_page new file mode 100644 index 0000000000..2b991c3aa8 Binary files /dev/null and b/library/core/src/test/assets/ogg/four_packets_with_empty_page differ diff --git a/library/core/src/test/assets/ogg/invalid_header b/library/core/src/test/assets/ogg/invalid_header new file mode 100644 index 0000000000..071e04d058 Binary files /dev/null and b/library/core/src/test/assets/ogg/invalid_header differ diff --git a/library/core/src/test/assets/ogg/invalid_ogg_header b/library/core/src/test/assets/ogg/invalid_ogg_header new file mode 100644 index 0000000000..ebafb5205e Binary files /dev/null and b/library/core/src/test/assets/ogg/invalid_ogg_header differ diff --git a/library/core/src/test/assets/ogg/opus_header b/library/core/src/test/assets/ogg/opus_header new file mode 100644 index 0000000000..071e7cf6bb Binary files /dev/null and b/library/core/src/test/assets/ogg/opus_header differ diff --git a/library/core/src/test/assets/ogg/packet_with_zero_size_terminator b/library/core/src/test/assets/ogg/packet_with_zero_size_terminator new file mode 100644 index 0000000000..d7fbc60810 Binary files /dev/null and b/library/core/src/test/assets/ogg/packet_with_zero_size_terminator differ diff --git a/library/core/src/test/assets/ogg/page_header b/library/core/src/test/assets/ogg/page_header new file mode 100644 index 0000000000..c9688a52f0 Binary files /dev/null and b/library/core/src/test/assets/ogg/page_header differ diff --git a/library/core/src/test/assets/ogg/random_1000_pages b/library/core/src/test/assets/ogg/random_1000_pages new file mode 100644 index 0000000000..ef8552ff3d Binary files /dev/null and b/library/core/src/test/assets/ogg/random_1000_pages differ diff --git a/library/core/src/test/assets/ogg/three_headers b/library/core/src/test/assets/ogg/three_headers new file mode 100644 index 0000000000..d59ff413eb Binary files /dev/null and b/library/core/src/test/assets/ogg/three_headers differ diff --git a/library/core/src/test/assets/ogg/vorbis_header b/library/core/src/test/assets/ogg/vorbis_header new file mode 100644 index 0000000000..bad7fa758a Binary files /dev/null and b/library/core/src/test/assets/ogg/vorbis_header differ diff --git a/library/core/src/test/assets/ogg/zero_sized_packets_at_end_of_stream b/library/core/src/test/assets/ogg/zero_sized_packets_at_end_of_stream new file mode 100644 index 0000000000..e35059a89f Binary files /dev/null and b/library/core/src/test/assets/ogg/zero_sized_packets_at_end_of_stream differ diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/Id3PeekerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/Id3PeekerTest.java index 59e904a5a4..7868b78297 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/Id3PeekerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/Id3PeekerTest.java @@ -16,13 +16,15 @@ package com.google.android.exoplayer2.extractor; +import static com.google.android.exoplayer2.testutil.TestUtil.getByteArray; import static com.google.common.truth.Truth.assertThat; +import androidx.annotation.Nullable; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.id3.ApicFrame; import com.google.android.exoplayer2.metadata.id3.CommentFrame; -import com.google.android.exoplayer2.metadata.id3.Id3DecoderTest; import com.google.android.exoplayer2.testutil.FakeExtractorInput; import java.io.IOException; import org.junit.Test; @@ -41,7 +43,7 @@ public final class Id3PeekerTest { .setData(new byte[] {1, 'I', 'D', '3', 2, 3, 4, 5, 6, 7, 8, 9, 10}) .build(); - Metadata metadata = id3Peeker.peekId3Data(input, /* id3FramePredicate= */ null); + @Nullable Metadata metadata = id3Peeker.peekId3Data(input, /* id3FramePredicate= */ null); assertThat(metadata).isNull(); } @@ -49,17 +51,13 @@ public final class Id3PeekerTest { public void testPeekId3Data_returnId3Tag_ifId3TagPresent() throws IOException, InterruptedException { Id3Peeker id3Peeker = new Id3Peeker(); + FakeExtractorInput input = + new FakeExtractorInput.Builder() + .setData(getByteArray(ApplicationProvider.getApplicationContext(), "id3/apic.id3")) + .build(); - byte[] rawId3 = - Id3DecoderTest.buildSingleFrameTag( - "APIC", - new byte[] { - 3, 105, 109, 97, 103, 101, 47, 106, 112, 101, 103, 0, 16, 72, 101, 108, 108, 111, 32, - 87, 111, 114, 108, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 - }); - FakeExtractorInput input = new FakeExtractorInput.Builder().setData(rawId3).build(); - - Metadata metadata = id3Peeker.peekId3Data(input, /* id3FramePredicate= */ null); + @Nullable Metadata metadata = id3Peeker.peekId3Data(input, /* id3FramePredicate= */ null); + assertThat(metadata).isNotNull(); assertThat(metadata.length()).isEqualTo(1); ApicFrame apicFrame = (ApicFrame) metadata.get(0); @@ -74,28 +72,18 @@ public final class Id3PeekerTest { public void testPeekId3Data_returnId3TagAccordingToGivenPredicate_ifId3TagPresent() throws IOException, InterruptedException { Id3Peeker id3Peeker = new Id3Peeker(); + FakeExtractorInput input = + new FakeExtractorInput.Builder() + .setData(getByteArray(ApplicationProvider.getApplicationContext(), "id3/comm_apic.id3")) + .build(); - byte[] rawId3 = - Id3DecoderTest.buildMultiFramesTag( - new Id3DecoderTest.FrameSpec( - "COMM", - new byte[] { - 3, 101, 110, 103, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 0, 116, - 101, 120, 116, 0 - }), - new Id3DecoderTest.FrameSpec( - "APIC", - new byte[] { - 3, 105, 109, 97, 103, 101, 47, 106, 112, 101, 103, 0, 16, 72, 101, 108, 108, 111, - 32, 87, 111, 114, 108, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 - })); - FakeExtractorInput input = new FakeExtractorInput.Builder().setData(rawId3).build(); - + @Nullable Metadata metadata = id3Peeker.peekId3Data( input, (majorVersion, id0, id1, id2, id3) -> id0 == 'C' && id1 == 'O' && id2 == 'M' && id3 == 'M'); + assertThat(metadata).isNotNull(); assertThat(metadata.length()).isEqualTo(1); CommentFrame commentFrame = (CommentFrame) metadata.get(0); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeekerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeekerTest.java index e97fa878f7..978f12e1ae 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeekerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeekerTest.java @@ -15,9 +15,11 @@ */ package com.google.android.exoplayer2.extractor.ogg; +import static com.google.android.exoplayer2.testutil.TestUtil.getByteArray; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.ExtractorInput; @@ -34,7 +36,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class DefaultOggSeekerTest { - private final Random random = new Random(0); + private final Random random = new Random(/* seed= */ 0); @Test public void testSetupWithUnsetEndPositionFails() { @@ -53,113 +55,24 @@ public final class DefaultOggSeekerTest { } @Test - public void testSeeking() throws IOException, InterruptedException { - Random random = new Random(0); - for (int i = 0; i < 100; i++) { - testSeeking(random); - } - } + public void testSeeking() throws Exception { + byte[] data = + getByteArray(ApplicationProvider.getApplicationContext(), "ogg/random_1000_pages"); + int granuleCount = 49269395; + int firstPayloadPageSize = 2023; + int firstPayloadPageGranuleCount = 57058; + int lastPayloadPageSize = 282; + int lastPayloadPageGranuleCount = 20806; - @Test - public void testSkipToNextPage() throws Exception { - FakeExtractorInput extractorInput = - OggTestData.createInput( - TestUtil.joinByteArrays( - TestUtil.buildTestData(4000, random), - new byte[] {'O', 'g', 'g', 'S'}, - TestUtil.buildTestData(4000, random)), - false); - skipToNextPage(extractorInput); - assertThat(extractorInput.getPosition()).isEqualTo(4000); - } - - @Test - public void testSkipToNextPageOverlap() throws Exception { - FakeExtractorInput extractorInput = - OggTestData.createInput( - TestUtil.joinByteArrays( - TestUtil.buildTestData(2046, random), - new byte[] {'O', 'g', 'g', 'S'}, - TestUtil.buildTestData(4000, random)), - false); - skipToNextPage(extractorInput); - assertThat(extractorInput.getPosition()).isEqualTo(2046); - } - - @Test - public void testSkipToNextPageInputShorterThanPeekLength() throws Exception { - FakeExtractorInput extractorInput = - OggTestData.createInput( - TestUtil.joinByteArrays(new byte[] {'x', 'O', 'g', 'g', 'S'}), false); - skipToNextPage(extractorInput); - assertThat(extractorInput.getPosition()).isEqualTo(1); - } - - @Test - public void testSkipToNextPageNoMatch() throws Exception { - FakeExtractorInput extractorInput = - OggTestData.createInput(new byte[] {'g', 'g', 'S', 'O', 'g', 'g'}, false); - try { - skipToNextPage(extractorInput); - fail(); - } catch (EOFException e) { - // expected - } - } - - @Test - public void testReadGranuleOfLastPage() throws IOException, InterruptedException { - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - TestUtil.buildTestData(100, random), - OggTestData.buildOggHeader(0x00, 20000, 66, 3), - TestUtil.createByteArray(254, 254, 254), // laces - TestUtil.buildTestData(3 * 254, random), - OggTestData.buildOggHeader(0x00, 40000, 67, 3), - TestUtil.createByteArray(254, 254, 254), // laces - TestUtil.buildTestData(3 * 254, random), - OggTestData.buildOggHeader(0x05, 60000, 68, 3), - TestUtil.createByteArray(254, 254, 254), // laces - TestUtil.buildTestData(3 * 254, random)), - false); - assertReadGranuleOfLastPage(input, 60000); - } - - @Test - public void testReadGranuleOfLastPageAfterLastHeader() throws IOException, InterruptedException { - FakeExtractorInput input = OggTestData.createInput(TestUtil.buildTestData(100, random), false); - try { - assertReadGranuleOfLastPage(input, 60000); - fail(); - } catch (EOFException e) { - // Ignored. - } - } - - @Test - public void testReadGranuleOfLastPageWithUnboundedLength() - throws IOException, InterruptedException { - FakeExtractorInput input = OggTestData.createInput(new byte[0], true); - try { - assertReadGranuleOfLastPage(input, 60000); - fail(); - } catch (IllegalArgumentException e) { - // Ignored. - } - } - - private void testSeeking(Random random) throws IOException, InterruptedException { - OggTestFile testFile = OggTestFile.generate(random, 1000); - FakeExtractorInput input = new FakeExtractorInput.Builder().setData(testFile.data).build(); + FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build(); TestStreamReader streamReader = new TestStreamReader(); DefaultOggSeeker oggSeeker = new DefaultOggSeeker( - /* streamReader= */ streamReader, + streamReader, /* payloadStartPosition= */ 0, - /* payloadEndPosition= */ testFile.data.length, - /* firstPayloadPageSize= */ testFile.firstPayloadPageSize, - /* firstPayloadPageGranulePosition= */ testFile.firstPayloadPageGranuleCount, + /* payloadEndPosition= */ data.length, + firstPayloadPageSize, + /* firstPayloadPageGranulePosition= */ firstPayloadPageGranuleCount, /* firstPayloadPageIsLastPage= */ false); OggPageHeader pageHeader = new OggPageHeader(); @@ -177,30 +90,30 @@ public final class DefaultOggSeekerTest { assertThat(input.getPosition()).isEqualTo(0); // Test granule 0 from file end. - granule = seekTo(input, oggSeeker, 0, testFile.data.length - 1); + granule = seekTo(input, oggSeeker, 0, data.length - 1); assertThat(granule).isEqualTo(0); assertThat(input.getPosition()).isEqualTo(0); // Test last granule. - granule = seekTo(input, oggSeeker, testFile.granuleCount - 1, 0); - assertThat(granule).isEqualTo(testFile.granuleCount - testFile.lastPayloadPageGranuleCount); - assertThat(input.getPosition()).isEqualTo(testFile.data.length - testFile.lastPayloadPageSize); + granule = seekTo(input, oggSeeker, granuleCount - 1, 0); + assertThat(granule).isEqualTo(granuleCount - lastPayloadPageGranuleCount); + assertThat(input.getPosition()).isEqualTo(data.length - lastPayloadPageSize); for (int i = 0; i < 100; i += 1) { - long targetGranule = random.nextInt(testFile.granuleCount); - int initialPosition = random.nextInt(testFile.data.length); + long targetGranule = random.nextInt(granuleCount); + int initialPosition = random.nextInt(data.length); granule = seekTo(input, oggSeeker, targetGranule, initialPosition); - long currentPosition = input.getPosition(); + int currentPosition = (int) input.getPosition(); if (granule == 0) { assertThat(currentPosition).isEqualTo(0); } else { - int previousPageStart = testFile.findPreviousPageStart(currentPosition); + int previousPageStart = findPreviousPageStart(data, currentPosition); input.setPosition(previousPageStart); pageHeader.populate(input, false); assertThat(granule).isEqualTo(pageHeader.granulePosition); } - input.setPosition((int) currentPosition); + input.setPosition(currentPosition); pageHeader.populate(input, false); // The target granule should be within the current page. assertThat(granule).isAtMost(targetGranule); @@ -208,6 +121,85 @@ public final class DefaultOggSeekerTest { } } + @Test + public void testSkipToNextPage() throws Exception { + FakeExtractorInput extractorInput = + createInput( + TestUtil.joinByteArrays( + TestUtil.buildTestData(4000, random), + new byte[] {'O', 'g', 'g', 'S'}, + TestUtil.buildTestData(4000, random)), + /* simulateUnknownLength= */ false); + skipToNextPage(extractorInput); + assertThat(extractorInput.getPosition()).isEqualTo(4000); + } + + @Test + public void testSkipToNextPageOverlap() throws Exception { + FakeExtractorInput extractorInput = + createInput( + TestUtil.joinByteArrays( + TestUtil.buildTestData(2046, random), + new byte[] {'O', 'g', 'g', 'S'}, + TestUtil.buildTestData(4000, random)), + /* simulateUnknownLength= */ false); + skipToNextPage(extractorInput); + assertThat(extractorInput.getPosition()).isEqualTo(2046); + } + + @Test + public void testSkipToNextPageInputShorterThanPeekLength() throws Exception { + FakeExtractorInput extractorInput = + createInput( + TestUtil.joinByteArrays(new byte[] {'x', 'O', 'g', 'g', 'S'}), + /* simulateUnknownLength= */ false); + skipToNextPage(extractorInput); + assertThat(extractorInput.getPosition()).isEqualTo(1); + } + + @Test + public void testSkipToNextPageNoMatch() throws Exception { + FakeExtractorInput extractorInput = + createInput(new byte[] {'g', 'g', 'S', 'O', 'g', 'g'}, /* simulateUnknownLength= */ false); + try { + skipToNextPage(extractorInput); + fail(); + } catch (EOFException e) { + // expected + } + } + + @Test + public void testReadGranuleOfLastPage() throws IOException, InterruptedException { + // This test stream has three headers with granule numbers 20000, 40000 and 60000. + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/three_headers"); + FakeExtractorInput input = createInput(data, /* simulateUnknownLength= */ false); + assertReadGranuleOfLastPage(input, 60000); + } + + @Test + public void testReadGranuleOfLastPageAfterLastHeader() throws Exception { + FakeExtractorInput input = + createInput(TestUtil.buildTestData(100, random), /* simulateUnknownLength= */ false); + try { + assertReadGranuleOfLastPage(input, 60000); + fail(); + } catch (EOFException e) { + // Ignored. + } + } + + @Test + public void testReadGranuleOfLastPageWithUnboundedLength() throws Exception { + FakeExtractorInput input = createInput(new byte[0], /* simulateUnknownLength= */ true); + try { + assertReadGranuleOfLastPage(input, 60000); + fail(); + } catch (IllegalArgumentException e) { + // Ignored. + } + } + private static void skipToNextPage(ExtractorInput extractorInput) throws IOException, InterruptedException { DefaultOggSeeker oggSeeker = @@ -248,6 +240,15 @@ public final class DefaultOggSeekerTest { } } + private static FakeExtractorInput createInput(byte[] data, boolean simulateUnknownLength) { + return new FakeExtractorInput.Builder() + .setData(data) + .setSimulateIOErrors(true) + .setSimulateUnknownLength(simulateUnknownLength) + .setSimulatePartialReads(true) + .build(); + } + private static long seekTo( FakeExtractorInput input, DefaultOggSeeker oggSeeker, long targetGranule, int initialPosition) throws IOException, InterruptedException { @@ -264,6 +265,16 @@ public final class DefaultOggSeekerTest { return -(nextSeekPosition + 2); } + private static int findPreviousPageStart(byte[] data, int position) { + for (int i = position - 4; i >= 0; i--) { + if (data[i] == 'O' && data[i + 1] == 'g' && data[i + 2] == 'g' && data[i + 3] == 'S') { + return i; + } + } + fail(); + return -1; + } + private static class TestStreamReader extends StreamReader { @Override protected long preparePayload(ParsableByteArray packet) { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java index b80c8d3892..d9b203319f 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java @@ -15,12 +15,13 @@ */ package com.google.android.exoplayer2.extractor.ogg; +import static com.google.android.exoplayer2.testutil.TestUtil.getByteArray; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.testutil.ExtractorAsserts; import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; import com.google.android.exoplayer2.testutil.FakeExtractorInput; -import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,51 +54,38 @@ public final class OggExtractorTest { @Test public void testSniffVorbis() throws Exception { - byte[] data = - TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x02, 0, 1000, 1), - TestUtil.createByteArray(7), // Laces - new byte[] {0x01, 'v', 'o', 'r', 'b', 'i', 's'}); + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/vorbis_header"); assertSniff(data, /* expectedResult= */ true); } @Test public void testSniffFlac() throws Exception { - byte[] data = - TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x02, 0, 1000, 1), - TestUtil.createByteArray(5), // Laces - new byte[] {0x7F, 'F', 'L', 'A', 'C'}); + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/flac_header"); assertSniff(data, /* expectedResult= */ true); } @Test public void testSniffFailsOpusFile() throws Exception { - byte[] data = - TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x02, 0, 1000, 0x00), new byte[] {'O', 'p', 'u', 's'}); + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/opus_header"); assertSniff(data, /* expectedResult= */ false); } @Test public void testSniffFailsInvalidOggHeader() throws Exception { - byte[] data = OggTestData.buildOggHeader(0x00, 0, 1000, 0x00); + byte[] data = + getByteArray(ApplicationProvider.getApplicationContext(), "ogg/invalid_ogg_header"); assertSniff(data, /* expectedResult= */ false); } @Test public void testSniffInvalidHeader() throws Exception { - byte[] data = - TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x02, 0, 1000, 1), - TestUtil.createByteArray(7), // Laces - new byte[] {0x7F, 'X', 'o', 'r', 'b', 'i', 's'}); + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/invalid_header"); assertSniff(data, /* expectedResult= */ false); } @Test public void testSniffFailsEOF() throws Exception { - byte[] data = OggTestData.buildOggHeader(0x02, 0, 1000, 0x00); + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/eof_header"); assertSniff(data, /* expectedResult= */ false); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPacketTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPacketTest.java index 18a03ddc29..5ca99d2d4e 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPacketTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPacketTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.ogg; +import static com.google.android.exoplayer2.testutil.TestUtil.getByteArray; import static com.google.common.truth.Truth.assertThat; import androidx.test.core.app.ApplicationProvider; @@ -25,7 +26,6 @@ import com.google.android.exoplayer2.util.ParsableByteArray; import java.io.IOException; import java.util.Arrays; import java.util.Random; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,14 +35,8 @@ public final class OggPacketTest { private static final String TEST_FILE = "ogg/bear.opus"; - private Random random; - private OggPacket oggPacket; - - @Before - public void setUp() throws Exception { - random = new Random(0); - oggPacket = new OggPacket(); - } + private final Random random = new Random(/* seed= */ 0); + private final OggPacket oggPacket = new OggPacket(); @Test public void testReadPacketsWithEmptyPage() throws Exception { @@ -50,26 +44,10 @@ public final class OggPacketTest { byte[] secondPacket = TestUtil.buildTestData(272, random); byte[] thirdPacket = TestUtil.buildTestData(256, random); byte[] fourthPacket = TestUtil.buildTestData(271, random); - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - // First page with a single packet. - OggTestData.buildOggHeader(0x02, 0, 1000, 0x01), - TestUtil.createByteArray(0x08), // Laces - firstPacket, - // Second page with a single packet. - OggTestData.buildOggHeader(0x00, 16, 1001, 0x02), - TestUtil.createByteArray(0xFF, 0x11), // Laces - secondPacket, - // Third page with zero packets. - OggTestData.buildOggHeader(0x00, 16, 1002, 0x00), - // Fourth page with two packets. - OggTestData.buildOggHeader(0x04, 128, 1003, 0x04), - TestUtil.createByteArray(0xFF, 0x01, 0xFF, 0x10), // Laces - thirdPacket, - fourthPacket), - true); + createInput( + getByteArray( + ApplicationProvider.getApplicationContext(), "ogg/four_packets_with_empty_page")); assertReadPacket(input, firstPacket); assertThat((oggPacket.getPageHeader().type & 0x02) == 0x02).isTrue(); @@ -113,15 +91,11 @@ public final class OggPacketTest { public void testReadPacketWithZeroSizeTerminator() throws Exception { byte[] firstPacket = TestUtil.buildTestData(255, random); byte[] secondPacket = TestUtil.buildTestData(8, random); - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x06, 0, 1000, 0x04), - TestUtil.createByteArray(0xFF, 0x00, 0x00, 0x08), // Laces. - firstPacket, - secondPacket), - true); + createInput( + getByteArray( + ApplicationProvider.getApplicationContext(), + "ogg/packet_with_zero_size_terminator")); assertReadPacket(input, firstPacket); assertReadPacket(input, secondPacket); @@ -131,19 +105,11 @@ public final class OggPacketTest { @Test public void testReadContinuedPacketOverTwoPages() throws Exception { byte[] firstPacket = TestUtil.buildTestData(518); - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - // First page. - OggTestData.buildOggHeader(0x02, 0, 1000, 0x02), - TestUtil.createByteArray(0xFF, 0xFF), // Laces. - Arrays.copyOf(firstPacket, 510), - // Second page (continued packet). - OggTestData.buildOggHeader(0x05, 10, 1001, 0x01), - TestUtil.createByteArray(0x08), // Laces. - Arrays.copyOfRange(firstPacket, 510, 510 + 8)), - true); + createInput( + getByteArray( + ApplicationProvider.getApplicationContext(), + "ogg/continued_packet_over_two_pages")); assertReadPacket(input, firstPacket); assertThat((oggPacket.getPageHeader().type & 0x04) == 0x04).isTrue(); @@ -156,27 +122,11 @@ public final class OggPacketTest { @Test public void testReadContinuedPacketOverFourPages() throws Exception { byte[] firstPacket = TestUtil.buildTestData(1028); - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - // First page. - OggTestData.buildOggHeader(0x02, 0, 1000, 0x02), - TestUtil.createByteArray(0xFF, 0xFF), // Laces. - Arrays.copyOf(firstPacket, 510), - // Second page (continued packet). - OggTestData.buildOggHeader(0x01, 10, 1001, 0x01), - TestUtil.createByteArray(0xFF), // Laces. - Arrays.copyOfRange(firstPacket, 510, 510 + 255), - // Third page (continued packet). - OggTestData.buildOggHeader(0x01, 10, 1002, 0x01), - TestUtil.createByteArray(0xFF), // Laces. - Arrays.copyOfRange(firstPacket, 510 + 255, 510 + 255 + 255), - // Fourth page (continued packet). - OggTestData.buildOggHeader(0x05, 10, 1003, 0x01), - TestUtil.createByteArray(0x08), // Laces. - Arrays.copyOfRange(firstPacket, 510 + 255 + 255, 510 + 255 + 255 + 8)), - true); + createInput( + getByteArray( + ApplicationProvider.getApplicationContext(), + "ogg/continued_packet_over_four_pages")); assertReadPacket(input, firstPacket); assertThat((oggPacket.getPageHeader().type & 0x04) == 0x04).isTrue(); @@ -189,15 +139,10 @@ public final class OggPacketTest { @Test public void testReadDiscardContinuedPacketAtStart() throws Exception { byte[] pageBody = TestUtil.buildTestData(256 + 8); - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - // Page with a continued packet at start. - OggTestData.buildOggHeader(0x01, 10, 1001, 0x03), - TestUtil.createByteArray(255, 1, 8), // Laces. - pageBody), - true); + createInput( + getByteArray( + ApplicationProvider.getApplicationContext(), "ogg/continued_packet_at_start")); // Expect the first partial packet to be discarded. assertReadPacket(input, Arrays.copyOfRange(pageBody, 256, 256 + 8)); @@ -209,20 +154,11 @@ public final class OggPacketTest { byte[] firstPacket = TestUtil.buildTestData(8, random); byte[] secondPacket = TestUtil.buildTestData(8, random); byte[] thirdPacket = TestUtil.buildTestData(8, random); - FakeExtractorInput input = - OggTestData.createInput( - TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x02, 0, 1000, 0x01), - TestUtil.createByteArray(0x08), // Laces. - firstPacket, - OggTestData.buildOggHeader(0x04, 0, 1001, 0x03), - TestUtil.createByteArray(0x08, 0x00, 0x00), // Laces. - secondPacket, - OggTestData.buildOggHeader(0x04, 0, 1002, 0x03), - TestUtil.createByteArray(0x08, 0x00, 0x00), // Laces. - thirdPacket), - true); + createInput( + getByteArray( + ApplicationProvider.getApplicationContext(), + "ogg/zero_sized_packets_at_end_of_stream")); assertReadPacket(input, firstPacket); assertReadPacket(input, secondPacket); @@ -241,6 +177,15 @@ public final class OggPacketTest { assertThat(packetCounter).isEqualTo(277); } + private static FakeExtractorInput createInput(byte[] data) { + return new FakeExtractorInput.Builder() + .setData(data) + .setSimulateIOErrors(true) + .setSimulateUnknownLength(true) + .setSimulatePartialReads(true) + .build(); + } + private void assertReadPacket(FakeExtractorInput extractorInput, byte[] expected) throws IOException, InterruptedException { assertThat(readPacket(extractorInput)).isTrue(); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPageHeaderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPageHeaderTest.java index 4d9e08a12d..7ce346ee8d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPageHeaderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggPageHeaderTest.java @@ -15,13 +15,14 @@ */ package com.google.android.exoplayer2.extractor.ogg; +import static com.google.android.exoplayer2.testutil.TestUtil.getByteArray; import static com.google.common.truth.Truth.assertThat; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.testutil.FakeExtractorInput; import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException; import com.google.android.exoplayer2.testutil.TestUtil; -import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,13 +31,12 @@ import org.junit.runner.RunWith; public final class OggPageHeaderTest { @Test - public void testPopulatePageHeader() throws IOException, InterruptedException { - FakeExtractorInput input = OggTestData.createInput(TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x01, 123456, 4, 2), - TestUtil.createByteArray(2, 2) - ), true); + public void testPopulatePageHeader() throws Exception { + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/page_header"); + + FakeExtractorInput input = createInput(data, /* simulateUnknownLength= */ true); OggPageHeader header = new OggPageHeader(); - populatePageHeader(input, header, false); + populatePageHeader(input, header, /* quiet= */ false); assertThat(header.type).isEqualTo(0x01); assertThat(header.headerSize).isEqualTo(27 + 2); @@ -50,51 +50,51 @@ public final class OggPageHeaderTest { } @Test - public void testPopulatePageHeaderQuiteOnExceptionLessThan27Bytes() - throws IOException, InterruptedException { - FakeExtractorInput input = OggTestData.createInput(TestUtil.createByteArray(2, 2), false); + public void testPopulatePageHeaderQuietOnExceptionLessThan27Bytes() throws Exception { + FakeExtractorInput input = + createInput(TestUtil.createByteArray(2, 2), /* simulateUnknownLength= */ false); OggPageHeader header = new OggPageHeader(); - assertThat(populatePageHeader(input, header, true)).isFalse(); + assertThat(populatePageHeader(input, header, /* quiet= */ true)).isFalse(); } @Test - public void testPopulatePageHeaderQuiteOnExceptionNotOgg() - throws IOException, InterruptedException { - byte[] headerBytes = TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x01, 123456, 4, 2), - TestUtil.createByteArray(2, 2) - ); + public void testPopulatePageHeaderQuietOnExceptionNotOgg() throws Exception { + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/page_header"); // change from 'O' to 'o' - headerBytes[0] = 'o'; - FakeExtractorInput input = OggTestData.createInput(headerBytes, false); + data[0] = 'o'; + FakeExtractorInput input = createInput(data, /* simulateUnknownLength= */ false); OggPageHeader header = new OggPageHeader(); - assertThat(populatePageHeader(input, header, true)).isFalse(); + assertThat(populatePageHeader(input, header, /* quiet= */ true)).isFalse(); } @Test - public void testPopulatePageHeaderQuiteOnExceptionWrongRevision() - throws IOException, InterruptedException { - byte[] headerBytes = TestUtil.joinByteArrays( - OggTestData.buildOggHeader(0x01, 123456, 4, 2), - TestUtil.createByteArray(2, 2) - ); + public void testPopulatePageHeaderQuiteOnExceptionWrongRevision() throws Exception { + byte[] data = getByteArray(ApplicationProvider.getApplicationContext(), "ogg/page_header"); // change revision from 0 to 1 - headerBytes[4] = 0x01; - FakeExtractorInput input = OggTestData.createInput(headerBytes, false); + data[4] = 0x01; + FakeExtractorInput input = createInput(data, /* simulateUnknownLength= */ false); OggPageHeader header = new OggPageHeader(); - assertThat(populatePageHeader(input, header, true)).isFalse(); + assertThat(populatePageHeader(input, header, /* quiet= */ true)).isFalse(); } - private boolean populatePageHeader(FakeExtractorInput input, OggPageHeader header, - boolean quite) throws IOException, InterruptedException { + private static boolean populatePageHeader( + FakeExtractorInput input, OggPageHeader header, boolean quiet) throws Exception { while (true) { try { - return header.populate(input, quite); + return header.populate(input, quiet); } catch (SimulatedIOException e) { // ignored } } } + private static FakeExtractorInput createInput(byte[] data, boolean simulateUnknownLength) { + return new FakeExtractorInput.Builder() + .setData(data) + .setSimulateIOErrors(true) + .setSimulateUnknownLength(simulateUnknownLength) + .setSimulatePartialReads(true) + .build(); + } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggTestData.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggTestData.java deleted file mode 100644 index 1cd3d5e5d2..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggTestData.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 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.extractor.ogg; - -import com.google.android.exoplayer2.testutil.FakeExtractorInput; -import com.google.android.exoplayer2.testutil.TestUtil; - -/** Provides ogg/vorbis test data in bytes for unit tests. */ -/* package */ final class OggTestData { - - public static FakeExtractorInput createInput(byte[] data, boolean simulateUnknownLength) { - return new FakeExtractorInput.Builder() - .setData(data) - .setSimulateIOErrors(true) - .setSimulateUnknownLength(simulateUnknownLength) - .setSimulatePartialReads(true) - .build(); - } - - public static byte[] buildOggHeader( - int headerType, long granule, int pageSequenceCounter, int pageSegmentCount) { - return TestUtil.createByteArray( - 0x4F, - 0x67, - 0x67, - 0x53, // Oggs. - 0x00, // Stream revision. - headerType, - (int) (granule) & 0xFF, - (int) (granule >> 8) & 0xFF, - (int) (granule >> 16) & 0xFF, - (int) (granule >> 24) & 0xFF, - (int) (granule >> 32) & 0xFF, - (int) (granule >> 40) & 0xFF, - (int) (granule >> 48) & 0xFF, - (int) (granule >> 56) & 0xFF, - 0x00, // LSB of data serial number. - 0x10, - 0x00, - 0x00, // MSB of data serial number. - (pageSequenceCounter) & 0xFF, - (pageSequenceCounter >> 8) & 0xFF, - (pageSequenceCounter >> 16) & 0xFF, - (pageSequenceCounter >> 24) & 0xFF, - 0x00, // LSB of page checksum. - 0x00, - 0x10, - 0x00, // MSB of page checksum. - pageSegmentCount); - } - -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggTestFile.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggTestFile.java deleted file mode 100644 index a334c5128e..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ogg/OggTestFile.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2016 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.extractor.ogg; - -import static org.junit.Assert.fail; - -import com.google.android.exoplayer2.testutil.TestUtil; -import java.util.ArrayList; -import java.util.Random; - -/** Generates test data. */ -/* package */ final class OggTestFile { - - private static final int MAX_PACKET_LENGTH = 2048; - private static final int MAX_SEGMENT_COUNT = 10; - private static final int MAX_GRANULES_IN_PAGE = 100000; - - public final byte[] data; - public final int granuleCount; - public final int pageCount; - public final int firstPayloadPageSize; - public final int firstPayloadPageGranuleCount; - public final int lastPayloadPageSize; - public final int lastPayloadPageGranuleCount; - - private OggTestFile( - byte[] data, - int granuleCount, - int pageCount, - int firstPayloadPageSize, - int firstPayloadPageGranuleCount, - int lastPayloadPageSize, - int lastPayloadPageGranuleCount) { - this.data = data; - this.granuleCount = granuleCount; - this.pageCount = pageCount; - this.firstPayloadPageSize = firstPayloadPageSize; - this.firstPayloadPageGranuleCount = firstPayloadPageGranuleCount; - this.lastPayloadPageSize = lastPayloadPageSize; - this.lastPayloadPageGranuleCount = lastPayloadPageGranuleCount; - } - - public static OggTestFile generate(Random random, int pageCount) { - ArrayList fileData = new ArrayList<>(); - int fileSize = 0; - int granuleCount = 0; - int firstPayloadPageSize = 0; - int firstPayloadPageGranuleCount = 0; - int lastPageloadPageSize = 0; - int lastPayloadPageGranuleCount = 0; - int packetLength = -1; - - for (int i = 0; i < pageCount; i++) { - int headerType = 0x00; - if (packetLength >= 0) { - headerType |= 1; - } - if (i == 0) { - headerType |= 2; - } - if (i == pageCount - 1) { - headerType |= 4; - } - int pageGranuleCount = random.nextInt(MAX_GRANULES_IN_PAGE - 1) + 1; - int pageSegmentCount = random.nextInt(MAX_SEGMENT_COUNT); - granuleCount += pageGranuleCount; - byte[] header = OggTestData.buildOggHeader(headerType, granuleCount, 0, pageSegmentCount); - fileData.add(header); - int pageSize = header.length; - - byte[] laces = new byte[pageSegmentCount]; - int bodySize = 0; - for (int j = 0; j < pageSegmentCount; j++) { - if (packetLength < 0) { - if (i < pageCount - 1) { - packetLength = random.nextInt(MAX_PACKET_LENGTH); - } else { - int maxPacketLength = 255 * (pageSegmentCount - j) - 1; - packetLength = random.nextInt(maxPacketLength); - } - } else if (i == pageCount - 1 && j == pageSegmentCount - 1) { - packetLength = Math.min(packetLength, 254); - } - laces[j] = (byte) Math.min(packetLength, 255); - bodySize += laces[j] & 0xFF; - packetLength -= 255; - } - fileData.add(laces); - pageSize += laces.length; - - byte[] payload = TestUtil.buildTestData(bodySize, random); - fileData.add(payload); - pageSize += payload.length; - - fileSize += pageSize; - if (i == 0) { - firstPayloadPageSize = pageSize; - firstPayloadPageGranuleCount = pageGranuleCount; - } else if (i == pageCount - 1) { - lastPageloadPageSize = pageSize; - lastPayloadPageGranuleCount = pageGranuleCount; - } - } - - byte[] file = new byte[fileSize]; - int position = 0; - for (byte[] data : fileData) { - System.arraycopy(data, 0, file, position, data.length); - position += data.length; - } - return new OggTestFile( - file, - granuleCount, - pageCount, - firstPayloadPageSize, - firstPayloadPageGranuleCount, - lastPageloadPageSize, - lastPayloadPageGranuleCount); - } - - public int findPreviousPageStart(long position) { - for (int i = (int) (position - 4); i >= 0; i--) { - if (data[i] == 'O' && data[i + 1] == 'g' && data[i + 2] == 'g' && data[i + 3] == 'S') { - return i; - } - } - fail(); - return -1; - } -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java index 34ed88d2d0..11f967dcdc 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java @@ -16,8 +16,7 @@ package com.google.android.exoplayer2.mediacodec; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.areEqual; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.waitUntilAllEventsAreExecuted; +import static com.google.android.exoplayer2.testutil.TestUtil.assertBufferInfosEqual; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; @@ -28,12 +27,12 @@ import android.os.HandlerThread; import android.os.Looper; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.io.IOException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Shadows; /** Unit tests for {@link AsynchronousMediaCodecAdapter}. */ @RunWith(AndroidJUnit4.class) @@ -87,8 +86,7 @@ public class AsynchronousMediaCodecAdapterTest { } @Test - public void dequeueInputBufferIndex_afterFlushCompletes_returnsNextInputBuffer() - throws InterruptedException { + public void dequeueInputBufferIndex_afterFlushCompletes_returnsNextInputBuffer() { adapter.start(); Handler handler = new Handler(looper); handler.post( @@ -97,13 +95,13 @@ public class AsynchronousMediaCodecAdapterTest { handler.post( () -> adapter.getMediaCodecCallback().onInputBufferAvailable(codec, /* index=*/ 1)); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(1); } @Test - public void dequeueInputBufferIndex_afterFlushCompletesWithError_throwsException() - throws InterruptedException { + public void dequeueInputBufferIndex_afterFlushCompletesWithError_throwsException() { AtomicInteger calls = new AtomicInteger(0); adapter.setCodecStartRunnable( () -> { @@ -114,7 +112,8 @@ public class AsynchronousMediaCodecAdapterTest { adapter.start(); adapter.flush(); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThrows( IllegalStateException.class, () -> { @@ -138,7 +137,7 @@ public class AsynchronousMediaCodecAdapterTest { adapter.getMediaCodecCallback().onOutputBufferAvailable(codec, /* index=*/ 0, outBufferInfo); assertThat(adapter.dequeueOutputBufferIndex(bufferInfo)).isEqualTo(0); - assertThat(areEqual(bufferInfo, outBufferInfo)).isTrue(); + assertBufferInfosEqual(bufferInfo, outBufferInfo); } @Test @@ -153,8 +152,7 @@ public class AsynchronousMediaCodecAdapterTest { } @Test - public void dequeueOutputBufferIndex_afterFlushCompletes_returnsNextOutputBuffer() - throws InterruptedException { + public void dequeueOutputBufferIndex_afterFlushCompletes_returnsNextOutputBuffer() { adapter.start(); Handler handler = new Handler(looper); MediaCodec.BufferInfo info0 = new MediaCodec.BufferInfo(); @@ -166,14 +164,14 @@ public class AsynchronousMediaCodecAdapterTest { handler.post( () -> adapter.getMediaCodecCallback().onOutputBufferAvailable(codec, /* index=*/ 1, info1)); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.dequeueOutputBufferIndex(bufferInfo)).isEqualTo(1); - assertThat(areEqual(bufferInfo, info1)).isTrue(); + assertBufferInfosEqual(info1, bufferInfo); } @Test - public void dequeueOutputBufferIndex_afterFlushCompletesWithError_throwsException() - throws InterruptedException { + public void dequeueOutputBufferIndex_afterFlushCompletesWithError_throwsException() { AtomicInteger calls = new AtomicInteger(0); adapter.setCodecStartRunnable( () -> { @@ -184,7 +182,8 @@ public class AsynchronousMediaCodecAdapterTest { adapter.start(); adapter.flush(); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo)); } @@ -213,26 +212,28 @@ public class AsynchronousMediaCodecAdapterTest { } @Test - public void getOutputFormat_afterFlush_returnsPreviousFormat() throws InterruptedException { + public void getOutputFormat_afterFlush_returnsPreviousFormat() { adapter.start(); MediaFormat format = new MediaFormat(); adapter.getMediaCodecCallback().onOutputFormatChanged(codec, format); adapter.dequeueOutputBufferIndex(bufferInfo); adapter.flush(); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.getOutputFormat()).isEqualTo(format); } @Test - public void shutdown_withPendingFlush_cancelsFlush() throws InterruptedException { + public void shutdown_withPendingFlush_cancelsFlush() { AtomicInteger onCodecStartCalled = new AtomicInteger(0); adapter.setCodecStartRunnable(() -> onCodecStartCalled.incrementAndGet()); adapter.start(); adapter.flush(); adapter.shutdown(); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(onCodecStartCalled.get()).isEqualTo(1); } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapterTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapterTest.java index f974144dd6..01fcdb0711 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapterTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapterTest.java @@ -16,8 +16,7 @@ package com.google.android.exoplayer2.mediacodec; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.areEqual; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.waitUntilAllEventsAreExecuted; +import static com.google.android.exoplayer2.testutil.TestUtil.assertBufferInfosEqual; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import static org.robolectric.Shadows.shadowOf; @@ -29,13 +28,13 @@ import android.os.HandlerThread; import android.os.Looper; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.io.IOException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Shadows; import org.robolectric.shadows.ShadowLooper; /** Unit tests for {@link DedicatedThreadAsyncMediaCodecAdapter}. */ @@ -44,7 +43,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { private DedicatedThreadAsyncMediaCodecAdapter adapter; private MediaCodec codec; private TestHandlerThread handlerThread; - private MediaCodec.BufferInfo bufferInfo = null; + private MediaCodec.BufferInfo bufferInfo; @Before public void setUp() throws IOException { @@ -69,8 +68,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { } @Test - public void dequeueInputBufferIndex_withAfterFlushFailed_throwsException() - throws InterruptedException { + public void dequeueInputBufferIndex_withAfterFlushFailed_throwsException() { AtomicInteger codecStartCalls = new AtomicInteger(0); adapter.setCodecStartRunnable( () -> { @@ -81,11 +79,8 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { adapter.start(); adapter.flush(); - assertThat( - waitUntilAllEventsAreExecuted( - handlerThread.getLooper(), /* time= */ 5, TimeUnit.SECONDS)) - .isTrue(); - + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex()); } @@ -114,8 +109,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { } @Test - public void dequeueInputBufferIndex_withFlushCompletedAndInputBuffer_returnsInputBuffer() - throws InterruptedException { + public void dequeueInputBufferIndex_withFlushCompletedAndInputBuffer_returnsInputBuffer() { adapter.start(); Looper looper = handlerThread.getLooper(); Handler handler = new Handler(looper); @@ -128,8 +122,8 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { // Enqueue another onInputBufferAvailable after the flush event handler.post(() -> adapter.onInputBufferAvailable(codec, 10)); - // Wait until all tasks have been handled - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(10); } @@ -142,8 +136,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { } @Test - public void dequeueOutputBufferIndex_withInternalException_throwsException() - throws InterruptedException { + public void dequeueOutputBufferIndex_withInternalException_throwsException() { AtomicInteger codecStartCalls = new AtomicInteger(0); adapter.setCodecStartRunnable( () -> { @@ -154,10 +147,8 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { adapter.start(); adapter.flush(); - assertThat( - waitUntilAllEventsAreExecuted( - handlerThread.getLooper(), /* time= */ 5, TimeUnit.SECONDS)) - .isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo)); } @@ -176,7 +167,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { adapter.onOutputBufferAvailable(codec, 0, enqueuedBufferInfo); assertThat(adapter.dequeueOutputBufferIndex((bufferInfo))).isEqualTo(0); - assertThat(areEqual(bufferInfo, enqueuedBufferInfo)).isTrue(); + assertBufferInfosEqual(enqueuedBufferInfo, bufferInfo); } @Test @@ -190,8 +181,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { } @Test - public void dequeueOutputBufferIndex_withFlushCompletedAndOutputBuffer_returnsOutputBuffer() - throws InterruptedException { + public void dequeueOutputBufferIndex_withFlushCompletedAndOutputBuffer_returnsOutputBuffer() { adapter.start(); Looper looper = handlerThread.getLooper(); Handler handler = new Handler(looper); @@ -208,10 +198,10 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { lastBufferInfo.presentationTimeUs = 10; handler.post(() -> adapter.onOutputBufferAvailable(codec, 10, lastBufferInfo)); - // Wait until all tasks have been handled - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.dequeueOutputBufferIndex(bufferInfo)).isEqualTo(10); - assertThat(areEqual(bufferInfo, lastBufferInfo)).isTrue(); + assertBufferInfosEqual(lastBufferInfo, bufferInfo); } @Test @@ -250,7 +240,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { } @Test - public void getOutputFormat_afterFlush_returnsPreviousFormat() throws InterruptedException { + public void getOutputFormat_afterFlush_returnsPreviousFormat() { MediaFormat format = new MediaFormat(); adapter.start(); adapter.onOutputFormatChanged(codec, format); @@ -260,15 +250,14 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { assertThat(adapter.getOutputFormat()).isEqualTo(format); adapter.flush(); - assertThat( - waitUntilAllEventsAreExecuted( - handlerThread.getLooper(), /* time= */ 5, TimeUnit.SECONDS)) - .isTrue(); + + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.getOutputFormat()).isEqualTo(format); } @Test - public void flush_multipleTimes_onlyLastFlushExecutes() throws InterruptedException { + public void flush_multipleTimes_onlyLastFlushExecutes() { AtomicInteger codecStartCalls = new AtomicInteger(0); adapter.setCodecStartRunnable(() -> codecStartCalls.incrementAndGet()); adapter.start(); @@ -290,22 +279,22 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest { } assertThat(codecStartCalls.get()).isEqualTo(1); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + shadowLooper.idle(); assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(3); assertThat(codecStartCalls.get()).isEqualTo(2); } @Test - public void flush_andImmediatelyShutdown_flushIsNoOp() throws InterruptedException { + public void flush_andImmediatelyShutdown_flushIsNoOp() { AtomicInteger onCodecStartCount = new AtomicInteger(0); adapter.setCodecStartRunnable(() -> onCodecStartCount.incrementAndGet()); adapter.start(); - // Obtain looper when adapter is started - Looper looper = handlerThread.getLooper(); adapter.flush(); adapter.shutdown(); - assertThat(waitUntilAllEventsAreExecuted(looper, 5, TimeUnit.SECONDS)).isTrue(); + // Wait until all tasks have been handled. + Shadows.shadowOf(handlerThread.getLooper()).idle(); // Only adapter.start() calls onCodecStart. assertThat(onCodecStartCount.get()).isEqualTo(1); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecAsyncCallbackTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecAsyncCallbackTest.java index 5b6af91110..0161b541f1 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecAsyncCallbackTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecAsyncCallbackTest.java @@ -15,7 +15,7 @@ */ package com.google.android.exoplayer2.mediacodec; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.areEqual; +import static com.google.android.exoplayer2.testutil.TestUtil.assertBufferInfosEqual; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; @@ -101,9 +101,9 @@ public class MediaCodecAsyncCallbackTest { MediaCodec.BufferInfo outBufferInfo = new MediaCodec.BufferInfo(); assertThat(mediaCodecAsyncCallback.dequeueOutputBufferIndex(outBufferInfo)).isEqualTo(0); - assertThat(areEqual(outBufferInfo, bufferInfo1)).isTrue(); + assertBufferInfosEqual(bufferInfo1, outBufferInfo); assertThat(mediaCodecAsyncCallback.dequeueOutputBufferIndex(outBufferInfo)).isEqualTo(1); - assertThat(areEqual(outBufferInfo, bufferInfo2)).isTrue(); + assertBufferInfosEqual(bufferInfo2, outBufferInfo); assertThat(mediaCodecAsyncCallback.dequeueOutputBufferIndex(outBufferInfo)) .isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecTestUtils.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecTestUtils.java deleted file mode 100644 index ea816be4aa..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MediaCodecTestUtils.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 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.mediacodec; - -import static org.robolectric.Shadows.shadowOf; - -import android.media.MediaCodec; -import android.os.Handler; -import android.os.Looper; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** Testing utilities for MediaCodec related test classes */ -public class MediaCodecTestUtils { - /** - * Compares if two {@link android.media.MediaCodec.BufferInfo} are equal by inspecting {@link - * android.media.MediaCodec.BufferInfo#flags}, {@link android.media.MediaCodec.BufferInfo#size}, - * {@link android.media.MediaCodec.BufferInfo#presentationTimeUs} and {@link - * android.media.MediaCodec.BufferInfo#offset}. - */ - public static boolean areEqual(MediaCodec.BufferInfo lhs, MediaCodec.BufferInfo rhs) { - return lhs.flags == rhs.flags - && lhs.offset == rhs.offset - && lhs.presentationTimeUs == rhs.presentationTimeUs - && lhs.size == rhs.size; - } - - /** - * Blocks until all events of a shadow looper are executed or the specified time elapses. - * - * @param looper the shadow looper - * @param time the time to wait - * @param unit the time units - * @return true if all events are executed, false if the time elapsed. - * @throws InterruptedException if the Thread was interrupted while waiting. - */ - public static boolean waitUntilAllEventsAreExecuted(Looper looper, long time, TimeUnit unit) - throws InterruptedException { - Handler handler = new Handler(looper); - CountDownLatch latch = new CountDownLatch(1); - handler.post(() -> latch.countDown()); - shadowOf(looper).idle(); - return latch.await(time, unit); - } -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapterTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapterTest.java index c31b86db39..bc9baab577 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapterTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapterTest.java @@ -16,8 +16,7 @@ package com.google.android.exoplayer2.mediacodec; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.areEqual; -import static com.google.android.exoplayer2.mediacodec.MediaCodecTestUtils.waitUntilAllEventsAreExecuted; +import static com.google.android.exoplayer2.testutil.TestUtil.assertBufferInfosEqual; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import static org.robolectric.Shadows.shadowOf; @@ -29,13 +28,13 @@ import android.os.HandlerThread; import android.os.Looper; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.io.IOException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Shadows; import org.robolectric.shadows.ShadowLooper; /** Unit tests for {@link MultiLockAsyncMediaCodecAdapter}. */ @@ -43,7 +42,7 @@ import org.robolectric.shadows.ShadowLooper; public class MultiLockAsyncMediaCodecAdapterTest { private MultiLockAsyncMediaCodecAdapter adapter; private MediaCodec codec; - private MediaCodec.BufferInfo bufferInfo = null; + private MediaCodec.BufferInfo bufferInfo; private TestHandlerThread handlerThread; @Before @@ -81,10 +80,7 @@ public class MultiLockAsyncMediaCodecAdapterTest { adapter.start(); adapter.flush(); - assertThat( - waitUntilAllEventsAreExecuted( - handlerThread.getLooper(), /* time= */ 5, TimeUnit.SECONDS)) - .isTrue(); + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex()); } @@ -128,7 +124,7 @@ public class MultiLockAsyncMediaCodecAdapterTest { handler.post(() -> adapter.onInputBufferAvailable(codec, 10)); // Wait until all tasks have been handled - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(10); } @@ -154,10 +150,7 @@ public class MultiLockAsyncMediaCodecAdapterTest { adapter.start(); adapter.flush(); - assertThat( - waitUntilAllEventsAreExecuted( - handlerThread.getLooper(), /* time= */ 5, TimeUnit.SECONDS)) - .isTrue(); + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo)); } @@ -176,7 +169,7 @@ public class MultiLockAsyncMediaCodecAdapterTest { adapter.onOutputBufferAvailable(codec, 0, enqueuedBufferInfo); assertThat(adapter.dequeueOutputBufferIndex((bufferInfo))).isEqualTo(0); - assertThat(areEqual(bufferInfo, enqueuedBufferInfo)).isTrue(); + assertBufferInfosEqual(enqueuedBufferInfo, bufferInfo); } @Test @@ -209,9 +202,9 @@ public class MultiLockAsyncMediaCodecAdapterTest { handler.post(() -> adapter.onOutputBufferAvailable(codec, 10, lastBufferInfo)); // Wait until all tasks have been handled - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.dequeueOutputBufferIndex(bufferInfo)).isEqualTo(10); - assertThat(areEqual(bufferInfo, lastBufferInfo)).isTrue(); + assertBufferInfosEqual(lastBufferInfo, bufferInfo); } @Test @@ -250,7 +243,7 @@ public class MultiLockAsyncMediaCodecAdapterTest { } @Test - public void getOutputFormat_afterFlush_returnsPreviousFormat() throws InterruptedException { + public void getOutputFormat_afterFlush_returnsPreviousFormat() { MediaFormat format = new MediaFormat(); adapter.start(); adapter.onOutputFormatChanged(codec, format); @@ -260,15 +253,12 @@ public class MultiLockAsyncMediaCodecAdapterTest { assertThat(adapter.getOutputFormat()).isEqualTo(format); adapter.flush(); - assertThat( - waitUntilAllEventsAreExecuted( - handlerThread.getLooper(), /* time= */ 5, TimeUnit.SECONDS)) - .isTrue(); + Shadows.shadowOf(handlerThread.getLooper()).idle(); assertThat(adapter.getOutputFormat()).isEqualTo(format); } @Test - public void flush_multipleTimes_onlyLastFlushExecutes() throws InterruptedException { + public void flush_multipleTimes_onlyLastFlushExecutes() { AtomicInteger codecStartCalls = new AtomicInteger(0); adapter.setCodecStartRunnable(() -> codecStartCalls.incrementAndGet()); adapter.start(); @@ -290,22 +280,20 @@ public class MultiLockAsyncMediaCodecAdapterTest { } assertThat(codecStartCalls.get()).isEqualTo(1); - assertThat(waitUntilAllEventsAreExecuted(looper, /* time= */ 5, TimeUnit.SECONDS)).isTrue(); + shadowLooper.idle(); assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(3); assertThat(codecStartCalls.get()).isEqualTo(2); } @Test - public void flush_andImmediatelyShutdown_flushIsNoOp() throws InterruptedException { + public void flush_andImmediatelyShutdown_flushIsNoOp() { AtomicInteger codecStartCalls = new AtomicInteger(0); adapter.setCodecStartRunnable(() -> codecStartCalls.incrementAndGet()); adapter.start(); - // Obtain looper when adapter is started. - Looper looper = handlerThread.getLooper(); adapter.flush(); adapter.shutdown(); - assertThat(waitUntilAllEventsAreExecuted(looper, 5, TimeUnit.SECONDS)).isTrue(); + Shadows.shadowOf(handlerThread.getLooper()).idle(); // Only adapter.start() called codec#start() assertThat(codecStartCalls.get()).isEqualTo(1); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java index d7664e21ca..cc1ae4b71b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java @@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.database.DatabaseIOException; import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.VersionTable; +import com.google.android.exoplayer2.testutil.DownloadBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java index cdb113918e..19108b8a6b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java @@ -24,6 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.offline.Download.State; import com.google.android.exoplayer2.scheduler.Requirements; +import com.google.android.exoplayer2.testutil.DownloadBuilder; import com.google.android.exoplayer2.testutil.DummyMainThread; import com.google.android.exoplayer2.testutil.DummyMainThread.TestRunnable; import com.google.android.exoplayer2.testutil.TestDownloadManagerListener; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSchemeDataSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSchemeDataSourceTest.java index 8cb142f05d..680674a16c 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSchemeDataSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSchemeDataSourceTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.fail; import android.net.Uri; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.util.Util; import java.io.IOException; import org.junit.Before; @@ -45,7 +46,7 @@ public final class DataSchemeDataSourceTest { @Test public void testBase64Data() throws IOException { DataSpec dataSpec = buildDataSpec(DATA_SCHEME_URI); - DataSourceAsserts.assertDataSourceContent( + assertDataSourceContent( schemeDataDataSource, dataSpec, Util.getUtf8Bytes( @@ -55,7 +56,7 @@ public final class DataSchemeDataSourceTest { @Test public void testAsciiData() throws IOException { - DataSourceAsserts.assertDataSourceContent( + assertDataSourceContent( schemeDataDataSource, buildDataSpec("data:,A%20brief%20note"), Util.getUtf8Bytes("A brief note")); @@ -78,22 +79,21 @@ public final class DataSchemeDataSourceTest { public void testSequentialRangeRequests() throws IOException { DataSpec dataSpec = buildDataSpec(DATA_SCHEME_URI, /* position= */ 1, /* length= */ C.LENGTH_UNSET); - DataSourceAsserts.assertDataSourceContent( + assertDataSourceContent( schemeDataDataSource, dataSpec, Util.getUtf8Bytes( "\"provider\":\"widevine_test\",\"content_id\":\"MjAxNV90ZWFycw==\",\"key_ids\":" + "[\"00000000000000000000000000000000\"]}")); dataSpec = buildDataSpec(DATA_SCHEME_URI, /* position= */ 10, /* length= */ C.LENGTH_UNSET); - DataSourceAsserts.assertDataSourceContent( + assertDataSourceContent( schemeDataDataSource, dataSpec, Util.getUtf8Bytes( "\":\"widevine_test\",\"content_id\":\"MjAxNV90ZWFycw==\",\"key_ids\":" + "[\"00000000000000000000000000000000\"]}")); dataSpec = buildDataSpec(DATA_SCHEME_URI, /* position= */ 15, /* length= */ 5); - DataSourceAsserts.assertDataSourceContent( - schemeDataDataSource, dataSpec, Util.getUtf8Bytes("devin")); + assertDataSourceContent(schemeDataDataSource, dataSpec, Util.getUtf8Bytes("devin")); } @Test @@ -154,4 +154,23 @@ public final class DataSchemeDataSourceTest { return new DataSpec(Uri.parse(uriString), position, length, /* key= */ null); } + /** + * Asserts that data read from a {@link DataSource} matches {@code expected}. + * + * @param dataSource The {@link DataSource} through which to read. + * @param dataSpec The {@link DataSpec} to use when opening the {@link DataSource}. + * @param expectedData The expected data. + * @throws IOException If an error occurs reading fom the {@link DataSource}. + */ + private static void assertDataSourceContent( + DataSource dataSource, DataSpec dataSpec, byte[] expectedData) throws IOException { + try { + long length = dataSource.open(dataSpec); + assertThat(length).isEqualTo(expectedData.length); + byte[] readData = TestUtil.readToEnd(dataSource); + assertThat(readData).isEqualTo(expectedData); + } finally { + dataSource.close(); + } + } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSourceAsserts.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSourceAsserts.java deleted file mode 100644 index eff3245923..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/DataSourceAsserts.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.upstream; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.android.exoplayer2.testutil.TestUtil; -import java.io.IOException; - -/** - * Assertions for data source tests. - */ -/* package */ final class DataSourceAsserts { - - /** - * Asserts that data read from a {@link DataSource} matches {@code expected}. - * - * @param dataSource The {@link DataSource} through which to read. - * @param dataSpec The {@link DataSpec} to use when opening the {@link DataSource}. - * @param expectedData The expected data. - * @throws IOException If an error occurs reading fom the {@link DataSource}. - */ - public static void assertDataSourceContent(DataSource dataSource, DataSpec dataSpec, - byte[] expectedData) throws IOException { - try { - long length = dataSource.open(dataSpec); - assertThat(length).isEqualTo(expectedData.length); - byte[] readData = TestUtil.readToEnd(dataSource); - assertThat(readData).isEqualTo(expectedData); - } finally { - dataSource.close(); - } - } - - private DataSourceAsserts() {} - -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java index fda57cbce4..ecb406efd0 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.upstream.cache; +import static com.google.android.exoplayer2.testutil.TestUtil.createTestFile; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -103,12 +104,9 @@ public class CachedContentIndexTest { // add a span int cacheFileLength = 20; File cacheSpanFile = - SimpleCacheSpanTest.createCacheSpanFile( - cacheDir, - cachedContent1.id, - /* offset= */ 10, - cacheFileLength, - /* lastTouchTimestamp= */ 30); + SimpleCacheSpan.getCacheFile( + cacheDir, cachedContent1.id, /* position= */ 10, /* timestamp= */ 30); + createTestFile(cacheSpanFile, cacheFileLength); SimpleCacheSpan span = SimpleCacheSpan.createCacheEntry(cacheSpanFile, cacheFileLength, index); assertThat(span).isNotNull(); cachedContent1.addSpan(span); @@ -288,12 +286,9 @@ public class CachedContentIndexTest { CachedContent cachedContent = index.getOrAdd("key1"); long cacheFileLength = 20; File cacheFile = - SimpleCacheSpanTest.createCacheSpanFile( - cacheDir, - cachedContent.id, - /* offset= */ 10, - cacheFileLength, - /* lastTouchTimestamp= */ 30); + SimpleCacheSpan.getCacheFile( + cacheDir, cachedContent.id, /* position= */ 10, /* timestamp= */ 30); + createTestFile(cacheFile, cacheFileLength); SimpleCacheSpan span = SimpleCacheSpan.createCacheEntry(cacheFile, cacheFileLength, index); cachedContent.addSpan(span); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpanTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpanTest.java index 5908c7db20..0686ae3a59 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpanTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpanTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.upstream.cache; +import static com.google.android.exoplayer2.testutil.TestUtil.createTestFile; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -24,7 +25,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.util.Util; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.Set; import java.util.TreeSet; @@ -37,13 +37,6 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class SimpleCacheSpanTest { - public static File createCacheSpanFile( - File cacheDir, int id, long offset, long length, long lastTouchTimestamp) throws IOException { - File cacheFile = SimpleCacheSpan.getCacheFile(cacheDir, id, offset, lastTouchTimestamp); - createTestFile(cacheFile, length); - return cacheFile; - } - private CachedContentIndex index; private File cacheDir; @@ -82,10 +75,10 @@ public class SimpleCacheSpanTest { public void testUpgradeFileName() throws Exception { String key = "abc%def"; int id = index.assignIdForKey(key); - File v3file = createTestFile(id + ".0.1.v3.exo"); - File v2file = createTestFile("abc%25def.1.2.v2.exo"); // %25 is '%' after escaping - File wrongEscapedV2file = createTestFile("abc%2Gdef.3.4.v2.exo"); // 2G is invalid hex - File v1File = createTestFile("abc%def.5.6.v1.exo"); // V1 did not escape + File v3file = createTestFile(cacheDir, id + ".0.1.v3.exo"); + File v2file = createTestFile(cacheDir, "abc%25def.1.2.v2.exo"); // %25 is '%' after escaping + File wrongEscapedV2file = createTestFile(cacheDir, "abc%2Gdef.3.4.v2.exo"); // 2G is invalid hex + File v1File = createTestFile(cacheDir, "abc%def.5.6.v1.exo"); // V1 did not escape for (File file : cacheDir.listFiles()) { SimpleCacheSpan cacheEntry = SimpleCacheSpan.createCacheEntry(file, file.length(), index); @@ -125,26 +118,12 @@ public class SimpleCacheSpanTest { assertThat(cachedPositions.get(5)).isEqualTo(6); } - private static void createTestFile(File file, long length) throws IOException { - FileOutputStream output = new FileOutputStream(file); - for (int i = 0; i < length; i++) { - output.write(i); - } - output.close(); - } - - private File createTestFile(String name) throws IOException { - File file = new File(cacheDir, name); - createTestFile(file, 1); - return file; - } - private void assertCacheSpan(String key, long offset, long lastTouchTimestamp) throws IOException { int id = index.assignIdForKey(key); - long cacheFileLength = 1; - File cacheFile = createCacheSpanFile(cacheDir, id, offset, cacheFileLength, lastTouchTimestamp); - SimpleCacheSpan cacheSpan = SimpleCacheSpan.createCacheEntry(cacheFile, cacheFileLength, index); + File cacheFile = SimpleCacheSpan.getCacheFile(cacheDir, id, offset, lastTouchTimestamp); + createTestFile(cacheFile, /* length= */ 1); + SimpleCacheSpan cacheSpan = SimpleCacheSpan.createCacheEntry(cacheFile, /* length= */ 1, index); String message = cacheFile.toString(); assertWithMessage(message).that(cacheSpan).isNotNull(); assertWithMessage(message).that(cacheFile.getParentFile()).isEqualTo(cacheDir); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadBuilder.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DownloadBuilder.java similarity index 75% rename from library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadBuilder.java rename to testutils/src/main/java/com/google/android/exoplayer2/testutil/DownloadBuilder.java index e07166a21c..1b707b3d3a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadBuilder.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DownloadBuilder.java @@ -13,11 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.offline; +package com.google.android.exoplayer2.testutil; import android.net.Uri; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.offline.Download; +import com.google.android.exoplayer2.offline.DownloadProgress; +import com.google.android.exoplayer2.offline.DownloadRequest; +import com.google.android.exoplayer2.offline.StreamKey; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -29,7 +33,7 @@ import java.util.List; * creation for tests. Tests must avoid depending on the default values but explicitly set tested * parameters during test initialization. */ -/* package */ final class DownloadBuilder { +public final class DownloadBuilder { private final DownloadProgress progress; @@ -47,7 +51,12 @@ import java.util.List; private int stopReason; private int failureReason; - /* package */ DownloadBuilder(String id) { + /** + * Creates a download builder for "uri" with type "type" and no stream keys. + * + * @param id The unique content identifier for the download. + */ + public DownloadBuilder(String id) { this( id, "type", @@ -57,7 +66,12 @@ import java.util.List; new byte[0]); } - /* package */ DownloadBuilder(DownloadRequest request) { + /** + * Creates a download builder based on the attributes of the specified request. + * + * @param request A {@link DownloadRequest} defining the content to download. + */ + public DownloadBuilder(DownloadRequest request) { this( request.id, request.type, @@ -67,12 +81,13 @@ import java.util.List; request.data); } - /* package */ DownloadBuilder( + /** Creates a download builder. */ + private DownloadBuilder( String id, String type, Uri uri, List streamKeys, - String cacheKey, + @Nullable String cacheKey, byte[] customMetadata) { this.id = id; this.type = type; @@ -86,76 +101,85 @@ import java.util.List; this.progress = new DownloadProgress(); } - public DownloadBuilder setId(String id) { - this.id = id; - return this; - } - + /** @see DownloadRequest#type */ public DownloadBuilder setType(String type) { this.type = type; return this; } + /** @see DownloadRequest#uri */ public DownloadBuilder setUri(String uri) { this.uri = Uri.parse(uri); return this; } + /** @see DownloadRequest#uri */ public DownloadBuilder setUri(Uri uri) { this.uri = uri; return this; } + /** @see DownloadRequest#customCacheKey */ public DownloadBuilder setCacheKey(@Nullable String cacheKey) { this.cacheKey = cacheKey; return this; } + /** @see Download#state */ public DownloadBuilder setState(@Download.State int state) { this.state = state; return this; } + /** @see DownloadProgress#percentDownloaded */ public DownloadBuilder setPercentDownloaded(float percentDownloaded) { progress.percentDownloaded = percentDownloaded; return this; } + /** @see DownloadProgress#bytesDownloaded */ public DownloadBuilder setBytesDownloaded(long bytesDownloaded) { progress.bytesDownloaded = bytesDownloaded; return this; } + /** @see Download#contentLength */ public DownloadBuilder setContentLength(long contentLength) { this.contentLength = contentLength; return this; } + /** @see Download#failureReason */ public DownloadBuilder setFailureReason(int failureReason) { this.failureReason = failureReason; return this; } + /** @see Download#stopReason */ public DownloadBuilder setStopReason(int stopReason) { this.stopReason = stopReason; return this; } + /** @see Download#startTimeMs */ public DownloadBuilder setStartTimeMs(long startTimeMs) { this.startTimeMs = startTimeMs; return this; } + /** @see Download#updateTimeMs */ public DownloadBuilder setUpdateTimeMs(long updateTimeMs) { this.updateTimeMs = updateTimeMs; return this; } + /** @see DownloadRequest#streamKeys */ public DownloadBuilder setStreamKeys(StreamKey... streamKeys) { this.streamKeys = Arrays.asList(streamKeys); return this; } + /** @see DownloadRequest#data */ public DownloadBuilder setCustomMetadata(byte[] customMetadata) { this.customMetadata = customMetadata; return this; diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java index c47b438100..1fb3e4f0cc 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java @@ -24,6 +24,7 @@ import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; +import android.media.MediaCodec; import android.net.Uri; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.database.DatabaseProvider; @@ -37,6 +38,8 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; @@ -180,6 +183,26 @@ public class TestUtil { return joined; } + /** Writes one byte long dummy test data to the file and returns it. */ + public static File createTestFile(File directory, String name) throws IOException { + return createTestFile(directory, name, /* length= */ 1); + } + + /** Writes dummy test data with the specified length to the file and returns it. */ + public static File createTestFile(File directory, String name, long length) throws IOException { + return createTestFile(new File(directory, name), length); + } + + /** Writes dummy test data with the specified length to the file and returns it. */ + public static File createTestFile(File file, long length) throws IOException { + FileOutputStream output = new FileOutputStream(file); + for (long i = 0; i < length; i++) { + output.write((int) i); + } + output.close(); + return file; + } + /** Returns the bytes of an asset file. */ public static byte[] getByteArray(Context context, String fileName) throws IOException { return Util.toByteArray(getInputStream(context, fileName)); @@ -240,6 +263,15 @@ public class TestUtil { } } + /** Returns whether two {@link android.media.MediaCodec.BufferInfo BufferInfos} are equal. */ + public static void assertBufferInfosEqual( + MediaCodec.BufferInfo expected, MediaCodec.BufferInfo actual) { + assertThat(expected.flags).isEqualTo(actual.flags); + assertThat(expected.offset).isEqualTo(actual.offset); + assertThat(expected.presentationTimeUs).isEqualTo(actual.presentationTimeUs); + assertThat(expected.size).isEqualTo(actual.size); + } + /** * Asserts whether actual bitmap is very similar to the expected bitmap at some quality level. *