From daafbb1f1ce0cae9a23c324a073ca38e02ed8a52 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 31 Aug 2017 10:02:43 -0700 Subject: [PATCH] Add missing Robolectric test path to codebase ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=167151714 --- .../com/google/android/exoplayer2/CTest.java | 43 ++ .../google/android/exoplayer2/FormatTest.java | 159 ++++++ .../exoplayer2/util/AtomicFileTest.java | 97 ++++ .../exoplayer2/util/ColorParserTest.java | 103 ++++ .../exoplayer2/util/NalUnitUtilTest.java | 217 +++++++ .../exoplayer2/util/ParsableBitArrayTest.java | 198 +++++++ .../util/ParsableByteArrayTest.java | 530 ++++++++++++++++++ .../util/ParsableNalUnitBitArrayTest.java | 128 +++++ .../ReusableBufferedOutputStreamTest.java | 53 ++ .../android/exoplayer2/util/UriUtilTest.java | 109 ++++ .../android/exoplayer2/util/UtilTest.java | 200 +++++++ 11 files changed, 1837 insertions(+) create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/CTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/FormatTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/NalUnitUtilTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/ParsableByteArrayTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/ReusableBufferedOutputStreamTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/UriUtilTest.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java diff --git a/library/core/src/test/java/com/google/android/exoplayer2/CTest.java b/library/core/src/test/java/com/google/android/exoplayer2/CTest.java new file mode 100644 index 0000000000..ff4756f5ed --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/CTest.java @@ -0,0 +1,43 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; +import android.media.MediaCodec; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Unit test for {@link C}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public class CTest { + + @SuppressLint("InlinedApi") + @Test + public void testConstants() { + // Sanity check that constant values match those defined by the platform. + assertThat(C.BUFFER_FLAG_KEY_FRAME).isEqualTo(MediaCodec.BUFFER_FLAG_KEY_FRAME); + assertThat(C.BUFFER_FLAG_END_OF_STREAM).isEqualTo(MediaCodec.BUFFER_FLAG_END_OF_STREAM); + assertThat(C.CRYPTO_MODE_AES_CTR).isEqualTo(MediaCodec.CRYPTO_MODE_AES_CTR); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/FormatTest.java b/library/core/src/test/java/com/google/android/exoplayer2/FormatTest.java new file mode 100644 index 0000000000..8e36edc105 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/FormatTest.java @@ -0,0 +1,159 @@ +/* + * 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; + +import static com.google.android.exoplayer2.C.WIDEVINE_UUID; +import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_MP4; +import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_WEBM; +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.media.MediaFormat; +import android.os.Parcel; +import com.google.android.exoplayer2.drm.DrmInitData; +import com.google.android.exoplayer2.metadata.Metadata; +import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; +import com.google.android.exoplayer2.testutil.TestUtil; +import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.video.ColorInfo; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Unit test for {@link Format}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class FormatTest { + + private static final List INIT_DATA; + static { + byte[] initData1 = new byte[] {1, 2, 3}; + byte[] initData2 = new byte[] {4, 5, 6}; + List initData = new ArrayList<>(); + initData.add(initData1); + initData.add(initData2); + INIT_DATA = Collections.unmodifiableList(initData); + } + + @Test + public void testParcelable() { + DrmInitData.SchemeData DRM_DATA_1 = new DrmInitData.SchemeData(WIDEVINE_UUID, "cenc", VIDEO_MP4, + TestUtil.buildTestData(128, 1 /* data seed */)); + DrmInitData.SchemeData DRM_DATA_2 = new DrmInitData.SchemeData(C.UUID_NIL, null, VIDEO_WEBM, + TestUtil.buildTestData(128, 1 /* data seed */)); + DrmInitData drmInitData = new DrmInitData(DRM_DATA_1, DRM_DATA_2); + byte[] projectionData = new byte[] {1, 2, 3}; + Metadata metadata = new Metadata( + new TextInformationFrame("id1", "description1", "value1"), + new TextInformationFrame("id2", "description2", "value2")); + ColorInfo colorInfo = new ColorInfo(C.COLOR_SPACE_BT709, + C.COLOR_RANGE_LIMITED, C.COLOR_TRANSFER_SDR, new byte[] {1, 2, 3, 4, 5, 6, 7}); + + Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, + 1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, colorInfo, 6, + 44100, C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.NO_VALUE, + Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, drmInitData, metadata); + + Parcel parcel = Parcel.obtain(); + formatToParcel.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Format formatFromParcel = Format.CREATOR.createFromParcel(parcel); + assertThat(formatFromParcel).isEqualTo(formatToParcel); + + parcel.recycle(); + } + + @Test + public void testConversionToFrameworkMediaFormat() { + if (Util.SDK_INT < 16) { + // Test doesn't apply. + return; + } + + testConversionToFrameworkMediaFormatV16(Format.createVideoSampleFormat(null, "video/xyz", null, + 5000, 102400, 1280, 720, 30, INIT_DATA, null)); + testConversionToFrameworkMediaFormatV16(Format.createVideoSampleFormat(null, "video/xyz", null, + 5000, Format.NO_VALUE, 1280, 720, 30, null, null)); + testConversionToFrameworkMediaFormatV16(Format.createAudioSampleFormat(null, "audio/xyz", null, + 500, 128, 5, 44100, INIT_DATA, null, 0, null)); + testConversionToFrameworkMediaFormatV16(Format.createAudioSampleFormat(null, "audio/xyz", null, + 500, Format.NO_VALUE, 5, 44100, null, null, 0, null)); + testConversionToFrameworkMediaFormatV16(Format.createTextSampleFormat(null, "text/xyz", 0, + "eng")); + testConversionToFrameworkMediaFormatV16(Format.createTextSampleFormat(null, "text/xyz", 0, + null)); + } + + @SuppressLint("InlinedApi") + @TargetApi(16) + private static void testConversionToFrameworkMediaFormatV16(Format in) { + MediaFormat out = in.getFrameworkMediaFormatV16(); + assertThat(out.getString(MediaFormat.KEY_MIME)).isEqualTo(in.sampleMimeType); + assertOptionalV16(out, MediaFormat.KEY_LANGUAGE, in.language); + assertOptionalV16(out, MediaFormat.KEY_MAX_INPUT_SIZE, in.maxInputSize); + assertOptionalV16(out, MediaFormat.KEY_WIDTH, in.width); + assertOptionalV16(out, MediaFormat.KEY_HEIGHT, in.height); + assertOptionalV16(out, MediaFormat.KEY_CHANNEL_COUNT, in.channelCount); + assertOptionalV16(out, MediaFormat.KEY_SAMPLE_RATE, in.sampleRate); + assertOptionalV16(out, MediaFormat.KEY_FRAME_RATE, in.frameRate); + + for (int i = 0; i < in.initializationData.size(); i++) { + byte[] originalData = in.initializationData.get(i); + ByteBuffer frameworkBuffer = out.getByteBuffer("csd-" + i); + byte[] frameworkData = Arrays.copyOf(frameworkBuffer.array(), frameworkBuffer.limit()); + assertThat(frameworkData).isEqualTo(originalData); + } + } + + @TargetApi(16) + private static void assertOptionalV16(MediaFormat format, String key, String value) { + if (value == null) { + assertThat(format.containsKey(key)).isEqualTo(false); + } else { + assertThat(format.getString(key)).isEqualTo(value); + } + } + + @TargetApi(16) + private static void assertOptionalV16(MediaFormat format, String key, int value) { + if (value == Format.NO_VALUE) { + assertThat(format.containsKey(key)).isEqualTo(false); + } else { + assertThat(format.getInteger(key)).isEqualTo(value); + } + } + + @TargetApi(16) + private static void assertOptionalV16(MediaFormat format, String key, float value) { + if (value == Format.NO_VALUE) { + assertThat(format.containsKey(key)).isEqualTo(false); + } else { + assertThat(format.getFloat(key)).isEqualTo(value); + } + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java new file mode 100644 index 0000000000..dcf3d31eb3 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/AtomicFileTest.java @@ -0,0 +1,97 @@ +/* + * 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.util; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +/** + * Tests {@link AtomicFile}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class AtomicFileTest { + + private File tempFolder; + private File file; + private AtomicFile atomicFile; + + @Before + public void setUp() throws Exception { + tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest"); + file = new File(tempFolder, "atomicFile"); + atomicFile = new AtomicFile(file); + } + + @After + public void tearDown() throws Exception { + Util.recursiveDelete(tempFolder); + } + + @Test + public void testDelete() throws Exception { + assertThat(file.createNewFile()).isTrue(); + atomicFile.delete(); + assertThat(file.exists()).isFalse(); + } + + @Test + public void testWriteRead() throws Exception { + OutputStream output = atomicFile.startWrite(); + output.write(5); + atomicFile.endWrite(output); + output.close(); + + assertRead(); + + output = atomicFile.startWrite(); + output.write(5); + output.write(6); + output.close(); + + assertRead(); + + output = atomicFile.startWrite(); + output.write(6); + + assertRead(); + output.close(); + + output = atomicFile.startWrite(); + + assertRead(); + output.close(); + } + + private void assertRead() throws IOException { + InputStream input = atomicFile.openRead(); + assertThat(input.read()).isEqualTo(5); + assertThat(input.read()).isEqualTo(-1); + input.close(); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java new file mode 100644 index 0000000000..13b126090c --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java @@ -0,0 +1,103 @@ +/* + * 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.util; + +import static android.graphics.Color.BLACK; +import static android.graphics.Color.RED; +import static android.graphics.Color.WHITE; +import static android.graphics.Color.argb; +import static android.graphics.Color.parseColor; +import static com.google.android.exoplayer2.util.ColorParser.parseTtmlColor; +import static com.google.common.truth.Truth.assertThat; + +import android.graphics.Color; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Unit test for ColorParser. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class ColorParserTest { + + // Negative tests. + + @Test(expected = IllegalArgumentException.class) + public void testParseUnknownColor() { + ColorParser.parseTtmlColor("colorOfAnElectron"); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseNull() { + ColorParser.parseTtmlColor(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseEmpty() { + ColorParser.parseTtmlColor(""); + } + + @Test(expected = IllegalArgumentException.class) + public void testRgbColorParsingRgbValuesNegative() { + ColorParser.parseTtmlColor("rgb(-4, 55, 209)"); + } + + // Positive tests. + + @Test + public void testHexCodeParsing() { + assertThat(parseTtmlColor("#FFFFFF")).isEqualTo(WHITE); + assertThat(parseTtmlColor("#FFFFFFFF")).isEqualTo(WHITE); + assertThat(parseTtmlColor("#123456")).isEqualTo(parseColor("#FF123456")); + // Hex colors in ColorParser are RGBA, where-as {@link Color#parseColor} takes ARGB. + assertThat(parseTtmlColor("#FFFFFF00")).isEqualTo(parseColor("#00FFFFFF")); + assertThat(parseTtmlColor("#12345678")).isEqualTo(parseColor("#78123456")); + } + + @Test + public void testRgbColorParsing() { + assertThat(parseTtmlColor("rgb(255,255,255)")).isEqualTo(WHITE); + // Spaces are ignored. + assertThat(parseTtmlColor(" rgb ( 255, 255, 255)")).isEqualTo(WHITE); + } + + @Test + public void testRgbColorParsingRgbValuesOutOfBounds() { + int outOfBounds = ColorParser.parseTtmlColor("rgb(999, 999, 999)"); + int color = Color.rgb(999, 999, 999); + // Behave like the framework does. + assertThat(outOfBounds).isEqualTo(color); + } + + @Test + public void testRgbaColorParsing() { + assertThat(parseTtmlColor("rgba(255,255,255,255)")).isEqualTo(WHITE); + assertThat(parseTtmlColor("rgba(255,255,255,255)")) + .isEqualTo(argb(255, 255, 255, 255)); + assertThat(parseTtmlColor("rgba(0, 0, 0, 255)")).isEqualTo(BLACK); + assertThat(parseTtmlColor("rgba(0, 0, 255, 0)")) + .isEqualTo(argb(0, 0, 0, 255)); + assertThat(parseTtmlColor("rgba(255, 0, 0, 255)")).isEqualTo(RED); + assertThat(parseTtmlColor("rgba(255, 0, 255, 0)")) + .isEqualTo(argb(0, 255, 0, 255)); + assertThat(parseTtmlColor("rgba(255, 0, 0, 205)")) + .isEqualTo(argb(205, 255, 0, 0)); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/NalUnitUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/NalUnitUtilTest.java new file mode 100644 index 0000000000..ee77664cce --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/NalUnitUtilTest.java @@ -0,0 +1,217 @@ +/* + * 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.util; + +import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.common.truth.Truth.assertThat; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link NalUnitUtil}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class NalUnitUtilTest { + + private static final int TEST_PARTIAL_NAL_POSITION = 4; + private static final int TEST_NAL_POSITION = 10; + private static final byte[] SPS_TEST_DATA = createByteArray(0x00, 0x00, 0x01, 0x67, 0x4D, 0x40, + 0x16, 0xEC, 0xA0, 0x50, 0x17, 0xFC, 0xB8, 0x08, 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, + 0x00, 0x0F, 0x47, 0x8B, 0x16, 0xCB); + private static final int SPS_TEST_DATA_OFFSET = 3; + + @Test + public void testFindNalUnit() { + byte[] data = buildTestData(); + + // Should find NAL unit. + int result = NalUnitUtil.findNalUnit(data, 0, data.length, null); + assertThat(result).isEqualTo(TEST_NAL_POSITION); + // Should find NAL unit whose prefix ends one byte before the limit. + result = NalUnitUtil.findNalUnit(data, 0, TEST_NAL_POSITION + 4, null); + assertThat(result).isEqualTo(TEST_NAL_POSITION); + // Shouldn't find NAL unit whose prefix ends at the limit (since the limit is exclusive). + result = NalUnitUtil.findNalUnit(data, 0, TEST_NAL_POSITION + 3, null); + assertThat(result).isEqualTo(TEST_NAL_POSITION + 3); + // Should find NAL unit whose prefix starts at the offset. + result = NalUnitUtil.findNalUnit(data, TEST_NAL_POSITION, data.length, null); + assertThat(result).isEqualTo(TEST_NAL_POSITION); + // Shouldn't find NAL unit whose prefix starts one byte past the offset. + result = NalUnitUtil.findNalUnit(data, TEST_NAL_POSITION + 1, data.length, null); + assertThat(result).isEqualTo(data.length); + } + + @Test + public void testFindNalUnitWithPrefix() { + byte[] data = buildTestData(); + + // First byte of NAL unit in data1, rest in data2. + boolean[] prefixFlags = new boolean[3]; + byte[] data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 1); + byte[] data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 1, data.length); + int result = NalUnitUtil.findNalUnit(data1, 0, data1.length, prefixFlags); + assertThat(result).isEqualTo(data1.length); + result = NalUnitUtil.findNalUnit(data2, 0, data2.length, prefixFlags); + assertThat(result).isEqualTo(-1); + assertPrefixFlagsCleared(prefixFlags); + + // First three bytes of NAL unit in data1, rest in data2. + prefixFlags = new boolean[3]; + data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 3); + data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 3, data.length); + result = NalUnitUtil.findNalUnit(data1, 0, data1.length, prefixFlags); + assertThat(result).isEqualTo(data1.length); + result = NalUnitUtil.findNalUnit(data2, 0, data2.length, prefixFlags); + assertThat(result).isEqualTo(-3); + assertPrefixFlagsCleared(prefixFlags); + + // First byte of NAL unit in data1, second byte in data2, rest in data3. + prefixFlags = new boolean[3]; + data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 1); + data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 1, TEST_NAL_POSITION + 2); + byte[] data3 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 2, data.length); + result = NalUnitUtil.findNalUnit(data1, 0, data1.length, prefixFlags); + assertThat(result).isEqualTo(data1.length); + result = NalUnitUtil.findNalUnit(data2, 0, data2.length, prefixFlags); + assertThat(result).isEqualTo(data2.length); + result = NalUnitUtil.findNalUnit(data3, 0, data3.length, prefixFlags); + assertThat(result).isEqualTo(-2); + assertPrefixFlagsCleared(prefixFlags); + + // NAL unit split with one byte in four arrays. + prefixFlags = new boolean[3]; + data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 1); + data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 1, TEST_NAL_POSITION + 2); + data3 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 2, TEST_NAL_POSITION + 3); + byte[] data4 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 2, data.length); + result = NalUnitUtil.findNalUnit(data1, 0, data1.length, prefixFlags); + assertThat(result).isEqualTo(data1.length); + result = NalUnitUtil.findNalUnit(data2, 0, data2.length, prefixFlags); + assertThat(result).isEqualTo(data2.length); + result = NalUnitUtil.findNalUnit(data3, 0, data3.length, prefixFlags); + assertThat(result).isEqualTo(data3.length); + result = NalUnitUtil.findNalUnit(data4, 0, data4.length, prefixFlags); + assertThat(result).isEqualTo(-3); + assertPrefixFlagsCleared(prefixFlags); + + // NAL unit entirely in data2. data1 ends with partial prefix. + prefixFlags = new boolean[3]; + data1 = Arrays.copyOfRange(data, 0, TEST_PARTIAL_NAL_POSITION + 2); + data2 = Arrays.copyOfRange(data, TEST_PARTIAL_NAL_POSITION + 2, data.length); + result = NalUnitUtil.findNalUnit(data1, 0, data1.length, prefixFlags); + assertThat(result).isEqualTo(data1.length); + result = NalUnitUtil.findNalUnit(data2, 0, data2.length, prefixFlags); + assertThat(result).isEqualTo(4); + assertPrefixFlagsCleared(prefixFlags); + } + + @Test + public void testParseSpsNalUnit() { + NalUnitUtil.SpsData data = NalUnitUtil.parseSpsNalUnit(SPS_TEST_DATA, SPS_TEST_DATA_OFFSET, + SPS_TEST_DATA.length); + assertThat(data.width).isEqualTo(640); + assertThat(data.height).isEqualTo(360); + assertThat(data.deltaPicOrderAlwaysZeroFlag).isFalse(); + assertThat(data.frameMbsOnlyFlag).isTrue(); + assertThat(data.frameNumLength).isEqualTo(4); + assertThat(data.picOrderCntLsbLength).isEqualTo(6); + assertThat(data.seqParameterSetId).isEqualTo(0); + assertThat(data.pixelWidthAspectRatio).isEqualTo(1.0f); + assertThat(data.picOrderCountType).isEqualTo(0); + assertThat(data.separateColorPlaneFlag).isFalse(); + } + + @Test + public void testUnescapeDoesNotModifyBuffersWithoutStartCodes() { + assertUnescapeDoesNotModify(""); + assertUnescapeDoesNotModify("0000"); + assertUnescapeDoesNotModify("172BF38A3C"); + assertUnescapeDoesNotModify("000004"); + } + + @Test + public void testUnescapeModifiesBuffersWithStartCodes() { + assertUnescapeMatchesExpected("00000301", "000001"); + assertUnescapeMatchesExpected("0000030200000300", "000002000000"); + } + + @Test + public void testDiscardToSps() { + assertDiscardToSpsMatchesExpected("", ""); + assertDiscardToSpsMatchesExpected("00", ""); + assertDiscardToSpsMatchesExpected("FFFF000001", ""); + assertDiscardToSpsMatchesExpected("00000001", ""); + assertDiscardToSpsMatchesExpected("00000001FF67", ""); + assertDiscardToSpsMatchesExpected("00000001000167", ""); + assertDiscardToSpsMatchesExpected("0000000167", "0000000167"); + assertDiscardToSpsMatchesExpected("0000000167FF", "0000000167FF"); + assertDiscardToSpsMatchesExpected("0000000167FF", "0000000167FF"); + assertDiscardToSpsMatchesExpected("0000000167FF000000016700", "0000000167FF000000016700"); + assertDiscardToSpsMatchesExpected("000000000167FF", "0000000167FF"); + assertDiscardToSpsMatchesExpected("0001670000000167FF", "0000000167FF"); + assertDiscardToSpsMatchesExpected("FF00000001660000000167FF", "0000000167FF"); + } + + private static byte[] buildTestData() { + byte[] data = new byte[20]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) 0xFF; + } + // Insert an incomplete NAL unit start code. + data[TEST_PARTIAL_NAL_POSITION] = 0; + data[TEST_PARTIAL_NAL_POSITION + 1] = 0; + // Insert a complete NAL unit start code. + data[TEST_NAL_POSITION] = 0; + data[TEST_NAL_POSITION + 1] = 0; + data[TEST_NAL_POSITION + 2] = 1; + data[TEST_NAL_POSITION + 3] = 5; + return data; + } + + private static void assertPrefixFlagsCleared(boolean[] flags) { + assertThat(flags[0] || flags[1] || flags[2]).isEqualTo(false); + } + + private static void assertUnescapeDoesNotModify(String input) { + assertUnescapeMatchesExpected(input, input); + } + + private static void assertUnescapeMatchesExpected(String input, String expectedOutput) { + byte[] bitstream = Util.getBytesFromHexString(input); + byte[] expectedOutputBitstream = Util.getBytesFromHexString(expectedOutput); + int count = NalUnitUtil.unescapeStream(bitstream, bitstream.length); + assertThat(count).isEqualTo(expectedOutputBitstream.length); + byte[] outputBitstream = new byte[count]; + System.arraycopy(bitstream, 0, outputBitstream, 0, count); + assertThat(outputBitstream).isEqualTo(expectedOutputBitstream); + } + + private static void assertDiscardToSpsMatchesExpected(String input, String expectedOutput) { + byte[] bitstream = Util.getBytesFromHexString(input); + byte[] expectedOutputBitstream = Util.getBytesFromHexString(expectedOutput); + ByteBuffer buffer = ByteBuffer.wrap(bitstream); + buffer.position(buffer.limit()); + NalUnitUtil.discardToSps(buffer); + assertThat(Arrays.copyOf(buffer.array(), buffer.position())).isEqualTo(expectedOutputBitstream); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java new file mode 100644 index 0000000000..0d864f407f --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java @@ -0,0 +1,198 @@ +/* + * 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.util; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link ParsableBitArray}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class ParsableBitArrayTest { + + private static final byte[] TEST_DATA = new byte[] {0x3C, (byte) 0xD2, (byte) 0x5F, (byte) 0x01, + (byte) 0xFF, (byte) 0x14, (byte) 0x60, (byte) 0x99}; + + private ParsableBitArray testArray; + + @Before + public void setUp() { + testArray = new ParsableBitArray(TEST_DATA); + } + + @Test + public void testReadAllBytes() { + byte[] bytesRead = new byte[TEST_DATA.length]; + testArray.readBytes(bytesRead, 0, TEST_DATA.length); + assertThat(bytesRead).isEqualTo(TEST_DATA); + assertThat(testArray.getPosition()).isEqualTo(TEST_DATA.length * 8); + assertThat(testArray.getBytePosition()).isEqualTo(TEST_DATA.length); + } + + @Test + public void testReadBit() { + assertReadBitsToEnd(0); + } + + @Test + public void testReadBits() { + assertThat(testArray.readBits(5)).isEqualTo(getTestDataBits(0, 5)); + assertThat(testArray.readBits(0)).isEqualTo(getTestDataBits(5, 0)); + assertThat(testArray.readBits(3)).isEqualTo(getTestDataBits(5, 3)); + assertThat(testArray.readBits(16)).isEqualTo(getTestDataBits(8, 16)); + assertThat(testArray.readBits(3)).isEqualTo(getTestDataBits(24, 3)); + assertThat(testArray.readBits(18)).isEqualTo(getTestDataBits(27, 18)); + assertThat(testArray.readBits(5)).isEqualTo(getTestDataBits(45, 5)); + assertThat(testArray.readBits(14)).isEqualTo(getTestDataBits(50, 14)); + } + + @Test + public void testReadBitsToByteArray() { + byte[] result = new byte[TEST_DATA.length]; + // Test read within byte boundaries. + testArray.readBits(result, 0, 6); + assertThat(result[0]).isEqualTo((byte) (TEST_DATA[0] & 0xFC)); + // Test read across byte boundaries. + testArray.readBits(result, 0, 8); + assertThat(result[0]).isEqualTo( + (byte) (((TEST_DATA[0] & 0x03) << 6) | ((TEST_DATA[1] & 0xFC) >> 2))); + // Test reading across multiple bytes. + testArray.readBits(result, 1, 50); + for (int i = 1; i < 7; i++) { + assertThat(result[i]) + .isEqualTo((byte) (((TEST_DATA[i] & 0x03) << 6) | ((TEST_DATA[i + 1] & 0xFC) >> 2))); + } + assertThat(result[7]).isEqualTo((byte) ((TEST_DATA[7] & 0x03) << 6)); + assertThat(testArray.bitsLeft()).isEqualTo(0); + // Test read last buffer byte across input data bytes. + testArray.setPosition(31); + result[3] = 0; + testArray.readBits(result, 3, 3); + assertThat(result[3]).isEqualTo((byte) 0xE0); + // Test read bits in the middle of a input data byte. + result[0] = 0; + assertThat(testArray.getPosition()).isEqualTo(34); + testArray.readBits(result, 0, 3); + assertThat(result[0]).isEqualTo((byte) 0xE0); + // Test read 0 bits. + testArray.setPosition(32); + result[1] = 0; + testArray.readBits(result, 1, 0); + assertThat(result[1]).isEqualTo((byte) 0); + // Test reading a number of bits divisible by 8. + testArray.setPosition(0); + testArray.readBits(result, 0, 16); + assertThat(result[0]).isEqualTo(TEST_DATA[0]); + assertThat(result[1]).isEqualTo(TEST_DATA[1]); + // Test least significant bits are unmodified. + result[1] = (byte) 0xFF; + testArray.readBits(result, 0, 9); + assertThat(result[0]).isEqualTo((byte) 0x5F); + assertThat(result[1]).isEqualTo((byte) 0x7F); + } + + @Test + public void testRead32BitsByteAligned() { + assertThat(testArray.readBits(32)).isEqualTo(getTestDataBits(0, 32)); + assertThat(testArray.readBits(32)).isEqualTo(getTestDataBits(32, 32)); + } + + @Test + public void testRead32BitsNonByteAligned() { + assertThat(testArray.readBits(5)).isEqualTo(getTestDataBits(0, 5)); + assertThat(testArray.readBits(32)).isEqualTo(getTestDataBits(5, 32)); + } + + @Test + public void testSkipBytes() { + testArray.skipBytes(2); + assertReadBitsToEnd(16); + } + + @Test + public void testSkipBitsByteAligned() { + testArray.skipBits(16); + assertReadBitsToEnd(16); + } + + @Test + public void testSkipBitsNonByteAligned() { + testArray.skipBits(5); + assertReadBitsToEnd(5); + } + + @Test + public void testSetPositionByteAligned() { + testArray.setPosition(16); + assertReadBitsToEnd(16); + } + + @Test + public void testSetPositionNonByteAligned() { + testArray.setPosition(5); + assertReadBitsToEnd(5); + } + + @Test + public void testByteAlignFromNonByteAligned() { + testArray.setPosition(11); + testArray.byteAlign(); + assertThat(testArray.getBytePosition()).isEqualTo(2); + assertThat(testArray.getPosition()).isEqualTo(16); + assertReadBitsToEnd(16); + } + + @Test + public void testByteAlignFromByteAligned() { + testArray.setPosition(16); + testArray.byteAlign(); // Should be a no-op. + assertThat(testArray.getBytePosition()).isEqualTo(2); + assertThat(testArray.getPosition()).isEqualTo(16); + assertReadBitsToEnd(16); + } + + private void assertReadBitsToEnd(int expectedStartPosition) { + int position = testArray.getPosition(); + assertThat(position).isEqualTo(expectedStartPosition); + for (int i = position; i < TEST_DATA.length * 8; i++) { + assertThat(testArray.readBit()).isEqualTo(getTestDataBit(i)); + assertThat(testArray.getPosition()).isEqualTo(i + 1); + } + } + + private static int getTestDataBits(int bitPosition, int length) { + int result = 0; + for (int i = 0; i < length; i++) { + result = result << 1; + if (getTestDataBit(bitPosition++)) { + result |= 0x1; + } + } + return result; + } + + private static boolean getTestDataBit(int bitPosition) { + return (TEST_DATA[bitPosition / 8] & (0x80 >>> (bitPosition % 8))) != 0; + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableByteArrayTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableByteArrayTest.java new file mode 100644 index 0000000000..504a58b4a8 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableByteArrayTest.java @@ -0,0 +1,530 @@ +/* + * 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.util; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.Charset.forName; +import static junit.framework.TestCase.fail; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link ParsableByteArray}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class ParsableByteArrayTest { + + private static final byte[] TEST_DATA = + new byte[] {0x0F, (byte) 0xFF, (byte) 0x42, (byte) 0x0F, 0x00, 0x00, 0x00, 0x00}; + + private static ParsableByteArray getTestDataArray() { + ParsableByteArray testArray = new ParsableByteArray(TEST_DATA.length); + System.arraycopy(TEST_DATA, 0, testArray.data, 0, TEST_DATA.length); + return testArray; + } + + @Test + public void testReadShort() { + testReadShort((short) -1); + testReadShort((short) 0); + testReadShort((short) 1); + testReadShort(Short.MIN_VALUE); + testReadShort(Short.MAX_VALUE); + } + + private static void testReadShort(short testValue) { + ParsableByteArray testArray = new ParsableByteArray( + ByteBuffer.allocate(4).putShort(testValue).array()); + int readValue = testArray.readShort(); + + // Assert that the value we read was the value we wrote. + assertThat(readValue).isEqualTo(testValue); + // And that the position advanced as expected. + assertThat(testArray.getPosition()).isEqualTo(2); + + // And that skipping back and reading gives the same results. + testArray.skipBytes(-2); + readValue = testArray.readShort(); + assertThat(readValue).isEqualTo(testValue); + assertThat(testArray.getPosition()).isEqualTo(2); + } + + @Test + public void testReadInt() { + testReadInt(0); + testReadInt(1); + testReadInt(-1); + testReadInt(Integer.MIN_VALUE); + testReadInt(Integer.MAX_VALUE); + } + + private static void testReadInt(int testValue) { + ParsableByteArray testArray = new ParsableByteArray( + ByteBuffer.allocate(4).putInt(testValue).array()); + int readValue = testArray.readInt(); + + // Assert that the value we read was the value we wrote. + assertThat(readValue).isEqualTo(testValue); + // And that the position advanced as expected. + assertThat(testArray.getPosition()).isEqualTo(4); + + // And that skipping back and reading gives the same results. + testArray.skipBytes(-4); + readValue = testArray.readInt(); + assertThat(readValue).isEqualTo(testValue); + assertThat(testArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadUnsignedInt() { + testReadUnsignedInt(0); + testReadUnsignedInt(1); + testReadUnsignedInt(Integer.MAX_VALUE); + testReadUnsignedInt(Integer.MAX_VALUE + 1L); + testReadUnsignedInt(0xFFFFFFFFL); + } + + private static void testReadUnsignedInt(long testValue) { + ParsableByteArray testArray = new ParsableByteArray( + Arrays.copyOfRange(ByteBuffer.allocate(8).putLong(testValue).array(), 4, 8)); + long readValue = testArray.readUnsignedInt(); + + // Assert that the value we read was the value we wrote. + assertThat(readValue).isEqualTo(testValue); + // And that the position advanced as expected. + assertThat(testArray.getPosition()).isEqualTo(4); + + // And that skipping back and reading gives the same results. + testArray.skipBytes(-4); + readValue = testArray.readUnsignedInt(); + assertThat(readValue).isEqualTo(testValue); + assertThat(testArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadUnsignedIntToInt() { + testReadUnsignedIntToInt(0); + testReadUnsignedIntToInt(1); + testReadUnsignedIntToInt(Integer.MAX_VALUE); + try { + testReadUnsignedIntToInt(-1); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + try { + testReadUnsignedIntToInt(Integer.MIN_VALUE); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + } + + private static void testReadUnsignedIntToInt(int testValue) { + ParsableByteArray testArray = new ParsableByteArray( + ByteBuffer.allocate(4).putInt(testValue).array()); + int readValue = testArray.readUnsignedIntToInt(); + + // Assert that the value we read was the value we wrote. + assertThat(readValue).isEqualTo(testValue); + // And that the position advanced as expected. + assertThat(testArray.getPosition()).isEqualTo(4); + + // And that skipping back and reading gives the same results. + testArray.skipBytes(-4); + readValue = testArray.readUnsignedIntToInt(); + assertThat(readValue).isEqualTo(testValue); + assertThat(testArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadUnsignedLongToLong() { + testReadUnsignedLongToLong(0); + testReadUnsignedLongToLong(1); + testReadUnsignedLongToLong(Long.MAX_VALUE); + try { + testReadUnsignedLongToLong(-1); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + try { + testReadUnsignedLongToLong(Long.MIN_VALUE); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + } + + private static void testReadUnsignedLongToLong(long testValue) { + ParsableByteArray testArray = new ParsableByteArray( + ByteBuffer.allocate(8).putLong(testValue).array()); + long readValue = testArray.readUnsignedLongToLong(); + + // Assert that the value we read was the value we wrote. + assertThat(readValue).isEqualTo(testValue); + // And that the position advanced as expected. + assertThat(testArray.getPosition()).isEqualTo(8); + + // And that skipping back and reading gives the same results. + testArray.skipBytes(-8); + readValue = testArray.readUnsignedLongToLong(); + assertThat(readValue).isEqualTo(testValue); + assertThat(testArray.getPosition()).isEqualTo(8); + } + + @Test + public void testReadLong() { + testReadLong(0); + testReadLong(1); + testReadLong(-1); + testReadLong(Long.MIN_VALUE); + testReadLong(Long.MAX_VALUE); + } + + private static void testReadLong(long testValue) { + ParsableByteArray testArray = new ParsableByteArray( + ByteBuffer.allocate(8).putLong(testValue).array()); + long readValue = testArray.readLong(); + + // Assert that the value we read was the value we wrote. + assertThat(readValue).isEqualTo(testValue); + // And that the position advanced as expected. + assertThat(testArray.getPosition()).isEqualTo(8); + + // And that skipping back and reading gives the same results. + testArray.skipBytes(-8); + readValue = testArray.readLong(); + assertThat(readValue).isEqualTo(testValue); + assertThat(testArray.getPosition()).isEqualTo(8); + } + + @Test + public void testReadingMovesPosition() { + ParsableByteArray parsableByteArray = getTestDataArray(); + + // Given an array at the start + assertThat(parsableByteArray.getPosition()).isEqualTo(0); + // When reading an integer, the position advances + parsableByteArray.readUnsignedInt(); + assertThat(parsableByteArray.getPosition()).isEqualTo(4); + } + + @Test + public void testOutOfBoundsThrows() { + ParsableByteArray parsableByteArray = getTestDataArray(); + + // Given an array at the end + parsableByteArray.readUnsignedLongToLong(); + assertThat(parsableByteArray.getPosition()).isEqualTo(TEST_DATA.length); + // Then reading more data throws. + try { + parsableByteArray.readUnsignedInt(); + fail(); + } catch (Exception e) { + // Expected. + } + } + + @Test + public void testModificationsAffectParsableArray() { + ParsableByteArray parsableByteArray = getTestDataArray(); + + // When modifying the wrapped byte array + byte[] data = parsableByteArray.data; + long readValue = parsableByteArray.readUnsignedInt(); + data[0] = (byte) (TEST_DATA[0] + 1); + parsableByteArray.setPosition(0); + // Then the parsed value changes. + assertThat(parsableByteArray.readUnsignedInt()).isNotEqualTo(readValue); + } + + @Test + public void testReadingUnsignedLongWithMsbSetThrows() { + ParsableByteArray parsableByteArray = getTestDataArray(); + + // Given an array with the most-significant bit set on the top byte + byte[] data = parsableByteArray.data; + data[0] = (byte) 0x80; + // Then reading an unsigned long throws. + try { + parsableByteArray.readUnsignedLongToLong(); + fail(); + } catch (Exception e) { + // Expected. + } + } + + @Test + public void testReadUnsignedFixedPoint1616() { + ParsableByteArray parsableByteArray = getTestDataArray(); + + // When reading the integer part of a 16.16 fixed point value + int value = parsableByteArray.readUnsignedFixedPoint1616(); + // Then the read value is equal to the array elements interpreted as a short. + assertThat(value).isEqualTo((0xFF & TEST_DATA[0]) << 8 | (TEST_DATA[1] & 0xFF)); + assertThat(parsableByteArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadingBytesReturnsCopy() { + ParsableByteArray parsableByteArray = getTestDataArray(); + + // When reading all the bytes back + int length = parsableByteArray.limit(); + assertThat(length).isEqualTo(TEST_DATA.length); + byte[] copy = new byte[length]; + parsableByteArray.readBytes(copy, 0, length); + // Then the array elements are the same. + assertThat(copy).isEqualTo(parsableByteArray.data); + } + + @Test + public void testReadLittleEndianLong() { + ParsableByteArray byteArray = new ParsableByteArray(new byte[] { + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, (byte) 0xFF + }); + assertThat(byteArray.readLittleEndianLong()).isEqualTo(0xFF00000000000001L); + assertThat(byteArray.getPosition()).isEqualTo(8); + } + + @Test + public void testReadLittleEndianUnsignedInt() { + ParsableByteArray byteArray = new ParsableByteArray(new byte[] { + 0x10, 0x00, 0x00, (byte) 0xFF + }); + assertThat(byteArray.readLittleEndianUnsignedInt()).isEqualTo(0xFF000010L); + assertThat(byteArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadLittleEndianInt() { + ParsableByteArray byteArray = new ParsableByteArray(new byte[] { + 0x01, 0x00, 0x00, (byte) 0xFF + }); + assertThat(byteArray.readLittleEndianInt()).isEqualTo(0xFF000001); + assertThat(byteArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadLittleEndianUnsignedInt24() { + byte[] data = { 0x01, 0x02, (byte) 0xFF }; + ParsableByteArray byteArray = new ParsableByteArray(data); + assertThat(byteArray.readLittleEndianUnsignedInt24()).isEqualTo(0xFF0201); + assertThat(byteArray.getPosition()).isEqualTo(3); + } + + @Test + public void testReadLittleEndianUnsignedShort() { + ParsableByteArray byteArray = new ParsableByteArray(new byte[] { + 0x01, (byte) 0xFF, 0x02, (byte) 0xFF + }); + assertThat(byteArray.readLittleEndianUnsignedShort()).isEqualTo(0xFF01); + assertThat(byteArray.getPosition()).isEqualTo(2); + assertThat(byteArray.readLittleEndianUnsignedShort()).isEqualTo(0xFF02); + assertThat(byteArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadLittleEndianShort() { + ParsableByteArray byteArray = new ParsableByteArray(new byte[] { + 0x01, (byte) 0xFF, 0x02, (byte) 0xFF + }); + assertThat(byteArray.readLittleEndianShort()).isEqualTo((short) 0xFF01); + assertThat(byteArray.getPosition()).isEqualTo(2); + assertThat(byteArray.readLittleEndianShort()).isEqualTo((short) 0xFF02); + assertThat(byteArray.getPosition()).isEqualTo(4); + } + + @Test + public void testReadString() { + byte[] data = { + (byte) 0xC3, (byte) 0xA4, (byte) 0x20, + (byte) 0xC3, (byte) 0xB6, (byte) 0x20, + (byte) 0xC2, (byte) 0xAE, (byte) 0x20, + (byte) 0xCF, (byte) 0x80, (byte) 0x20, + (byte) 0xE2, (byte) 0x88, (byte) 0x9A, (byte) 0x20, + (byte) 0xC2, (byte) 0xB1, (byte) 0x20, + (byte) 0xE8, (byte) 0xB0, (byte) 0xA2, (byte) 0x20, + }; + ParsableByteArray byteArray = new ParsableByteArray(data); + assertThat(byteArray.readString(data.length)).isEqualTo("ä ö ® π √ ± 谢 "); + assertThat(byteArray.getPosition()).isEqualTo(data.length); + } + + @Test + public void testReadAsciiString() { + byte[] data = new byte[] {'t', 'e', 's', 't'}; + ParsableByteArray testArray = new ParsableByteArray(data); + assertThat(testArray.readString(data.length, forName("US-ASCII"))).isEqualTo("test"); + assertThat(testArray.getPosition()).isEqualTo(data.length); + } + + @Test + public void testReadStringOutOfBoundsDoesNotMovePosition() { + byte[] data = { + (byte) 0xC3, (byte) 0xA4, (byte) 0x20 + }; + ParsableByteArray byteArray = new ParsableByteArray(data); + try { + byteArray.readString(data.length + 1); + fail(); + } catch (StringIndexOutOfBoundsException e) { + assertThat(byteArray.getPosition()).isEqualTo(0); + } + } + + @Test + public void testReadEmptyString() { + byte[] bytes = new byte[0]; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readLine()).isNull(); + } + + @Test + public void testReadNullTerminatedStringWithLengths() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', 0, 'b', 'a', 'r', 0 + }; + // Test with lengths that match NUL byte positions. + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readNullTerminatedString(4)).isEqualTo("foo"); + assertThat(parser.getPosition()).isEqualTo(4); + assertThat(parser.readNullTerminatedString(4)).isEqualTo("bar"); + assertThat(parser.getPosition()).isEqualTo(8); + assertThat(parser.readNullTerminatedString()).isNull(); + // Test with lengths that do not match NUL byte positions. + parser = new ParsableByteArray(bytes); + assertThat(parser.readNullTerminatedString(2)).isEqualTo("fo"); + assertThat(parser.getPosition()).isEqualTo(2); + assertThat(parser.readNullTerminatedString(2)).isEqualTo("o"); + assertThat(parser.getPosition()).isEqualTo(4); + assertThat(parser.readNullTerminatedString(3)).isEqualTo("bar"); + assertThat(parser.getPosition()).isEqualTo(7); + assertThat(parser.readNullTerminatedString(1)).isEqualTo(""); + assertThat(parser.getPosition()).isEqualTo(8); + assertThat(parser.readNullTerminatedString()).isNull(); + // Test with limit at NUL + parser = new ParsableByteArray(bytes, 4); + assertThat(parser.readNullTerminatedString(4)).isEqualTo("foo"); + assertThat(parser.getPosition()).isEqualTo(4); + assertThat(parser.readNullTerminatedString()).isNull(); + // Test with limit before NUL + parser = new ParsableByteArray(bytes, 3); + assertThat(parser.readNullTerminatedString(3)).isEqualTo("foo"); + assertThat(parser.getPosition()).isEqualTo(3); + assertThat(parser.readNullTerminatedString()).isNull(); + } + + @Test + public void testReadNullTerminatedString() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', 0, 'b', 'a', 'r', 0 + }; + // Test normal case. + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readNullTerminatedString()).isEqualTo("foo"); + assertThat(parser.getPosition()).isEqualTo(4); + assertThat(parser.readNullTerminatedString()).isEqualTo("bar"); + assertThat(parser.getPosition()).isEqualTo(8); + assertThat(parser.readNullTerminatedString()).isNull(); + // Test with limit at NUL. + parser = new ParsableByteArray(bytes, 4); + assertThat(parser.readNullTerminatedString()).isEqualTo("foo"); + assertThat(parser.getPosition()).isEqualTo(4); + assertThat(parser.readNullTerminatedString()).isNull(); + // Test with limit before NUL. + parser = new ParsableByteArray(bytes, 3); + assertThat(parser.readNullTerminatedString()).isEqualTo("foo"); + assertThat(parser.getPosition()).isEqualTo(3); + assertThat(parser.readNullTerminatedString()).isNull(); + } + + @Test + public void testReadNullTerminatedStringWithoutEndingNull() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', 0, 'b', 'a', 'r' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readNullTerminatedString()).isEqualTo("foo"); + assertThat(parser.readNullTerminatedString()).isEqualTo("bar"); + assertThat(parser.readNullTerminatedString()).isNull(); + } + + @Test + public void testReadSingleLineWithoutEndingTrail() { + byte[] bytes = new byte[] { + 'f', 'o', 'o' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readLine()).isEqualTo("foo"); + assertThat(parser.readLine()).isNull(); + } + + @Test + public void testReadSingleLineWithEndingLf() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\n' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readLine()).isEqualTo("foo"); + assertThat(parser.readLine()).isNull(); + } + + @Test + public void testReadTwoLinesWithCrFollowedByLf() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\r', '\n', 'b', 'a', 'r' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readLine()).isEqualTo("foo"); + assertThat(parser.readLine()).isEqualTo("bar"); + assertThat(parser.readLine()).isNull(); + } + + @Test + public void testReadThreeLinesWithEmptyLine() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\r', '\n', '\r', 'b', 'a', 'r' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readLine()).isEqualTo("foo"); + assertThat(parser.readLine()).isEqualTo(""); + assertThat(parser.readLine()).isEqualTo("bar"); + assertThat(parser.readLine()).isNull(); + } + + @Test + public void testReadFourLinesWithLfFollowedByCr() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\n', '\r', '\r', 'b', 'a', 'r', '\r', '\n' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertThat(parser.readLine()).isEqualTo("foo"); + assertThat(parser.readLine()).isEqualTo(""); + assertThat(parser.readLine()).isEqualTo(""); + assertThat(parser.readLine()).isEqualTo("bar"); + assertThat(parser.readLine()).isNull(); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java new file mode 100644 index 0000000000..a3f38abcdb --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java @@ -0,0 +1,128 @@ +/* + * 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.util; + +import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link ParsableNalUnitBitArray}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class ParsableNalUnitBitArrayTest { + + private static final byte[] NO_ESCAPING_TEST_DATA = createByteArray(0, 3, 0, 1, 3, 0, 0); + private static final byte[] ALL_ESCAPING_TEST_DATA = createByteArray(0, 0, 3, 0, 0, 3, 0, 0, 3); + private static final byte[] MIX_TEST_DATA = createByteArray(255, 0, 0, 3, 255, 0, 0, 127); + + @Test + public void testReadNoEscaping() { + ParsableNalUnitBitArray array = + new ParsableNalUnitBitArray(NO_ESCAPING_TEST_DATA, 0, NO_ESCAPING_TEST_DATA.length); + assertThat(array.readBits(24)).isEqualTo(0x000300); + assertThat(array.readBits(7)).isEqualTo(0); + assertThat(array.readBit()).isTrue(); + assertThat(array.readBits(24)).isEqualTo(0x030000); + assertThat(array.canReadBits(1)).isFalse(); + assertThat(array.canReadBits(8)).isFalse(); + } + + @Test + public void testReadNoEscapingTruncated() { + ParsableNalUnitBitArray array = new ParsableNalUnitBitArray(NO_ESCAPING_TEST_DATA, 0, 4); + assertThat(array.canReadBits(32)).isTrue(); + array.skipBits(32); + assertThat(array.canReadBits(1)).isFalse(); + try { + array.readBit(); + fail(); + } catch (Exception e) { + // Expected. + } + } + + @Test + public void testReadAllEscaping() { + ParsableNalUnitBitArray array = + new ParsableNalUnitBitArray(ALL_ESCAPING_TEST_DATA, 0, ALL_ESCAPING_TEST_DATA.length); + assertThat(array.canReadBits(48)).isTrue(); + assertThat(array.canReadBits(49)).isFalse(); + assertThat(array.readBits(15)).isEqualTo(0); + assertThat(array.readBit()).isFalse(); + assertThat(array.readBits(17)).isEqualTo(0); + assertThat(array.readBits(15)).isEqualTo(0); + } + + @Test + public void testReadMix() { + ParsableNalUnitBitArray array = + new ParsableNalUnitBitArray(MIX_TEST_DATA, 0, MIX_TEST_DATA.length); + assertThat(array.canReadBits(56)).isTrue(); + assertThat(array.canReadBits(57)).isFalse(); + assertThat(array.readBits(7)).isEqualTo(127); + assertThat(array.readBits(2)).isEqualTo(2); + assertThat(array.readBits(17)).isEqualTo(3); + assertThat(array.readBits(7)).isEqualTo(126); + assertThat(array.readBits(23)).isEqualTo(127); + assertThat(array.canReadBits(1)).isFalse(); + } + + @Test + public void testReadExpGolomb() { + ParsableNalUnitBitArray array = new ParsableNalUnitBitArray(createByteArray(0x9E), 0, 1); + assertThat(array.canReadExpGolombCodedNum()).isTrue(); + assertThat(array.readUnsignedExpGolombCodedInt()).isEqualTo(0); + assertThat(array.readUnsignedExpGolombCodedInt()).isEqualTo(6); + assertThat(array.readUnsignedExpGolombCodedInt()).isEqualTo(0); + assertThat(array.canReadExpGolombCodedNum()).isFalse(); + try { + array.readUnsignedExpGolombCodedInt(); + fail(); + } catch (Exception e) { + // Expected. + } + } + + @Test + public void testReadExpGolombWithEscaping() { + ParsableNalUnitBitArray array = + new ParsableNalUnitBitArray(createByteArray(0, 0, 3, 128, 0), 0, 5); + assertThat(array.canReadExpGolombCodedNum()).isFalse(); + array.skipBit(); + assertThat(array.canReadExpGolombCodedNum()).isTrue(); + assertThat(array.readUnsignedExpGolombCodedInt()).isEqualTo(32767); + assertThat(array.canReadBits(1)).isFalse(); + } + + @Test + public void testReset() { + ParsableNalUnitBitArray array = new ParsableNalUnitBitArray(createByteArray(0, 0), 0, 2); + assertThat(array.canReadExpGolombCodedNum()).isFalse(); + assertThat(array.canReadBits(16)).isTrue(); + assertThat(array.canReadBits(17)).isFalse(); + array.reset(createByteArray(0, 0, 3, 0), 0, 4); + assertThat(array.canReadBits(24)).isTrue(); + assertThat(array.canReadBits(25)).isFalse(); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ReusableBufferedOutputStreamTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ReusableBufferedOutputStreamTest.java new file mode 100644 index 0000000000..8e384bbb10 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ReusableBufferedOutputStreamTest.java @@ -0,0 +1,53 @@ +/* + * 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.util; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.ByteArrayOutputStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Tests {@link ReusableBufferedOutputStream}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class ReusableBufferedOutputStreamTest { + + private static final byte[] TEST_DATA_1 = "test data 1".getBytes(); + private static final byte[] TEST_DATA_2 = "2 test data".getBytes(); + + @Test + public void testReset() throws Exception { + ByteArrayOutputStream byteArrayOutputStream1 = new ByteArrayOutputStream(1000); + ReusableBufferedOutputStream outputStream = new ReusableBufferedOutputStream( + byteArrayOutputStream1, 1000); + outputStream.write(TEST_DATA_1); + outputStream.close(); + + ByteArrayOutputStream byteArrayOutputStream2 = new ByteArrayOutputStream(1000); + outputStream.reset(byteArrayOutputStream2); + outputStream.write(TEST_DATA_2); + outputStream.close(); + + assertThat(byteArrayOutputStream1.toByteArray()).isEqualTo(TEST_DATA_1); + assertThat(byteArrayOutputStream2.toByteArray()).isEqualTo(TEST_DATA_2); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/UriUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/UriUtilTest.java new file mode 100644 index 0000000000..52e7a722fb --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/UriUtilTest.java @@ -0,0 +1,109 @@ +/* + * 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.util; + +import static com.google.android.exoplayer2.util.UriUtil.resolve; +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Unit tests for {@link UriUtil}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public final class UriUtilTest { + + /** + * Tests normal usage of {@link UriUtil#resolve(String, String)}. + *

+ * The test cases are taken from RFC-3986 5.4.1. + */ + @Test + public void testResolveNormal() { + String base = "http://a/b/c/d;p?q"; + + assertThat(resolve(base, "g:h")).isEqualTo("g:h"); + assertThat(resolve(base, "g")).isEqualTo("http://a/b/c/g"); + assertThat(resolve(base, "g/")).isEqualTo("http://a/b/c/g/"); + assertThat(resolve(base, "/g")).isEqualTo("http://a/g"); + assertThat(resolve(base, "//g")).isEqualTo("http://g"); + assertThat(resolve(base, "?y")).isEqualTo("http://a/b/c/d;p?y"); + assertThat(resolve(base, "g?y")).isEqualTo("http://a/b/c/g?y"); + assertThat(resolve(base, "#s")).isEqualTo("http://a/b/c/d;p?q#s"); + assertThat(resolve(base, "g#s")).isEqualTo("http://a/b/c/g#s"); + assertThat(resolve(base, "g?y#s")).isEqualTo("http://a/b/c/g?y#s"); + assertThat(resolve(base, ";x")).isEqualTo("http://a/b/c/;x"); + assertThat(resolve(base, "g;x")).isEqualTo("http://a/b/c/g;x"); + assertThat(resolve(base, "g;x?y#s")).isEqualTo("http://a/b/c/g;x?y#s"); + assertThat(resolve(base, "")).isEqualTo("http://a/b/c/d;p?q"); + assertThat(resolve(base, ".")).isEqualTo("http://a/b/c/"); + assertThat(resolve(base, "./")).isEqualTo("http://a/b/c/"); + assertThat(resolve(base, "..")).isEqualTo("http://a/b/"); + assertThat(resolve(base, "../")).isEqualTo("http://a/b/"); + assertThat(resolve(base, "../g")).isEqualTo("http://a/b/g"); + assertThat(resolve(base, "../..")).isEqualTo("http://a/"); + assertThat(resolve(base, "../../")).isEqualTo("http://a/"); + assertThat(resolve(base, "../../g")).isEqualTo("http://a/g"); + } + + /** + * Tests abnormal usage of {@link UriUtil#resolve(String, String)}. + *

+ * The test cases are taken from RFC-3986 5.4.2. + */ + @Test + public void testResolveAbnormal() { + String base = "http://a/b/c/d;p?q"; + + assertThat(resolve(base, "../../../g")).isEqualTo("http://a/g"); + assertThat(resolve(base, "../../../../g")).isEqualTo("http://a/g"); + + assertThat(resolve(base, "/./g")).isEqualTo("http://a/g"); + assertThat(resolve(base, "/../g")).isEqualTo("http://a/g"); + assertThat(resolve(base, "g.")).isEqualTo("http://a/b/c/g."); + assertThat(resolve(base, ".g")).isEqualTo("http://a/b/c/.g"); + assertThat(resolve(base, "g..")).isEqualTo("http://a/b/c/g.."); + assertThat(resolve(base, "..g")).isEqualTo("http://a/b/c/..g"); + + assertThat(resolve(base, "./../g")).isEqualTo("http://a/b/g"); + assertThat(resolve(base, "./g/.")).isEqualTo("http://a/b/c/g/"); + assertThat(resolve(base, "g/./h")).isEqualTo("http://a/b/c/g/h"); + assertThat(resolve(base, "g/../h")).isEqualTo("http://a/b/c/h"); + assertThat(resolve(base, "g;x=1/./y")).isEqualTo("http://a/b/c/g;x=1/y"); + assertThat(resolve(base, "g;x=1/../y")).isEqualTo("http://a/b/c/y"); + + assertThat(resolve(base, "g?y/./x")).isEqualTo("http://a/b/c/g?y/./x"); + assertThat(resolve(base, "g?y/../x")).isEqualTo("http://a/b/c/g?y/../x"); + assertThat(resolve(base, "g#s/./x")).isEqualTo("http://a/b/c/g#s/./x"); + assertThat(resolve(base, "g#s/../x")).isEqualTo("http://a/b/c/g#s/../x"); + + assertThat(resolve(base, "http:g")).isEqualTo("http:g"); + } + + /** + * Tests additional abnormal usage of {@link UriUtil#resolve(String, String)}. + */ + @Test + public void testResolveAbnormalAdditional() { + assertThat(resolve("http://a/b", "c:d/../e")).isEqualTo("c:e"); + assertThat(resolve("a:b", "../c")).isEqualTo("a:c"); + } + +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java new file mode 100644 index 0000000000..70caff9bf1 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java @@ -0,0 +1,200 @@ +/* + * 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.util; + +import static com.google.android.exoplayer2.util.Util.binarySearchCeil; +import static com.google.android.exoplayer2.util.Util.binarySearchFloor; +import static com.google.android.exoplayer2.util.Util.escapeFileName; +import static com.google.android.exoplayer2.util.Util.parseXsDateTime; +import static com.google.android.exoplayer2.util.Util.parseXsDuration; +import static com.google.android.exoplayer2.util.Util.unescapeFileName; +import static com.google.common.truth.Truth.assertThat; + +import com.google.android.exoplayer2.testutil.TestUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Unit tests for {@link Util}. + */ +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) +public class UtilTest { + + @Test + public void testArrayBinarySearchFloor() { + long[] values = new long[0]; + assertThat(binarySearchFloor(values, 0, false, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 0, false, true)).isEqualTo(0); + + values = new long[] {1, 3, 5}; + assertThat(binarySearchFloor(values, 0, false, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 0, true, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 0, false, true)).isEqualTo(0); + assertThat(binarySearchFloor(values, 0, true, true)).isEqualTo(0); + + assertThat(binarySearchFloor(values, 1, false, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 1, true, false)).isEqualTo(0); + assertThat(binarySearchFloor(values, 1, false, true)).isEqualTo(0); + assertThat(binarySearchFloor(values, 1, true, true)).isEqualTo(0); + + assertThat(binarySearchFloor(values, 4, false, false)).isEqualTo(1); + assertThat(binarySearchFloor(values, 4, true, false)).isEqualTo(1); + + assertThat(binarySearchFloor(values, 5, false, false)).isEqualTo(1); + assertThat(binarySearchFloor(values, 5, true, false)).isEqualTo(2); + + assertThat(binarySearchFloor(values, 6, false, false)).isEqualTo(2); + assertThat(binarySearchFloor(values, 6, true, false)).isEqualTo(2); + } + + @Test + public void testListBinarySearchFloor() { + List values = new ArrayList<>(); + assertThat(binarySearchFloor(values, 0, false, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 0, false, true)).isEqualTo(0); + + values.add(1); + values.add(3); + values.add(5); + assertThat(binarySearchFloor(values, 0, false, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 0, true, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 0, false, true)).isEqualTo(0); + assertThat(binarySearchFloor(values, 0, true, true)).isEqualTo(0); + + assertThat(binarySearchFloor(values, 1, false, false)).isEqualTo(-1); + assertThat(binarySearchFloor(values, 1, true, false)).isEqualTo(0); + assertThat(binarySearchFloor(values, 1, false, true)).isEqualTo(0); + assertThat(binarySearchFloor(values, 1, true, true)).isEqualTo(0); + + assertThat(binarySearchFloor(values, 4, false, false)).isEqualTo(1); + assertThat(binarySearchFloor(values, 4, true, false)).isEqualTo(1); + + assertThat(binarySearchFloor(values, 5, false, false)).isEqualTo(1); + assertThat(binarySearchFloor(values, 5, true, false)).isEqualTo(2); + + assertThat(binarySearchFloor(values, 6, false, false)).isEqualTo(2); + assertThat(binarySearchFloor(values, 6, true, false)).isEqualTo(2); + } + + @Test + public void testArrayBinarySearchCeil() { + long[] values = new long[0]; + assertThat(binarySearchCeil(values, 0, false, false)).isEqualTo(0); + assertThat(binarySearchCeil(values, 0, false, true)).isEqualTo(-1); + + values = new long[] {1, 3, 5}; + assertThat(binarySearchCeil(values, 0, false, false)).isEqualTo(0); + assertThat(binarySearchCeil(values, 0, true, false)).isEqualTo(0); + + assertThat(binarySearchCeil(values, 1, false, false)).isEqualTo(1); + assertThat(binarySearchCeil(values, 1, true, false)).isEqualTo(0); + + assertThat(binarySearchCeil(values, 2, false, false)).isEqualTo(1); + assertThat(binarySearchCeil(values, 2, true, false)).isEqualTo(1); + + assertThat(binarySearchCeil(values, 5, false, false)).isEqualTo(3); + assertThat(binarySearchCeil(values, 5, true, false)).isEqualTo(2); + assertThat(binarySearchCeil(values, 5, false, true)).isEqualTo(2); + assertThat(binarySearchCeil(values, 5, true, true)).isEqualTo(2); + + assertThat(binarySearchCeil(values, 6, false, false)).isEqualTo(3); + assertThat(binarySearchCeil(values, 6, true, false)).isEqualTo(3); + assertThat(binarySearchCeil(values, 6, false, true)).isEqualTo(2); + assertThat(binarySearchCeil(values, 6, true, true)).isEqualTo(2); + } + + @Test + public void testListBinarySearchCeil() { + List values = new ArrayList<>(); + assertThat(binarySearchCeil(values, 0, false, false)).isEqualTo(0); + assertThat(binarySearchCeil(values, 0, false, true)).isEqualTo(-1); + + values.add(1); + values.add(3); + values.add(5); + assertThat(binarySearchCeil(values, 0, false, false)).isEqualTo(0); + assertThat(binarySearchCeil(values, 0, true, false)).isEqualTo(0); + + assertThat(binarySearchCeil(values, 1, false, false)).isEqualTo(1); + assertThat(binarySearchCeil(values, 1, true, false)).isEqualTo(0); + + assertThat(binarySearchCeil(values, 2, false, false)).isEqualTo(1); + assertThat(binarySearchCeil(values, 2, true, false)).isEqualTo(1); + + assertThat(binarySearchCeil(values, 5, false, false)).isEqualTo(3); + assertThat(binarySearchCeil(values, 5, true, false)).isEqualTo(2); + assertThat(binarySearchCeil(values, 5, false, true)).isEqualTo(2); + assertThat(binarySearchCeil(values, 5, true, true)).isEqualTo(2); + + assertThat(binarySearchCeil(values, 6, false, false)).isEqualTo(3); + assertThat(binarySearchCeil(values, 6, true, false)).isEqualTo(3); + assertThat(binarySearchCeil(values, 6, false, true)).isEqualTo(2); + assertThat(binarySearchCeil(values, 6, true, true)).isEqualTo(2); + } + + @Test + public void testParseXsDuration() { + assertThat(parseXsDuration("PT150.279S")).isEqualTo(150279L); + assertThat(parseXsDuration("PT1.500S")).isEqualTo(1500L); + } + + @Test + public void testParseXsDateTime() throws Exception { + assertThat(parseXsDateTime("2014-06-19T23:07:42")).isEqualTo(1403219262000L); + assertThat(parseXsDateTime("2014-08-06T11:00:00Z")).isEqualTo(1407322800000L); + assertThat(parseXsDateTime("2014-08-06T11:00:00,000Z")).isEqualTo(1407322800000L); + assertThat(parseXsDateTime("2014-09-19T13:18:55-08:00")).isEqualTo(1411161535000L); + assertThat(parseXsDateTime("2014-09-19T13:18:55-0800")).isEqualTo(1411161535000L); + assertThat(parseXsDateTime("2014-09-19T13:18:55.000-0800")).isEqualTo(1411161535000L); + assertThat(parseXsDateTime("2014-09-19T13:18:55.000-800")).isEqualTo(1411161535000L); + } + + @Test + public void testUnescapeInvalidFileName() { + assertThat(Util.unescapeFileName("%a")).isNull(); + assertThat(Util.unescapeFileName("%xyz")).isNull(); + } + + @Test + public void testEscapeUnescapeFileName() { + assertEscapeUnescapeFileName("just+a regular+fileName", "just+a regular+fileName"); + assertEscapeUnescapeFileName("key:value", "key%3avalue"); + assertEscapeUnescapeFileName("<>:\"/\\|?*%", "%3c%3e%3a%22%2f%5c%7c%3f%2a%25"); + + Random random = new Random(0); + for (int i = 0; i < 1000; i++) { + String string = TestUtil.buildTestString(1000, random); + assertEscapeUnescapeFileName(string); + } + } + + private static void assertEscapeUnescapeFileName(String fileName, String escapedFileName) { + assertThat(escapeFileName(fileName)).isEqualTo(escapedFileName); + assertThat(unescapeFileName(escapedFileName)).isEqualTo(fileName); + } + + private static void assertEscapeUnescapeFileName(String fileName) { + String escapedFileName = Util.escapeFileName(fileName); + assertThat(unescapeFileName(escapedFileName)).isEqualTo(fileName); + } + +}