diff --git a/core_settings.gradle b/core_settings.gradle index 20a7c87bde2..c4914e30403 100644 --- a/core_settings.gradle +++ b/core_settings.gradle @@ -24,6 +24,7 @@ include modulePrefix + 'library-hls' include modulePrefix + 'library-smoothstreaming' include modulePrefix + 'library-ui' include modulePrefix + 'testutils' +include modulePrefix + 'testutils-robolectric' include modulePrefix + 'extension-ffmpeg' include modulePrefix + 'extension-flac' include modulePrefix + 'extension-gvr' @@ -43,6 +44,7 @@ project(modulePrefix + 'library-hls').projectDir = new File(rootDir, 'library/hl project(modulePrefix + 'library-smoothstreaming').projectDir = new File(rootDir, 'library/smoothstreaming') project(modulePrefix + 'library-ui').projectDir = new File(rootDir, 'library/ui') project(modulePrefix + 'testutils').projectDir = new File(rootDir, 'testutils') +project(modulePrefix + 'testutils-robolectric').projectDir = new File(rootDir, 'testutils_robolectric') project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'extensions/ffmpeg') project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac') project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr') diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle index d11f166c896..2f79c7a0ee6 100644 --- a/extensions/cast/build.gradle +++ b/extensions/cast/build.gradle @@ -38,10 +38,7 @@ dependencies { compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion compile project(modulePrefix + 'library-core') compile project(modulePrefix + 'library-ui') - testCompile project(modulePrefix + 'testutils') - testCompile 'junit:junit:' + junitVersion - testCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile 'org.robolectric:robolectric:' + robolectricVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/extensions/cast/src/test/AndroidManifest.xml b/extensions/cast/src/test/AndroidManifest.xml new file mode 100644 index 00000000000..057efdc245e --- /dev/null +++ b/extensions/cast/src/test/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java index bf4b20e1560..4c60e7c0b35 100644 --- a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java +++ b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java @@ -17,6 +17,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.testutil.TimelineAsserts; +import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaStatus; @@ -25,11 +26,9 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; /** Tests for {@link CastTimelineTracker}. */ @RunWith(RobolectricTestRunner.class) -@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) public class CastTimelineTrackerTest { private static final long DURATION_1_MS = 1000; @@ -49,12 +48,12 @@ public void testGetCastTimeline() { new long[] {DURATION_1_MS, MediaInfo.UNKNOWN_DURATION, MediaInfo.UNKNOWN_DURATION}); CastTimelineTracker tracker = new CastTimelineTracker(); - mediaInfo = mockMediaInfo("contentId1", DURATION_1_MS); + mediaInfo = getMediaInfo("contentId1", DURATION_1_MS); Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(status), C.msToUs(DURATION_1_MS), C.TIME_UNSET, C.TIME_UNSET); - mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS); + mediaInfo = getMediaInfo("contentId3", DURATION_3_MS); Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(status), @@ -62,7 +61,7 @@ public void testGetCastTimeline() { C.TIME_UNSET, C.msToUs(DURATION_3_MS)); - mediaInfo = mockMediaInfo("contentId2", DURATION_2_MS); + mediaInfo = getMediaInfo("contentId2", DURATION_2_MS); Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(status), @@ -80,7 +79,7 @@ public void testGetCastTimeline() { DURATION_5_MS, MediaInfo.UNKNOWN_DURATION }); - mediaInfo = mockMediaInfo("contentId5", DURATION_5_MS); + mediaInfo = getMediaInfo("contentId5", DURATION_5_MS); Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(newStatus), @@ -89,7 +88,7 @@ public void testGetCastTimeline() { C.msToUs(DURATION_5_MS), C.msToUs(DURATION_3_MS)); - mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS); + mediaInfo = getMediaInfo("contentId3", DURATION_3_MS); Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(newStatus), @@ -98,7 +97,7 @@ public void testGetCastTimeline() { C.msToUs(DURATION_5_MS), C.msToUs(DURATION_3_MS)); - mediaInfo = mockMediaInfo("contentId4", DURATION_4_MS); + mediaInfo = getMediaInfo("contentId4", DURATION_4_MS); Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(newStatus), @@ -112,7 +111,7 @@ private static MediaStatus mockMediaStatus( int[] itemIds, String[] contentIds, long[] durationsMs) { ArrayList items = new ArrayList<>(); for (int i = 0; i < contentIds.length; i++) { - MediaInfo mediaInfo = mockMediaInfo(contentIds[i], durationsMs[i]); + MediaInfo mediaInfo = getMediaInfo(contentIds[i], durationsMs[i]); MediaQueueItem item = Mockito.mock(MediaQueueItem.class); Mockito.when(item.getMedia()).thenReturn(mediaInfo); Mockito.when(item.getItemId()).thenReturn(itemIds[i]); @@ -123,10 +122,11 @@ private static MediaStatus mockMediaStatus( return status; } - private static MediaInfo mockMediaInfo(String contentId, long durationMs) { - MediaInfo mediaInfo = Mockito.mock(MediaInfo.class); - Mockito.when(mediaInfo.getContentId()).thenReturn(contentId); - Mockito.when(mediaInfo.getStreamDuration()).thenReturn(durationMs); - return mediaInfo; + private static MediaInfo getMediaInfo(String contentId, long durationMs) { + return new MediaInfo.Builder(contentId) + .setStreamDuration(durationMs) + .setContentType(MimeTypes.APPLICATION_MP4) + .setStreamType(MediaInfo.STREAM_TYPE_NONE) + .build(); } } diff --git a/extensions/cast/src/test/resources/robolectric.properties b/extensions/cast/src/test/resources/robolectric.properties new file mode 100644 index 00000000000..2f3210368ec --- /dev/null +++ b/extensions/cast/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/extensions/cronet/build.gradle b/extensions/cronet/build.gradle index 0b6f9a587c0..2d25c7299ce 100644 --- a/extensions/cronet/build.gradle +++ b/extensions/cronet/build.gradle @@ -39,12 +39,8 @@ dependencies { compile files('libs/cronet_api.jar') compile files('libs/cronet_impl_common_java.jar') compile files('libs/cronet_impl_native_java.jar') - androidTestCompile project(modulePrefix + 'library') - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion - androidTestCompile 'com.android.support.test:runner:' + testSupportLibraryVersion + testCompile project(modulePrefix + 'library') + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/extensions/cronet/src/androidTest/AndroidManifest.xml b/extensions/cronet/src/test/AndroidManifest.xml similarity index 71% rename from extensions/cronet/src/androidTest/AndroidManifest.xml rename to extensions/cronet/src/test/AndroidManifest.xml index 453cc684785..52be9aa1576 100644 --- a/extensions/cronet/src/androidTest/AndroidManifest.xml +++ b/extensions/cronet/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.ext.cronet"> - - - - - - - + diff --git a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java similarity index 90% rename from extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java rename to extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java index 28d22b91a58..291e73fcc16 100644 --- a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java +++ b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java @@ -19,9 +19,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import com.google.android.exoplayer2.testutil.MockitoUtil; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -30,11 +27,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; -/** - * Tests for {@link ByteArrayUploadDataProvider}. - */ -@RunWith(AndroidJUnit4.class) +/** Tests for {@link ByteArrayUploadDataProvider}. */ +@RunWith(RobolectricTestRunner.class) public final class ByteArrayUploadDataProviderTest { private static final byte[] TEST_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; @@ -45,7 +42,7 @@ public final class ByteArrayUploadDataProviderTest { @Before public void setUp() { - MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this); + MockitoAnnotations.initMocks(this); byteBuffer = ByteBuffer.allocate(TEST_DATA.length); byteArrayUploadDataProvider = new ByteArrayUploadDataProvider(TEST_DATA); } @@ -90,5 +87,4 @@ public void testRewind() throws IOException { assertThat(byteBuffer.array()).isEqualTo(TEST_DATA); verify(mockUploadDataSink).onRewindSucceeded(); } - } diff --git a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java similarity index 71% rename from extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java rename to extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java index 79be44398ed..4e990cd027a 100644 --- a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java +++ b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java @@ -31,10 +31,8 @@ import android.net.Uri; import android.os.ConditionVariable; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import android.os.SystemClock; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException; @@ -50,6 +48,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import org.chromium.net.CronetEngine; @@ -61,13 +60,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowSystemClock; -/** - * Tests for {@link CronetDataSource}. - */ -@RunWith(AndroidJUnit4.class) +/** Tests for {@link CronetDataSource}. */ +@RunWith(RobolectricTestRunner.class) public final class CronetDataSourceTest { private static final int TEST_CONNECT_TIMEOUT_MS = 100; @@ -85,18 +85,11 @@ public final class CronetDataSourceTest { private UrlResponseInfo testUrlResponseInfo; @Mock private UrlRequest.Builder mockUrlRequestBuilder; - @Mock - private UrlRequest mockUrlRequest; - @Mock - private Predicate mockContentTypePredicate; - @Mock - private TransferListener mockTransferListener; - @Mock - private Clock mockClock; - @Mock - private Executor mockExecutor; - @Mock - private NetworkException mockNetworkException; + @Mock private UrlRequest mockUrlRequest; + @Mock private Predicate mockContentTypePredicate; + @Mock private TransferListener mockTransferListener; + @Mock private Executor mockExecutor; + @Mock private NetworkException mockNetworkException; @Mock private CronetEngine mockCronetEngine; private CronetDataSource dataSourceUnderTest; @@ -104,30 +97,31 @@ public final class CronetDataSourceTest { @Before public void setUp() throws Exception { - MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this); - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - false)); + MockitoAnnotations.initMocks(this); + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + false)); when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true); when(mockCronetEngine.newUrlRequestBuilder( - anyString(), any(UrlRequest.Callback.class), any(Executor.class))) + anyString(), any(UrlRequest.Callback.class), any(Executor.class))) .thenReturn(mockUrlRequestBuilder); when(mockUrlRequestBuilder.allowDirectExecutor()).thenReturn(mockUrlRequestBuilder); when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest); mockStatusResponse(); testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null); - testPostDataSpec = new DataSpec( - Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0); + testPostDataSpec = + new DataSpec(Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0); testResponseHeader = new HashMap<>(); testResponseHeader.put("Content-Type", TEST_CONTENT_TYPE); // This value can be anything since the DataSpec is unset. @@ -173,20 +167,19 @@ public void testCallbackFromPreviousRequest() throws HttpDataSourceException { // Prepare a mock UrlRequest to be used in the second open() call. final UrlRequest mockUrlRequest2 = mock(UrlRequest.class); when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest2); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - // Invoke the callback for the previous request. - dataSourceUnderTest.onFailed( - mockUrlRequest, - testUrlResponseInfo, - mockNetworkException); - dataSourceUnderTest.onResponseStarted( - mockUrlRequest2, - testUrlResponseInfo); - return null; - } - }).when(mockUrlRequest2).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + // Invoke the callback for the previous request. + dataSourceUnderTest.onFailed( + mockUrlRequest, testUrlResponseInfo, mockNetworkException); + dataSourceUnderTest.onResponseStarted(mockUrlRequest2, testUrlResponseInfo); + return null; + } + }) + .when(mockUrlRequest2) + .start(); dataSourceUnderTest.open(testDataSpec); } @@ -253,8 +246,8 @@ public void testRequestOpenFail() { @Test public void testRequestOpenFailDueToDnsFailure() { mockResponseStartFailure(); - when(mockNetworkException.getErrorCode()).thenReturn( - NetworkException.ERROR_HOSTNAME_NOT_RESOLVED); + when(mockNetworkException.getErrorCode()) + .thenReturn(NetworkException.ERROR_HOSTNAME_NOT_RESOLVED); try { dataSourceUnderTest.open(testDataSpec); @@ -524,8 +517,8 @@ public void testOverread() throws HttpDataSourceException { assertThat(bytesOverRead).isEqualTo(C.RESULT_END_OF_INPUT); assertThat(returnedBuffer).isEqualTo(new byte[16]); // C.RESULT_END_OF_INPUT should not be reported though the TransferListener. - verify(mockTransferListener, never()).onBytesTransferred(dataSourceUnderTest, - C.RESULT_END_OF_INPUT); + verify(mockTransferListener, never()) + .onBytesTransferred(dataSourceUnderTest, C.RESULT_END_OF_INPUT); // There should still be only one call to read on cronet. verify(mockUrlRequest, times(1)).read(any(ByteBuffer.class)); // Check for connection not automatically closed. @@ -534,10 +527,10 @@ public void testOverread() throws HttpDataSourceException { } @Test - public void testConnectTimeout() { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testConnectTimeout() throws InterruptedException { + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); new Thread() { @Override @@ -551,29 +544,29 @@ public void run() { assertThat(e.getCause() instanceof SocketTimeoutException).isTrue(); assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus) .isEqualTo(TEST_CONNECTION_STATUS); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }.start(); startCondition.block(); // We should still be trying to open. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(timedOutCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(timedOutLatch); // Now we timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS); - timedOutCondition.block(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS + 10); + timedOutLatch.await(); verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec); } @Test - public void testConnectInterrupted() { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testConnectInterrupted() throws InterruptedException { + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); Thread thread = new Thread() { @@ -588,7 +581,7 @@ public void run() { assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue(); assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus) .isEqualTo(TEST_INVALID_CONNECTION_STATUS); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }; @@ -596,29 +589,29 @@ public void run() { startCondition.block(); // We should still be trying to open. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(timedOutCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(timedOutLatch); // Now we interrupt. thread.interrupt(); - timedOutCondition.block(); + timedOutLatch.await(); verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec); } @Test - public void testConnectResponseBeforeTimeout() { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testConnectResponseBeforeTimeout() throws InterruptedException { + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable openCondition = new ConditionVariable(); + final CountDownLatch openLatch = new CountDownLatch(1); new Thread() { @Override public void run() { try { dataSourceUnderTest.open(testDataSpec); - openCondition.open(); + openLatch.countDown(); } catch (HttpDataSourceException e) { fail(); } @@ -627,20 +620,20 @@ public void run() { startCondition.block(); // We should still be trying to open. - assertThat(openCondition.block(50)).isFalse(); + assertNotCountedDown(openLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(openCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(openLatch); // The response arrives just in time. dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); - openCondition.block(); + openLatch.await(); } @Test public void testRedirectIncreasesConnectionTimeout() throws InterruptedException { - when(mockClock.elapsedRealtime()).thenReturn(0L); + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); final AtomicInteger openExceptions = new AtomicInteger(0); new Thread() { @@ -654,40 +647,36 @@ public void run() { assertThat(e instanceof CronetDataSource.OpenException).isTrue(); assertThat(e.getCause() instanceof SocketTimeoutException).isTrue(); openExceptions.getAndIncrement(); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }.start(); startCondition.block(); // We should still be trying to open. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(timedOutCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(timedOutLatch); // A redirect arrives just in time. - dataSourceUnderTest.onRedirectReceived(mockUrlRequest, testUrlResponseInfo, - "RandomRedirectedUrl1"); + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1"); long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1; - when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs - 1); - // Give the thread some time to run. - assertThat(timedOutCondition.block(newTimeoutMs)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs - 1); // We should still be trying to open as we approach the new timeout. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // A redirect arrives just in time. - dataSourceUnderTest.onRedirectReceived(mockUrlRequest, testUrlResponseInfo, - "RandomRedirectedUrl2"); + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2"); newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2; - when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs - 1); - // Give the thread some time to run. - assertThat(timedOutCondition.block(newTimeoutMs)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs - 1); // We should still be trying to open as we approach the new timeout. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // Now we timeout. - when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs); - timedOutCondition.block(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs + 10); + timedOutLatch.await(); verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec); assertThat(openExceptions.get()).isEqualTo(1); @@ -707,20 +696,22 @@ public void testRedirectParseAndAttachCookie_dataSourceDoesNotHandleSetCookie_fo } @Test - public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders() - throws HttpDataSourceException { - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - true)); + public void + testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders() + throws HttpDataSourceException { + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + true)); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); mockSingleRedirectSuccess(); @@ -736,21 +727,23 @@ public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPrese } @Test - public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeadersIncludingByteRangeHeader() - throws HttpDataSourceException { + public void + testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeadersIncludingByteRangeHeader() + throws HttpDataSourceException { testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null); - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - true)); + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + true)); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); mockSingleRedirectSuccess(); @@ -778,18 +771,19 @@ public void testRedirectNoSetCookieFollowsRedirect() throws HttpDataSourceExcept @Test public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie() throws HttpDataSourceException { - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - true)); + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + true)); mockSingleRedirectSuccess(); mockFollowRedirectSuccess(); @@ -804,8 +798,9 @@ public void testExceptionFromTransferListener() throws HttpDataSourceException { // Make mockTransferListener throw an exception in CronetDataSource.close(). Ensure that // the subsequent open() call succeeds. - doThrow(new NullPointerException()).when(mockTransferListener).onTransferEnd( - dataSourceUnderTest); + doThrow(new NullPointerException()) + .when(mockTransferListener) + .onTransferEnd(dataSourceUnderTest); dataSourceUnderTest.open(testDataSpec); try { dataSourceUnderTest.close(); @@ -833,13 +828,12 @@ public void testReadFailure() throws HttpDataSourceException { } @Test - public void testReadInterrupted() throws HttpDataSourceException { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testReadInterrupted() throws HttpDataSourceException, InterruptedException { mockResponseStartSuccess(); dataSourceUnderTest.open(testDataSpec); final ConditionVariable startCondition = buildReadStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); byte[] returnedBuffer = new byte[8]; Thread thread = new Thread() { @@ -851,17 +845,17 @@ public void run() { } catch (HttpDataSourceException e) { // Expected. assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue(); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }; thread.start(); startCondition.block(); - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // Now we interrupt. thread.interrupt(); - timedOutCondition.block(); + timedOutLatch.await(); } @Test @@ -876,122 +870,135 @@ public void testAllowDirectExecutor() throws HttpDataSourceException { // Helper methods. private void mockStatusResponse() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - UrlRequest.StatusListener statusListener = - (UrlRequest.StatusListener) invocation.getArguments()[0]; - statusListener.onStatus(TEST_CONNECTION_STATUS); - return null; - } - }).when(mockUrlRequest).getStatus(any(UrlRequest.StatusListener.class)); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + UrlRequest.StatusListener statusListener = + (UrlRequest.StatusListener) invocation.getArguments()[0]; + statusListener.onStatus(TEST_CONNECTION_STATUS); + return null; + } + }) + .when(mockUrlRequest) + .getStatus(any(UrlRequest.StatusListener.class)); } private void mockResponseStartSuccess() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onResponseStarted( - mockUrlRequest, - testUrlResponseInfo); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockResponseStartRedirect() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onRedirectReceived( - mockUrlRequest, - createUrlResponseInfo(307), // statusCode - "http://redirect.location.com"); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, + createUrlResponseInfo(307), // statusCode + "http://redirect.location.com"); + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockSingleRedirectSuccess() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - if (!redirectCalled) { - redirectCalled = true; - dataSourceUnderTest.onRedirectReceived( - mockUrlRequest, - createUrlResponseInfoWithUrl("http://example.com/video", 300), - "http://example.com/video/redirect"); - } else { - dataSourceUnderTest.onResponseStarted( - mockUrlRequest, - testUrlResponseInfo); - } - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if (!redirectCalled) { + redirectCalled = true; + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, + createUrlResponseInfoWithUrl("http://example.com/video", 300), + "http://example.com/video/redirect"); + } else { + dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); + } + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockFollowRedirectSuccess() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onResponseStarted( - mockUrlRequest, - testUrlResponseInfo); - return null; - } - }).when(mockUrlRequest).followRedirect(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); + return null; + } + }) + .when(mockUrlRequest) + .followRedirect(); } private void mockResponseStartFailure() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onFailed( - mockUrlRequest, - createUrlResponseInfo(500), // statusCode - mockNetworkException); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onFailed( + mockUrlRequest, + createUrlResponseInfo(500), // statusCode + mockNetworkException); + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockReadSuccess(int position, int length) { final int[] positionAndRemaining = new int[] {position, length}; - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - if (positionAndRemaining[1] == 0) { - dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo); - } else { - ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0]; - int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining()); - inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength)); - positionAndRemaining[0] += readLength; - positionAndRemaining[1] -= readLength; - dataSourceUnderTest.onReadCompleted( - mockUrlRequest, - testUrlResponseInfo, - inputBuffer); - } - return null; - } - }).when(mockUrlRequest).read(any(ByteBuffer.class)); + doAnswer( + new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + if (positionAndRemaining[1] == 0) { + dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo); + } else { + ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0]; + int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining()); + inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength)); + positionAndRemaining[0] += readLength; + positionAndRemaining[1] -= readLength; + dataSourceUnderTest.onReadCompleted( + mockUrlRequest, testUrlResponseInfo, inputBuffer); + } + return null; + } + }) + .when(mockUrlRequest) + .read(any(ByteBuffer.class)); } private void mockReadFailure() { doAnswer( - new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onFailed( - mockUrlRequest, - createUrlResponseInfo(500), // statusCode - mockNetworkException); - return null; - } - }) + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onFailed( + mockUrlRequest, + createUrlResponseInfo(500), // statusCode + mockNetworkException); + return null; + } + }) .when(mockUrlRequest) .read(any(ByteBuffer.class)); } @@ -999,13 +1006,13 @@ public Object answer(InvocationOnMock invocation) throws Throwable { private ConditionVariable buildReadStartedCondition() { final ConditionVariable startedCondition = new ConditionVariable(); doAnswer( - new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - startedCondition.open(); - return null; - } - }) + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + startedCondition.open(); + return null; + } + }) .when(mockUrlRequest) .read(any(ByteBuffer.class)); return startedCondition; @@ -1013,16 +1020,26 @@ public Object answer(InvocationOnMock invocation) throws Throwable { private ConditionVariable buildUrlRequestStartedCondition() { final ConditionVariable startedCondition = new ConditionVariable(); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - startedCondition.open(); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + startedCondition.open(); + return null; + } + }) + .when(mockUrlRequest) + .start(); return startedCondition; } + private void assertNotCountedDown(CountDownLatch countDownLatch) throws InterruptedException { + // We are asserting that another thread does not count down the latch. We therefore sleep some + // time to give the other thread the chance to fail this test. + Thread.sleep(50); + assertThat(countDownLatch.getCount()).isGreaterThan(0L); + } + private static byte[] buildTestDataArray(int position, int length) { return buildTestDataBuffer(position, length).array(); } @@ -1045,5 +1062,4 @@ private static ByteBuffer buildTestDataBuffer(int position, int length) { testBuffer.flip(); return testBuffer; } - } diff --git a/extensions/cronet/src/test/resources/robolectric.properties b/extensions/cronet/src/test/resources/robolectric.properties new file mode 100644 index 00000000000..2f3210368ec --- /dev/null +++ b/extensions/cronet/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/library/core/build.gradle b/library/core/build.gradle index a87e11065fa..3d655ba543d 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -31,6 +31,7 @@ android { } test { java.srcDirs += "../../testutils/src/main/java/" + java.srcDirs += "../../testutils_robolectric/src/main/java/" } } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java index 83a978219e3..3465393853d 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java @@ -89,7 +89,7 @@ private static void assertData(Instrumentation instrumentation, int offset, int ContentDataSource dataSource = new ContentDataSource(instrumentation.getContext()); try { DataSpec dataSpec = new DataSpec(contentUri, offset, length, null); - byte[] completeData = TestUtil.getByteArray(instrumentation, DATA_PATH); + byte[] completeData = TestUtil.getByteArray(instrumentation.getContext(), DATA_PATH); byte[] expectedData = Arrays.copyOfRange(completeData, offset, length == C.LENGTH_UNSET ? completeData.length : offset + length); TestUtil.assertDataSourceContent(dataSource, dataSpec, expectedData, !pipeMode); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 8477a215d39..b1ddcdb2077 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -42,6 +42,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTrackSelection; import com.google.android.exoplayer2.testutil.FakeTrackSelector; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.upstream.Allocator; import java.io.IOException; import java.util.ArrayList; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java b/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java index f67301f017b..6f62b7fcfc4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java @@ -22,8 +22,8 @@ import android.util.Pair; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import java.util.HashMap; import org.junit.After; import org.junit.Before; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index 1e0d8681c5b..a4aa3eb9385 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -20,7 +20,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Window; @@ -29,6 +28,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import org.junit.Before; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java index ccc3ddea465..465e08b5d24 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java @@ -19,7 +19,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.testutil.FakeMediaSource; @@ -27,6 +26,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import org.junit.Test; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java index c1537c50d3b..24f1ddd5ed9 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java @@ -24,7 +24,6 @@ import android.os.HandlerThread; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.testutil.FakeMediaSource; @@ -32,6 +31,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import java.util.Arrays; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java index f0b47724228..6aa710aff4b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java @@ -17,12 +17,12 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import org.junit.Before; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java index b03a76c23e3..839492f1967 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java @@ -19,13 +19,13 @@ import static org.junit.Assert.fail; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MergingMediaSource.IllegalMergeException; import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/library/dash/build.gradle b/library/dash/build.gradle index 99441a28496..6cf6f644439 100644 --- a/library/dash/build.gradle +++ b/library/dash/build.gradle @@ -35,15 +35,7 @@ android { dependencies { compile project(modulePrefix + 'library-core') compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile project(modulePrefix + 'testutils') - testCompile 'com.google.truth:truth:' + truthVersion - testCompile 'junit:junit:' + junitVersion - testCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile 'org.robolectric:robolectric:' + robolectricVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java b/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java deleted file mode 100644 index 50752c8a72d..00000000000 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java +++ /dev/null @@ -1,100 +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.source.dash.offline; - -import android.net.Uri; - -/** - * Data for DASH downloading tests. - */ -/* package */ interface DashDownloadTestData { - - Uri TEST_MPD_URI = Uri.parse("test.mpd"); - - byte[] TEST_MPD = - ("\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - // Bounded range data - + " \n" - // Unbounded range data - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - // This segment list has a 1 second offset to make sure the progressive download order - + " \n" - + " \n" - + " \n" // 1s offset - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "").getBytes(); - - byte[] TEST_MPD_NO_INDEX = - ("\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "").getBytes(); -} diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index eb9b18512c1..98783ac93e8 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -50,6 +50,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Locale; @@ -1113,7 +1114,9 @@ public Long parse(Uri uri, InputStream inputStream) throws IOException { @Override public Long parse(Uri uri, InputStream inputStream) throws IOException { - String firstLine = new BufferedReader(new InputStreamReader(inputStream)).readLine(); + String firstLine = + new BufferedReader(new InputStreamReader(inputStream, Charset.forName(C.UTF8_NAME))) + .readLine(); try { Matcher matcher = TIMESTAMP_WITH_TIMEZONE_PATTERN.matcher(firstLine); if (!matcher.matches()) { diff --git a/library/dash/src/androidTest/AndroidManifest.xml b/library/dash/src/test/AndroidManifest.xml similarity index 71% rename from library/dash/src/androidTest/AndroidManifest.xml rename to library/dash/src/test/AndroidManifest.xml index 39596a81657..eecf596b929 100644 --- a/library/dash/src/androidTest/AndroidManifest.xml +++ b/library/dash/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.source.dash.test"> - - - - - - - + diff --git a/library/dash/src/androidTest/assets/sample_mpd_1 b/library/dash/src/test/assets/sample_mpd_1 similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_1 rename to library/dash/src/test/assets/sample_mpd_1 diff --git a/library/dash/src/androidTest/assets/sample_mpd_2_unknown_mime_type b/library/dash/src/test/assets/sample_mpd_2_unknown_mime_type similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_2_unknown_mime_type rename to library/dash/src/test/assets/sample_mpd_2_unknown_mime_type diff --git a/library/dash/src/androidTest/assets/sample_mpd_3_segment_template b/library/dash/src/test/assets/sample_mpd_3_segment_template similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_3_segment_template rename to library/dash/src/test/assets/sample_mpd_3_segment_template diff --git a/library/dash/src/androidTest/assets/sample_mpd_4_event_stream b/library/dash/src/test/assets/sample_mpd_4_event_stream similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_4_event_stream rename to library/dash/src/test/assets/sample_mpd_4_event_stream diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java new file mode 100644 index 00000000000..1c440c70bea --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java @@ -0,0 +1,76 @@ +/* + * 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.source.dash; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.upstream.ParsingLoadable; +import com.google.android.exoplayer2.util.Util; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link DashMediaSource}. */ +@RunWith(RobolectricTestRunner.class) +public final class DashMediaSourceTest { + + @Test + public void testIso8601ParserParse() throws IOException { + DashMediaSource.Iso8601Parser parser = new DashMediaSource.Iso8601Parser(); + // UTC. + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37Z"); + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+00:00"); + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+0000"); + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+00"); + // Positive timezone offsets. + assertParseStringToLong(1512381697000L - 4980000L, parser, "2017-12-04T10:01:37+01:23"); + assertParseStringToLong(1512381697000L - 4980000L, parser, "2017-12-04T10:01:37+0123"); + assertParseStringToLong(1512381697000L - 3600000L, parser, "2017-12-04T10:01:37+01"); + // Negative timezone offsets with minus character. + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37-01:23"); + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37-0123"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-01:00"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-0100"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-01"); + // Negative timezone offsets with hyphen character. + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37−01:23"); + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37−0123"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−01:00"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−0100"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−01"); + } + + @Test + public void testIso8601ParserParseMissingTimezone() throws IOException { + DashMediaSource.Iso8601Parser parser = new DashMediaSource.Iso8601Parser(); + try { + assertParseStringToLong(0, parser, "2017-12-04T10:01:37"); + fail(); + } catch (ParserException e) { + // Expected. + } + } + + private static void assertParseStringToLong( + long expected, ParsingLoadable.Parser parser, String data) throws IOException { + long actual = parser.parse(null, new ByteArrayInputStream(Util.getUtf8Bytes(data))); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java similarity index 81% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java index 4ddbf429ab3..15fa3b33559 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java @@ -28,33 +28,38 @@ import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.util.MimeTypes; import java.util.Arrays; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link DashUtil}. - */ -public final class DashUtilTest extends TestCase { +/** Unit tests for {@link DashUtil}. */ +@RunWith(RobolectricTestRunner.class) +public final class DashUtilTest { + @Test public void testLoadDrmInitDataFromManifest() throws Exception { Period period = newPeriod(newAdaptationSets(newRepresentations(newDrmInitData()))); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isEqualTo(newDrmInitData()); } + @Test public void testLoadDrmInitDataMissing() throws Exception { Period period = newPeriod(newAdaptationSets(newRepresentations(null /* no init data */))); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isNull(); } + @Test public void testLoadDrmInitDataNoRepresentations() throws Exception { - Period period = newPeriod(newAdaptationSets(/* no representation */)); + Period period = newPeriod(newAdaptationSets(/* no representation */ )); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isNull(); } + @Test public void testLoadDrmInitDataNoAdaptationSets() throws Exception { - Period period = newPeriod(/* no adaptation set */); + Period period = newPeriod(/* no adaptation set */ ); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isNull(); } @@ -68,8 +73,18 @@ private static AdaptationSet newAdaptationSets(Representation... representations } private static Representation newRepresentations(DrmInitData drmInitData) { - Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4, - MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0); + Format format = + Format.createVideoContainerFormat( + "id", + MimeTypes.VIDEO_MP4, + MimeTypes.VIDEO_H264, + "", + Format.NO_VALUE, + 1024, + 768, + Format.NO_VALUE, + null, + 0); if (drmInitData != null) { format = format.copyWithDrmInitData(drmInitData); } @@ -77,8 +92,7 @@ private static Representation newRepresentations(DrmInitData drmInitData) { } private static DrmInitData newDrmInitData() { - return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType", - new byte[] {1, 4, 7, 0, 3, 6})); + return new DrmInitData( + new SchemeData(C.WIDEVINE_UUID, "mimeType", new byte[] {1, 4, 7, 0, 3, 6})); } - } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java new file mode 100644 index 00000000000..9c3752551a1 --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java @@ -0,0 +1,356 @@ +/* + * 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.source.dash; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.FormatHolder; +import com.google.android.exoplayer2.decoder.DecoderInputBuffer; +import com.google.android.exoplayer2.metadata.MetadataInputBuffer; +import com.google.android.exoplayer2.metadata.emsg.EventMessage; +import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder; +import com.google.android.exoplayer2.source.dash.manifest.EventStream; +import com.google.android.exoplayer2.util.MimeTypes; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** + * Unit test for {@link EventSampleStream}. + */ +@RunWith(RobolectricTestRunner.class) +public final class EventSampleStreamTest { + + private static final String SCHEME_ID = "urn:test"; + private static final String VALUE = "123"; + private static final Format FORMAT = Format.createSampleFormat("urn:test/123", + MimeTypes.APPLICATION_EMSG, null, Format.NO_VALUE, null); + private static final byte[] MESSAGE_DATA = new byte[] {1, 2, 3, 4}; + private static final long DURATION_MS = 3000; + private static final long TIME_SCALE = 1000; + + private FormatHolder formatHolder; + private MetadataInputBuffer inputBuffer; + private EventMessageEncoder eventMessageEncoder; + + @Before + public void setUp() { + formatHolder = new FormatHolder(); + inputBuffer = new MetadataInputBuffer(); + eventMessageEncoder = new EventMessageEncoder(); + } + + /** + * Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} will + * return format for the first call. + */ + @Test + public void testReadDataReturnFormatForFirstRead() { + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[0], new EventMessage[0]); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_FORMAT_READ); + assertThat(formatHolder.format).isEqualTo(FORMAT); + } + + /** + * Tests that a non-dynamic {@link EventSampleStream} will return a buffer with + * {@link C#BUFFER_FLAG_END_OF_STREAM} when trying to read sample out-of-bound. + */ + @Test + public void testReadDataOutOfBoundReturnEndOfStreamAfterFormatForNonDynamicEventSampleStream() { + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[0], new EventMessage[0]); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.isEndOfStream()).isTrue(); + } + + /** + * Tests that a dynamic {@link EventSampleStream} will return {@link C#RESULT_NOTHING_READ} + * when trying to read sample out-of-bound. + */ + @Test + public void testReadDataOutOfBoundReturnEndOfStreamAfterFormatForDynamicEventSampleStream() { + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[0], new EventMessage[0]); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, true); + // first read - read format + readData(sampleStream); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_NOTHING_READ); + } + + /** + * Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} will + * return sample data after the first call. + */ + @Test + public void testReadDataReturnDataAfterFormat() { + long presentationTimeUs = 1000000; + EventMessage eventMessage = newEventMessageWithIdAndTime(1, presentationTimeUs); + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs}, new EventMessage[] {eventMessage}); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage)); + } + + /** + * Tests that {@link EventSampleStream#skipData(long)} will skip until the given position, and + * the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from that position. + */ + @Test + public void testSkipDataThenReadDataReturnDataFromSkippedPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + int skipped = sampleStream.skipData(presentationTimeUs2); + int result = readData(sampleStream); + assertThat(skipped).isEqualTo(1); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#seekToUs(long)} (long)} will seek to the given position, + * and the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from that position. + */ + @Test + public void testSeekToUsThenReadDataReturnDataFromSeekPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + sampleStream.seekToUs(presentationTimeUs2); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the read timestamp, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from after the last read sample timestamp. + */ + @Test + public void testUpdateEventStreamContinueToReadAfterLastReadSamplePresentationTime() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + // read first and second sample. + readData(sampleStream); + readData(sampleStream); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage3)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the timestamp the stream has skipped to, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the skipped position. + */ + @Test + public void testSkipDataThenUpdateStreamContinueToReadFromSkippedPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + sampleStream.skipData(presentationTimeUs2 + 1); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage3)); + } + + /** + * Tests that {@link EventSampleStream#skipData(long)} will only skip to the point right after + * it last event. A following {@link EventSampleStream#updateEventStream(EventStream, boolean)} + * will update the underlying event stream and keep the timestamp the stream has skipped to, so + * the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the skipped position. + */ + @Test + public void testSkipDataThenUpdateStreamContinueToReadDoNotSkippedMoreThanAvailable() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1}, + new EventMessage[] {eventMessage1}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + // even though the skip call is to 2000001, since eventStream1 only contains sample until + // 1000000, it will only skip to 1000001. + sampleStream.skipData(presentationTimeUs2 + 1); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the timestamp the stream has seek to, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the seek position. + */ + @Test + public void testSeekToUsThenUpdateStreamContinueToReadFromSeekPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + sampleStream.seekToUs(presentationTimeUs2); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the timestamp the stream has seek to, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the seek position. + */ + @Test + public void testSeekToThenUpdateStreamContinueToReadFromSeekPositionEvenSeekMoreThanAvailable() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1}, + new EventMessage[] {eventMessage1}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + sampleStream.seekToUs(presentationTimeUs2 + 1); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage3)); + } + + private int readData(EventSampleStream sampleStream) { + inputBuffer.clear(); + return sampleStream.readData(formatHolder, inputBuffer, false); + } + + private EventMessage newEventMessageWithIdAndTime(int id, long presentationTimeUs) { + return new EventMessage(SCHEME_ID, VALUE, DURATION_MS, id, MESSAGE_DATA, presentationTimeUs); + } + + private byte[] getEncodedMessage(EventMessage eventMessage) { + return eventMessageEncoder.encode(eventMessage, TIME_SCALE); + } + +} diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java similarity index 85% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java index 3b7982592dc..6f14c8790f7 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java @@ -18,39 +18,47 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; -import android.test.InstrumentationTestCase; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.metadata.emsg.EventMessage; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; +import java.nio.charset.Charset; import java.util.Collections; import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link DashManifestParser}. - */ -public class DashManifestParserTest extends InstrumentationTestCase { +/** Unit tests for {@link DashManifestParser}. */ +@RunWith(RobolectricTestRunner.class) +public class DashManifestParserTest { private static final String SAMPLE_MPD_1 = "sample_mpd_1"; private static final String SAMPLE_MPD_2_UNKNOWN_MIME_TYPE = "sample_mpd_2_unknown_mime_type"; private static final String SAMPLE_MPD_3_SEGMENT_TEMPLATE = "sample_mpd_3_segment_template"; private static final String SAMPLE_MPD_4_EVENT_STREAM = "sample_mpd_4_event_stream"; - /** - * Simple test to ensure the sample manifests parse without any exceptions being thrown. - */ + /** Simple test to ensure the sample manifests parse without any exceptions being thrown. */ + @Test public void testParseMediaPresentationDescription() throws IOException { DashManifestParser parser = new DashManifestParser(); - parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_1)); - parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_2_UNKNOWN_MIME_TYPE)); + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_1)); + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_2_UNKNOWN_MIME_TYPE)); } + @Test public void testParseMediaPresentationDescriptionWithSegmentTemplate() throws IOException { DashManifestParser parser = new DashManifestParser(); - DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_3_SEGMENT_TEMPLATE)); + DashManifest mpd = + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_3_SEGMENT_TEMPLATE)); assertThat(mpd.getPeriodCount()).isEqualTo(1); @@ -75,11 +83,13 @@ public void testParseMediaPresentationDescriptionWithSegmentTemplate() throws IO } } - public void testParseMediaPresentationDescriptionCanParseEventStream() - throws IOException { + @Test + public void testParseMediaPresentationDescriptionCanParseEventStream() throws IOException { DashManifestParser parser = new DashManifestParser(); - DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_4_EVENT_STREAM)); + DashManifest mpd = + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_4_EVENT_STREAM)); Period period = mpd.getPeriod(0); assertThat(period.eventStreams).hasSize(3); @@ -87,8 +97,14 @@ public void testParseMediaPresentationDescriptionCanParseEventStream() // assert text-only event stream EventStream eventStream1 = period.eventStreams.get(0); assertThat(eventStream1.events.length).isEqualTo(1); - EventMessage expectedEvent1 = new EventMessage("urn:uuid:XYZY", "call", 10000, 0, - "+ 1 800 10101010".getBytes(), 0); + EventMessage expectedEvent1 = + new EventMessage( + "urn:uuid:XYZY", + "call", + 10000, + 0, + "+ 1 800 10101010".getBytes(Charset.forName(C.UTF8_NAME)), + 0); assertThat(eventStream1.events[0]).isEqualTo(expectedEvent1); // assert CData-structured event stream @@ -135,6 +151,7 @@ public void testParseMediaPresentationDescriptionCanParseEventStream() 1000000000)); } + @Test public void testParseCea608AccessibilityChannel() { assertThat( DashManifestParser.parseCea608AccessibilityChannel( @@ -175,6 +192,7 @@ public void testParseCea608AccessibilityChannel() { .isEqualTo(Format.NO_VALUE); } + @Test public void testParseCea708AccessibilityChannel() { assertThat( DashManifestParser.parseCea708AccessibilityChannel( @@ -226,5 +244,4 @@ private static List buildCea608AccessibilityDescriptors(String value private static List buildCea708AccessibilityDescriptors(String value) { return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-708:2015", value, null)); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java similarity index 54% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java index c6060a7caad..9cf35941160 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java @@ -24,109 +24,143 @@ import java.util.Collections; import java.util.List; import java.util.Random; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link DashManifest}. - */ -public class DashManifestTest extends TestCase { +/** Unit tests for {@link DashManifest}. */ +@RunWith(RobolectricTestRunner.class) +public class DashManifestTest { private static final UtcTimingElement DUMMY_UTC_TIMING = new UtcTimingElement("", ""); private static final SingleSegmentBase DUMMY_SEGMENT_BASE = new SingleSegmentBase(); private static final Format DUMMY_FORMAT = Format.createSampleFormat("", "", 0); + @Test public void testCopy() throws Exception { Representation[][][] representations = newRepresentations(3, 2, 3); - DashManifest sourceManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0]), - newAdaptationSet(3, representations[0][1])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]), - newAdaptationSet(6, representations[1][1])), - newPeriod("7", 7, - newAdaptationSet(8, representations[2][0]), - newAdaptationSet(9, representations[2][1]))); - - List keys = Arrays.asList( - new RepresentationKey(0, 0, 0), - new RepresentationKey(0, 0, 1), - new RepresentationKey(0, 1, 2), - - new RepresentationKey(1, 0, 1), - new RepresentationKey(1, 1, 0), - new RepresentationKey(1, 1, 2), - - new RepresentationKey(2, 0, 1), - new RepresentationKey(2, 0, 2), - new RepresentationKey(2, 1, 0)); + DashManifest sourceManifest = + newDashManifest( + 10, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0]), + newAdaptationSet(3, representations[0][1])), + newPeriod( + "4", + 4, + newAdaptationSet(5, representations[1][0]), + newAdaptationSet(6, representations[1][1])), + newPeriod( + "7", + 7, + newAdaptationSet(8, representations[2][0]), + newAdaptationSet(9, representations[2][1]))); + + List keys = + Arrays.asList( + new RepresentationKey(0, 0, 0), + new RepresentationKey(0, 0, 1), + new RepresentationKey(0, 1, 2), + new RepresentationKey(1, 0, 1), + new RepresentationKey(1, 1, 0), + new RepresentationKey(1, 1, 2), + new RepresentationKey(2, 0, 1), + new RepresentationKey(2, 0, 2), + new RepresentationKey(2, 1, 0)); // Keys don't need to be in any particular order Collections.shuffle(keys, new Random(0)); DashManifest copyManifest = sourceManifest.copy(keys); - DashManifest expectedManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), - newAdaptationSet(3, representations[0][1][2])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0][1]), - newAdaptationSet(6, representations[1][1][0], representations[1][1][2])), - newPeriod("7", 7, - newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), - newAdaptationSet(9, representations[2][1][0]))); + DashManifest expectedManifest = + newDashManifest( + 10, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), + newAdaptationSet(3, representations[0][1][2])), + newPeriod( + "4", + 4, + newAdaptationSet(5, representations[1][0][1]), + newAdaptationSet(6, representations[1][1][0], representations[1][1][2])), + newPeriod( + "7", + 7, + newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), + newAdaptationSet(9, representations[2][1][0]))); assertManifestEquals(expectedManifest, copyManifest); } + @Test public void testCopySameAdaptationIndexButDifferentPeriod() throws Exception { Representation[][][] representations = newRepresentations(2, 1, 1); - DashManifest sourceManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]))); - - DashManifest copyManifest = sourceManifest.copy(Arrays.asList( - new RepresentationKey(0, 0, 0), - new RepresentationKey(1, 0, 0))); - - DashManifest expectedManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]))); + DashManifest sourceManifest = + newDashManifest( + 10, + newPeriod("1", 1, newAdaptationSet(2, representations[0][0])), + newPeriod("4", 4, newAdaptationSet(5, representations[1][0]))); + + DashManifest copyManifest = + sourceManifest.copy( + Arrays.asList(new RepresentationKey(0, 0, 0), new RepresentationKey(1, 0, 0))); + + DashManifest expectedManifest = + newDashManifest( + 10, + newPeriod("1", 1, newAdaptationSet(2, representations[0][0])), + newPeriod("4", 4, newAdaptationSet(5, representations[1][0]))); assertManifestEquals(expectedManifest, copyManifest); } + @Test public void testCopySkipPeriod() throws Exception { Representation[][][] representations = newRepresentations(3, 2, 3); - DashManifest sourceManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0]), - newAdaptationSet(3, representations[0][1])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]), - newAdaptationSet(6, representations[1][1])), - newPeriod("7", 7, - newAdaptationSet(8, representations[2][0]), - newAdaptationSet(9, representations[2][1]))); - - DashManifest copyManifest = sourceManifest.copy(Arrays.asList( - new RepresentationKey(0, 0, 0), - new RepresentationKey(0, 0, 1), - new RepresentationKey(0, 1, 2), - - new RepresentationKey(2, 0, 1), - new RepresentationKey(2, 0, 2), - new RepresentationKey(2, 1, 0))); - - DashManifest expectedManifest = newDashManifest(7, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), - newAdaptationSet(3, representations[0][1][2])), - newPeriod("7", 4, - newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), - newAdaptationSet(9, representations[2][1][0]))); + DashManifest sourceManifest = + newDashManifest( + 10, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0]), + newAdaptationSet(3, representations[0][1])), + newPeriod( + "4", + 4, + newAdaptationSet(5, representations[1][0]), + newAdaptationSet(6, representations[1][1])), + newPeriod( + "7", + 7, + newAdaptationSet(8, representations[2][0]), + newAdaptationSet(9, representations[2][1]))); + + DashManifest copyManifest = + sourceManifest.copy( + Arrays.asList( + new RepresentationKey(0, 0, 0), + new RepresentationKey(0, 0, 1), + new RepresentationKey(0, 1, 2), + new RepresentationKey(2, 0, 1), + new RepresentationKey(2, 0, 2), + new RepresentationKey(2, 1, 0))); + + DashManifest expectedManifest = + newDashManifest( + 7, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), + newAdaptationSet(3, representations[0][1][2])), + newPeriod( + "7", + 4, + newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), + newAdaptationSet(9, representations[2][1][0]))); assertManifestEquals(expectedManifest, copyManifest); } @@ -164,8 +198,8 @@ private static void assertManifestEquals(DashManifest expected, DashManifest act } } - private static Representation[][][] newRepresentations(int periodCount, int adaptationSetCounts, - int representationCounts) { + private static Representation[][][] newRepresentations( + int periodCount, int adaptationSetCounts, int representationCounts) { Representation[][][] representations = new Representation[periodCount][][]; for (int i = 0; i < periodCount; i++) { representations[i] = new Representation[adaptationSetCounts][]; @@ -184,8 +218,8 @@ private static Representation newRepresentation() { } private static DashManifest newDashManifest(int duration, Period... periods) { - return new DashManifest(0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY, - Arrays.asList(periods)); + return new DashManifest( + 0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY, Arrays.asList(periods)); } private static Period newPeriod(String id, int startMs, AdaptationSet... adaptationSets) { @@ -195,5 +229,4 @@ private static Period newPeriod(String id, int startMs, AdaptationSet... adaptat private static AdaptationSet newAdaptationSet(int seed, Representation... representations) { return new AdaptationSet(++seed, ++seed, Arrays.asList(representations), null, null); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java similarity index 92% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java index 4073625cd16..16c9a4706ea 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java @@ -18,17 +18,19 @@ import static com.google.common.truth.Truth.assertThat; import com.google.android.exoplayer2.C; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit test for {@link RangedUri}. - */ -public class RangedUriTest extends TestCase { +/** Unit test for {@link RangedUri}. */ +@RunWith(RobolectricTestRunner.class) +public class RangedUriTest { private static final String BASE_URI = "http://www.test.com/"; private static final String PARTIAL_URI = "path/file.ext"; private static final String FULL_URI = BASE_URI + PARTIAL_URI; + @Test public void testMerge() { RangedUri rangeA = new RangedUri(FULL_URI, 0, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, 10); @@ -36,6 +38,7 @@ public void testMerge() { assertMerge(rangeA, rangeB, expected, null); } + @Test public void testMergeUnbounded() { RangedUri rangeA = new RangedUri(FULL_URI, 0, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET); @@ -43,6 +46,7 @@ public void testMergeUnbounded() { assertMerge(rangeA, rangeB, expected, null); } + @Test public void testNonMerge() { // A and B do not overlap, so should not merge RangedUri rangeA = new RangedUri(FULL_URI, 0, 10); @@ -65,6 +69,7 @@ public void testNonMerge() { assertNonMerge(rangeA, rangeB, null); } + @Test public void testMergeWithBaseUri() { RangedUri rangeA = new RangedUri(PARTIAL_URI, 0, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, 10); @@ -85,5 +90,4 @@ private void assertNonMerge(RangedUri rangeA, RangedUri rangeB, String baseUrl) merged = rangeB.attemptMerge(rangeA, baseUrl); assertThat(merged).isNull(); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java similarity index 54% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java index 2ceb0f05063..309e6c8eb02 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java @@ -20,27 +20,49 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase; import com.google.android.exoplayer2.util.MimeTypes; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit test for {@link Representation}. - */ -public class RepresentationTest extends TestCase { +/** Unit test for {@link Representation}. */ +@RunWith(RobolectricTestRunner.class) +public class RepresentationTest { + @Test public void testGetCacheKey() { String uri = "http://www.google.com"; SegmentBase base = new SingleSegmentBase(new RangedUri(null, 0, 1), 1, 0, 1, 1); - Format format = Format.createVideoContainerFormat("0", MimeTypes.APPLICATION_MP4, null, - MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0); - Representation representation = Representation.newInstance("test_stream_1", 3, format, uri, - base); + Format format = + Format.createVideoContainerFormat( + "0", + MimeTypes.APPLICATION_MP4, + null, + MimeTypes.VIDEO_H264, + 2500000, + 1920, + 1080, + Format.NO_VALUE, + null, + 0); + Representation representation = + Representation.newInstance("test_stream_1", 3, format, uri, base); assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.0.3"); - format = Format.createVideoContainerFormat("150", MimeTypes.APPLICATION_MP4, null, - MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0); - representation = Representation.newInstance("test_stream_1", Representation.REVISION_ID_DEFAULT, - format, uri, base); + format = + Format.createVideoContainerFormat( + "150", + MimeTypes.APPLICATION_MP4, + null, + MimeTypes.VIDEO_H264, + 2500000, + 1920, + 1080, + Format.NO_VALUE, + null, + 0); + representation = + Representation.newInstance( + "test_stream_1", Representation.REVISION_ID_DEFAULT, format, uri, base); assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.150.-1"); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java similarity index 89% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java index b3221bbc182..4192280c817 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java @@ -16,14 +16,17 @@ package com.google.android.exoplayer2.source.dash.manifest; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit test for {@link UrlTemplate}. - */ -public class UrlTemplateTest extends TestCase { +/** Unit test for {@link UrlTemplate}. */ +@RunWith(RobolectricTestRunner.class) +public class UrlTemplateTest { + @Test public void testRealExamples() { String template = "QualityLevels($Bandwidth$)/Fragments(video=$Time$,format=mpd-time-csf)"; UrlTemplate urlTemplate = UrlTemplate.compile(template); @@ -41,6 +44,7 @@ public void testRealExamples() { assertThat(url).isEqualTo("chunk_ctvideo_cfm4s_ridabc1_cn10_w2073857842_mpd.m4s"); } + @Test public void testFull() { String template = "$Bandwidth$_a_$RepresentationID$_b_$Time$_c_$Number$"; UrlTemplate urlTemplate = UrlTemplate.compile(template); @@ -48,6 +52,7 @@ public void testFull() { assertThat(url).isEqualTo("650000_a_abc1_b_5000_c_10"); } + @Test public void testFullWithDollarEscaping() { String template = "$$$Bandwidth$$$_a$$_$RepresentationID$_b_$Time$_c_$Number$$$"; UrlTemplate urlTemplate = UrlTemplate.compile(template); @@ -55,6 +60,7 @@ public void testFullWithDollarEscaping() { assertThat(url).isEqualTo("$650000$_a$_abc1_b_5000_c_10$"); } + @Test public void testInvalidSubstitution() { String template = "$IllegalId$"; try { @@ -64,5 +70,4 @@ public void testInvalidSubstitution() { // Expected. } } - } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java new file mode 100644 index 00000000000..ea47722b69b --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java @@ -0,0 +1,162 @@ +/* + * 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.source.dash.offline; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; +import com.google.android.exoplayer2.upstream.DummyDataSource; +import com.google.android.exoplayer2.upstream.cache.Cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +/** + * Unit tests for {@link DashDownloadAction}. + */ +@RunWith(RobolectricTestRunner.class) +public class DashDownloadActionTest { + + @Test + public void testDownloadActionIsNotRemoveAction() throws Exception { + DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action.isRemoveAction()).isFalse(); + } + + @Test + public void testRemoveActionIsRemoveAction() throws Exception { + DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), true, null); + assertThat(action2.isRemoveAction()).isTrue(); + } + + @Test + public void testCreateDownloader() throws Exception { + MockitoAnnotations.initMocks(this); + DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); + DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper( + Mockito.mock(Cache.class), DummyDataSource.FACTORY); + assertThat(action.createDownloader(constructorHelper)).isNotNull(); + } + + @Test + public void testSameUriDifferentAction_IsSameMedia() throws Exception { + DashDownloadAction action1 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action1.isSameMedia(action2)).isTrue(); + } + + @Test + public void testDifferentUriAndAction_IsNotSameMedia() throws Exception { + DashDownloadAction action3 = new DashDownloadAction(Uri.parse("uri2"), true, null); + DashDownloadAction action4 = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action3.isSameMedia(action4)).isFalse(); + } + + @SuppressWarnings("EqualsWithItself") + @Test + public void testEquals() throws Exception { + DashDownloadAction action1 = new DashDownloadAction(Uri.parse("uri"), true, null); + assertThat(action1.equals(action1)).isTrue(); + + DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action3 = new DashDownloadAction(Uri.parse("uri"), true, null); + assertEqual(action2, action3); + + DashDownloadAction action4 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action5 = new DashDownloadAction(Uri.parse("uri"), false, null); + assertNotEqual(action4, action5); + + DashDownloadAction action6 = new DashDownloadAction(Uri.parse("uri"), false, null); + DashDownloadAction action7 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(0, 0, 0)); + assertNotEqual(action6, action7); + + DashDownloadAction action8 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(1, 1, 1)); + DashDownloadAction action9 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(0, 0, 0)); + assertNotEqual(action8, action9); + + DashDownloadAction action10 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action11 = new DashDownloadAction(Uri.parse("uri2"), true, null); + assertNotEqual(action10, action11); + + DashDownloadAction action12 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(0, 0, 0), new RepresentationKey(1, 1, 1)); + DashDownloadAction action13 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(1, 1, 1), new RepresentationKey(0, 0, 0)); + assertEqual(action12, action13); + + DashDownloadAction action14 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(0, 0, 0)); + DashDownloadAction action15 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(1, 1, 1), new RepresentationKey(0, 0, 0)); + assertNotEqual(action14, action15); + + DashDownloadAction action16 = new DashDownloadAction(Uri.parse("uri"), false, null); + DashDownloadAction action17 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey[0]); + assertEqual(action16, action17); + } + + @Test + public void testSerializerGetType() throws Exception { + DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action.getType()).isNotNull(); + } + + @Test + public void testSerializerWriteRead() throws Exception { + doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri"), false, null)); + doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri"), true, null)); + doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri2"), false, null, + new RepresentationKey(0, 0, 0), new RepresentationKey(1, 1, 1))); + } + + private static void assertNotEqual(DashDownloadAction action1, DashDownloadAction action2) { + assertThat(action1).isNotEqualTo(action2); + assertThat(action2).isNotEqualTo(action1); + } + + private static void assertEqual(DashDownloadAction action1, DashDownloadAction action2) { + assertThat(action1).isEqualTo(action2); + assertThat(action2).isEqualTo(action1); + } + + private static void doTestSerializationRoundTrip(DashDownloadAction action1) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream output = new DataOutputStream(out); + action1.writeToStream(output); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + DataInputStream input = new DataInputStream(in); + DownloadAction action2 = + DashDownloadAction.DESERIALIZER.readFromStream(DownloadAction.MASTER_VERSION, input); + + assertThat(action1).isEqualTo(action2); + } + +} diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java new file mode 100644 index 00000000000..a215347f158 --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java @@ -0,0 +1,103 @@ +/* + * 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.source.dash.offline; + +import android.net.Uri; +import com.google.android.exoplayer2.C; +import java.nio.charset.Charset; + +/** Data for DASH downloading tests. */ +/* package */ interface DashDownloadTestData { + + Uri TEST_MPD_URI = Uri.parse("test.mpd"); + + byte[] TEST_MPD = + ("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // Bounded range data + + " \n" + // Unbounded range data + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // This segment list has a 1 second offset to make sure the progressive download order + + " \n" + + " \n" + + " \n" // 1s offset + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "") + .getBytes(Charset.forName(C.UTF8_NAME)); + + byte[] TEST_MPD_NO_INDEX = + ("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "") + .getBytes(Charset.forName(C.UTF8_NAME)); +} diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java similarity index 65% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java index 5e7ce43ebf8..d00b24e84fe 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java @@ -22,10 +22,10 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertDataCached; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.offline.DownloadException; import com.google.android.exoplayer2.offline.Downloader.ProgressListener; @@ -35,7 +35,6 @@ import com.google.android.exoplayer2.testutil.FakeDataSet; import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.FakeDataSource.Factory; -import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; @@ -44,34 +43,38 @@ import java.io.File; import java.io.IOException; import java.util.Arrays; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link DashDownloader}. - */ -public class DashDownloaderTest extends InstrumentationTestCase { +/** Unit tests for {@link DashDownloader}. */ +@RunWith(RobolectricTestRunner.class) +public class DashDownloaderTest { private SimpleCache cache; private File tempFolder; - @Override + @Before public void setUp() throws Exception { - super.setUp(); - MockitoUtil.setUpMockito(this); - tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest"); + MockitoAnnotations.initMocks(this); + tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest"); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); } - @Override + @After public void tearDown() throws Exception { Util.recursiveDelete(tempFolder); - super.tearDown(); } + @Test public void testGetManifest() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD); + FakeDataSet fakeDataSet = new FakeDataSet().setData(TEST_MPD_URI, TEST_MPD); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); DashManifest manifest = dashDownloader.getManifest(); @@ -80,15 +83,17 @@ public void testGetManifest() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadManifestFailure() throws Exception { byte[] testMpdFirstPart = Arrays.copyOf(TEST_MPD, 10); byte[] testMpdSecondPart = Arrays.copyOfRange(TEST_MPD, 10, TEST_MPD.length); - FakeDataSet fakeDataSet = new FakeDataSet() - .newData(TEST_MPD_URI) - .appendReadData(testMpdFirstPart) - .appendReadError(new IOException()) - .appendReadData(testMpdSecondPart) - .endData(); + FakeDataSet fakeDataSet = + new FakeDataSet() + .newData(TEST_MPD_URI) + .appendReadData(testMpdFirstPart) + .appendReadError(new IOException()) + .appendReadData(testMpdSecondPart) + .endData(); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); // fails on the first try @@ -108,13 +113,15 @@ public void testDownloadManifestFailure() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadRepresentation() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -123,17 +130,19 @@ public void testDownloadRepresentation() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadRepresentationInSmallParts() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .newData("audio_segment_1") - .appendReadData(TestUtil.buildTestData(10)) - .appendReadData(TestUtil.buildTestData(10)) - .appendReadData(TestUtil.buildTestData(10)) - .endData() - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .newData("audio_segment_1") + .appendReadData(TestUtil.buildTestData(10)) + .appendReadData(TestUtil.buildTestData(10)) + .appendReadData(TestUtil.buildTestData(10)) + .endData() + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -142,16 +151,18 @@ public void testDownloadRepresentationInSmallParts() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadRepresentations() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations( @@ -161,19 +172,21 @@ public void testDownloadRepresentations() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadAllRepresentations() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3) - .setRandomData("period_2_segment_1", 1) - .setRandomData("period_2_segment_2", 2) - .setRandomData("period_2_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3) + .setRandomData("period_2_segment_1", 1) + .setRandomData("period_2_segment_2", 2) + .setRandomData("period_2_segment_3", 3); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); // dashDownloader.selectRepresentations() isn't called @@ -195,21 +208,23 @@ public void testDownloadAllRepresentations() throws Exception { dashDownloader.remove(); } + @Test public void testProgressiveDownload() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3); FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet); Factory factory = mock(Factory.class); when(factory.createDataSource()).thenReturn(fakeDataSource); - DashDownloader dashDownloader = new DashDownloader(TEST_MPD_URI, - new DownloaderConstructorHelper(cache, factory)); + DashDownloader dashDownloader = + new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory)); dashDownloader.selectRepresentations( new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)}); @@ -227,21 +242,23 @@ public void testProgressiveDownload() throws Exception { assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("text_segment_3"); } + @Test public void testProgressiveDownloadSeparatePeriods() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("period_2_segment_1", 1) - .setRandomData("period_2_segment_2", 2) - .setRandomData("period_2_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("period_2_segment_1", 1) + .setRandomData("period_2_segment_2", 2) + .setRandomData("period_2_segment_3", 3); FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet); Factory factory = mock(Factory.class); when(factory.createDataSource()).thenReturn(fakeDataSource); - DashDownloader dashDownloader = new DashDownloader(TEST_MPD_URI, - new DownloaderConstructorHelper(cache, factory)); + DashDownloader dashDownloader = + new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory)); dashDownloader.selectRepresentations( new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(1, 0, 0)}); @@ -259,17 +276,19 @@ public void testProgressiveDownloadSeparatePeriods() throws Exception { assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("period_2_segment_3"); } + @Test public void testDownloadRepresentationFailure() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .newData("audio_segment_2") - .appendReadData(TestUtil.buildTestData(2)) - .appendReadError(new IOException()) - .appendReadData(TestUtil.buildTestData(3)) - .endData() - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .newData("audio_segment_2") + .appendReadData(TestUtil.buildTestData(2)) + .appendReadError(new IOException()) + .appendReadData(TestUtil.buildTestData(3)) + .endData() + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -285,17 +304,19 @@ public void testDownloadRepresentationFailure() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testCounters() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .newData("audio_segment_2") - .appendReadData(TestUtil.buildTestData(2)) - .appendReadError(new IOException()) - .appendReadData(TestUtil.buildTestData(3)) - .endData() - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .newData("audio_segment_2") + .appendReadData(TestUtil.buildTestData(2)) + .appendReadError(new IOException()) + .appendReadData(TestUtil.buildTestData(3)) + .endData() + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); assertCounters(dashDownloader, C.LENGTH_UNSET, C.LENGTH_UNSET, C.LENGTH_UNSET); @@ -319,13 +340,15 @@ public void testCounters() throws Exception { assertCounters(dashDownloader, 4, 4, 10 + 4 + 5 + 6); } + @Test public void testListener() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -340,16 +363,18 @@ public void testListener() throws Exception { inOrder.verifyNoMoreInteractions(); } + @Test public void testRemoveAll() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations( new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)}); @@ -360,10 +385,12 @@ public void testRemoveAll() throws Exception { assertCacheEmpty(cache); } + @Test public void testRepresentationWithoutIndex() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD_NO_INDEX) - .setRandomData("test_segment_1", 4); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD_NO_INDEX) + .setRandomData("test_segment_1", 4); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -379,13 +406,15 @@ public void testRepresentationWithoutIndex() throws Exception { assertCacheEmpty(cache); } + @Test public void testSelectRepresentationsClearsPreviousSelection() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations( @@ -401,11 +430,13 @@ private DashDownloader getDashDownloader(FakeDataSet fakeDataSet) { return new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory)); } - private static void assertCounters(DashDownloader dashDownloader, int totalSegments, - int downloadedSegments, int downloadedBytes) { + private static void assertCounters( + DashDownloader dashDownloader, + int totalSegments, + int downloadedSegments, + int downloadedBytes) { assertThat(dashDownloader.getTotalSegments()).isEqualTo(totalSegments); assertThat(dashDownloader.getDownloadedSegments()).isEqualTo(downloadedSegments); assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(downloadedBytes); } - } diff --git a/library/dash/src/test/resources/robolectric.properties b/library/dash/src/test/resources/robolectric.properties new file mode 100644 index 00000000000..2f3210368ec --- /dev/null +++ b/library/dash/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/library/hls/build.gradle b/library/hls/build.gradle index 5471eacec65..41e0b71da24 100644 --- a/library/hls/build.gradle +++ b/library/hls/build.gradle @@ -35,10 +35,7 @@ android { dependencies { compile project(modulePrefix + 'library-core') compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java deleted file mode 100644 index ec70fb12002..00000000000 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java +++ /dev/null @@ -1,77 +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.source.hls.offline; - -/** - * Data for HLS downloading tests. - */ -/* package */ interface HlsDownloadTestData { - - String MASTER_PLAYLIST_URI = "test.m3u8"; - - String MEDIA_PLAYLIST_0_DIR = "gear0/"; - String MEDIA_PLAYLIST_0_URI = MEDIA_PLAYLIST_0_DIR + "prog_index.m3u8"; - String MEDIA_PLAYLIST_1_DIR = "gear1/"; - String MEDIA_PLAYLIST_1_URI = MEDIA_PLAYLIST_1_DIR + "prog_index.m3u8"; - String MEDIA_PLAYLIST_2_DIR = "gear2/"; - String MEDIA_PLAYLIST_2_URI = MEDIA_PLAYLIST_2_DIR + "prog_index.m3u8"; - String MEDIA_PLAYLIST_3_DIR = "gear3/"; - String MEDIA_PLAYLIST_3_URI = MEDIA_PLAYLIST_3_DIR + "prog_index.m3u8"; - - byte[] MASTER_PLAYLIST_DATA = - ("#EXTM3U\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=232370,CODECS=\"mp4a.40.2, avc1.4d4015\"\n" - + MEDIA_PLAYLIST_1_URI + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=649879,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" - + MEDIA_PLAYLIST_2_URI + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=991714,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" - + MEDIA_PLAYLIST_3_URI + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=41457,CODECS=\"mp4a.40.2\"\n" - + MEDIA_PLAYLIST_0_URI).getBytes(); - - byte[] MEDIA_PLAYLIST_DATA = - ("#EXTM3U\n" - + "#EXT-X-TARGETDURATION:10\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-MEDIA-SEQUENCE:0\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXTINF:9.97667,\n" - + "fileSequence0.ts\n" - + "#EXTINF:9.97667,\n" - + "fileSequence1.ts\n" - + "#EXTINF:9.97667,\n" - + "fileSequence2.ts\n" - + "#EXT-X-ENDLIST").getBytes(); - - String ENC_MEDIA_PLAYLIST_URI = "enc_index.m3u8"; - - byte[] ENC_MEDIA_PLAYLIST_DATA = - ("#EXTM3U\n" - + "#EXT-X-TARGETDURATION:10\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-MEDIA-SEQUENCE:0\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc.key\"\n" - + "#EXTINF:9.97667,\n" - + "fileSequence0.ts\n" - + "#EXTINF:9.97667,\n" - + "fileSequence1.ts\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc2.key\"\n" - + "#EXTINF:9.97667,\n" - + "fileSequence2.ts\n" - + "#EXT-X-ENDLIST").getBytes(); - -} diff --git a/library/hls/src/androidTest/AndroidManifest.xml b/library/hls/src/test/AndroidManifest.xml similarity index 71% rename from library/hls/src/androidTest/AndroidManifest.xml rename to library/hls/src/test/AndroidManifest.xml index b1aadd203b1..331f3439adb 100644 --- a/library/hls/src/androidTest/AndroidManifest.xml +++ b/library/hls/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.source.hls.test"> - - - - - - - + diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java new file mode 100644 index 00000000000..55db28a59a7 --- /dev/null +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java @@ -0,0 +1,83 @@ +/* + * 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.source.hls.offline; + +import com.google.android.exoplayer2.C; +import java.nio.charset.Charset; + +/** Data for HLS downloading tests. */ +/* package */ interface HlsDownloadTestData { + + String MASTER_PLAYLIST_URI = "test.m3u8"; + + String MEDIA_PLAYLIST_0_DIR = "gear0/"; + String MEDIA_PLAYLIST_0_URI = MEDIA_PLAYLIST_0_DIR + "prog_index.m3u8"; + String MEDIA_PLAYLIST_1_DIR = "gear1/"; + String MEDIA_PLAYLIST_1_URI = MEDIA_PLAYLIST_1_DIR + "prog_index.m3u8"; + String MEDIA_PLAYLIST_2_DIR = "gear2/"; + String MEDIA_PLAYLIST_2_URI = MEDIA_PLAYLIST_2_DIR + "prog_index.m3u8"; + String MEDIA_PLAYLIST_3_DIR = "gear3/"; + String MEDIA_PLAYLIST_3_URI = MEDIA_PLAYLIST_3_DIR + "prog_index.m3u8"; + + byte[] MASTER_PLAYLIST_DATA = + ("#EXTM3U\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=232370,CODECS=\"mp4a.40.2, avc1.4d4015\"\n" + + MEDIA_PLAYLIST_1_URI + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=649879,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" + + MEDIA_PLAYLIST_2_URI + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=991714,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" + + MEDIA_PLAYLIST_3_URI + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=41457,CODECS=\"mp4a.40.2\"\n" + + MEDIA_PLAYLIST_0_URI) + .getBytes(Charset.forName(C.UTF8_NAME)); + + byte[] MEDIA_PLAYLIST_DATA = + ("#EXTM3U\n" + + "#EXT-X-TARGETDURATION:10\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXTINF:9.97667,\n" + + "fileSequence0.ts\n" + + "#EXTINF:9.97667,\n" + + "fileSequence1.ts\n" + + "#EXTINF:9.97667,\n" + + "fileSequence2.ts\n" + + "#EXT-X-ENDLIST") + .getBytes(Charset.forName(C.UTF8_NAME)); + + String ENC_MEDIA_PLAYLIST_URI = "enc_index.m3u8"; + + byte[] ENC_MEDIA_PLAYLIST_DATA = + ("#EXTM3U\n" + + "#EXT-X-TARGETDURATION:10\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc.key\"\n" + + "#EXTINF:9.97667,\n" + + "fileSequence0.ts\n" + + "#EXTINF:9.97667,\n" + + "fileSequence1.ts\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc2.key\"\n" + + "#EXTINF:9.97667,\n" + + "fileSequence2.ts\n" + + "#EXT-X-ENDLIST") + .getBytes(Charset.forName(C.UTF8_NAME)); +} diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java similarity index 78% rename from library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java rename to library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java index c068a8182bb..1e6b98092b5 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java @@ -33,7 +33,6 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; -import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.testutil.FakeDataSet; @@ -42,40 +41,47 @@ import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.util.Util; import java.io.File; +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; /** Unit tests for {@link HlsDownloader}. */ -public class HlsDownloaderTest extends InstrumentationTestCase { +@RunWith(RobolectricTestRunner.class) +public class HlsDownloaderTest { private SimpleCache cache; private File tempFolder; private FakeDataSet fakeDataSet; private HlsDownloader hlsDownloader; - @Override + @Before public void setUp() throws Exception { - super.setUp(); - tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest"); + tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest"); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); - fakeDataSet = new FakeDataSet() - .setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA) - .setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA) - .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10) - .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11) - .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12) - .setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA) - .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13) - .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14) - .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15); + fakeDataSet = + new FakeDataSet() + .setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA) + .setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA) + .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10) + .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11) + .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12) + .setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA) + .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13) + .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14) + .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15); hlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI); } - @Override + @After public void tearDown() throws Exception { Util.recursiveDelete(tempFolder); - super.tearDown(); } + @Test public void testDownloadManifest() throws Exception { HlsMasterPlaylist manifest = hlsDownloader.getManifest(); @@ -83,17 +89,23 @@ public void testDownloadManifest() throws Exception { assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI); } + @Test public void testSelectRepresentationsClearsPreviousSelection() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_2_URI}); hlsDownloader.download(null); - assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI, MEDIA_PLAYLIST_2_URI, + assertCachedData( + cache, + fakeDataSet, + MASTER_PLAYLIST_URI, + MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts"); } + @Test public void testCounterMethods() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); @@ -104,12 +116,12 @@ public void testCounterMethods() throws Exception { .isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12); } + @Test public void testInitStatus() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); - HlsDownloader newHlsDownloader = - getHlsDownloader(MASTER_PLAYLIST_URI); + HlsDownloader newHlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI); newHlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); newHlsDownloader.init(); @@ -119,16 +131,22 @@ public void testInitStatus() throws Exception { .isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12); } + @Test public void testDownloadRepresentation() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); - assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI, MEDIA_PLAYLIST_1_URI, + assertCachedData( + cache, + fakeDataSet, + MASTER_PLAYLIST_URI, + MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts"); } + @Test public void testDownloadMultipleRepresentations() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI}); hlsDownloader.download(null); @@ -136,9 +154,11 @@ public void testDownloadMultipleRepresentations() throws Exception { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadAllRepresentations() throws Exception { // Add data for the rest of the playlists - fakeDataSet.setData(MEDIA_PLAYLIST_0_URI, MEDIA_PLAYLIST_DATA) + fakeDataSet + .setData(MEDIA_PLAYLIST_0_URI, MEDIA_PLAYLIST_DATA) .setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence0.ts", 10) .setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence1.ts", 11) .setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence2.ts", 12) @@ -167,6 +187,7 @@ public void testDownloadAllRepresentations() throws Exception { hlsDownloader.remove(); } + @Test public void testRemoveAll() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI}); hlsDownloader.download(null); @@ -175,27 +196,32 @@ public void testRemoveAll() throws Exception { assertCacheEmpty(cache); } + @Test public void testDownloadMediaPlaylist() throws Exception { hlsDownloader = getHlsDownloader(MEDIA_PLAYLIST_1_URI); hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); - assertCachedData(cache, fakeDataSet, MEDIA_PLAYLIST_1_URI, + assertCachedData( + cache, + fakeDataSet, + MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts"); } + @Test public void testDownloadEncMediaPlaylist() throws Exception { - fakeDataSet = new FakeDataSet() - .setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA) - .setRandomData("enc.key", 8) - .setRandomData("enc2.key", 9) - .setRandomData("fileSequence0.ts", 10) - .setRandomData("fileSequence1.ts", 11) - .setRandomData("fileSequence2.ts", 12); - hlsDownloader = - getHlsDownloader(ENC_MEDIA_PLAYLIST_URI); + fakeDataSet = + new FakeDataSet() + .setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA) + .setRandomData("enc.key", 8) + .setRandomData("enc2.key", 9) + .setRandomData("fileSequence0.ts", 10) + .setRandomData("fileSequence1.ts", 11) + .setRandomData("fileSequence2.ts", 12); + hlsDownloader = getHlsDownloader(ENC_MEDIA_PLAYLIST_URI); hlsDownloader.selectRepresentations(new String[] {ENC_MEDIA_PLAYLIST_URI}); hlsDownloader.download(null); @@ -204,8 +230,7 @@ public void testDownloadEncMediaPlaylist() throws Exception { private HlsDownloader getHlsDownloader(String mediaPlaylistUri) { Factory factory = new Factory(null).setFakeDataSet(fakeDataSet); - return new HlsDownloader(Uri.parse(mediaPlaylistUri), - new DownloaderConstructorHelper(cache, factory)); + return new HlsDownloader( + Uri.parse(mediaPlaylistUri), new DownloaderConstructorHelper(cache, factory)); } - } diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java similarity index 59% rename from library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java rename to library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java index 2944d01b57e..86426e1f944 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.hls.playlist; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import android.net.Uri; import com.google.android.exoplayer2.C; @@ -26,70 +27,85 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.List; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Test for {@link HlsMasterPlaylistParserTest}. - */ -public class HlsMasterPlaylistParserTest extends TestCase { +/** Test for {@link HlsMasterPlaylistParserTest}. */ +@RunWith(RobolectricTestRunner.class) +public class HlsMasterPlaylistParserTest { private static final String PLAYLIST_URI = "https://example.com/test.m3u8"; - private static final String PLAYLIST_SIMPLE = " #EXTM3U \n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" - + "http://example.com/spaces_in_codecs.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n" - + "http://example.com/mid.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n" - + "http://example.com/hi.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" - + "http://example.com/audio-only.m3u8"; - - private static final String PLAYLIST_WITH_AVG_BANDWIDTH = " #EXTM3U \n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000," - + "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" - + "http://example.com/spaces_in_codecs.m3u8\n"; - - private static final String PLAYLIST_WITH_INVALID_HEADER = "#EXTMU3\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n"; - - private static final String PLAYLIST_WITH_CC = " #EXTM3U \n" - + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n"; - - private static final String PLAYLIST_WITHOUT_CC = " #EXTM3U \n" - + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128," - + "CLOSED-CAPTIONS=NONE\n" - + "http://example.com/low.m3u8\n"; - - private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG = "#EXTM3U\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n" - + "uri1.m3u8\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n" - + "uri2.m3u8\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n" - + "uri1.m3u8\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n" - + "uri2.m3u8\n" - + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\"," - + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n" - + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\"," - + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n"; - + private static final String PLAYLIST_SIMPLE = + " #EXTM3U \n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" + + "http://example.com/spaces_in_codecs.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n" + + "http://example.com/mid.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n" + + "http://example.com/hi.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" + + "http://example.com/audio-only.m3u8"; + + private static final String PLAYLIST_WITH_AVG_BANDWIDTH = + " #EXTM3U \n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000," + + "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" + + "http://example.com/spaces_in_codecs.m3u8\n"; + + private static final String PLAYLIST_WITH_INVALID_HEADER = + "#EXTMU3\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n"; + + private static final String PLAYLIST_WITH_CC = + " #EXTM3U \n" + + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS," + + "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n"; + + private static final String PLAYLIST_WITHOUT_CC = + " #EXTM3U \n" + + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS," + + "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128," + + "CLOSED-CAPTIONS=NONE\n" + + "http://example.com/low.m3u8\n"; + + private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG = + "#EXTM3U\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n" + + "uri1.m3u8\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n" + + "uri2.m3u8\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n" + + "uri1.m3u8\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n" + + "uri2.m3u8\n" + + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\"," + + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n" + + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\"," + + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n"; + + @Test public void testParseMasterPlaylist() throws IOException { HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE); @@ -129,9 +145,10 @@ public void testParseMasterPlaylist() throws IOException { assertThat(variants.get(4).url).isEqualTo("http://example.com/audio-only.m3u8"); } + @Test public void testMasterPlaylistWithBandwdithAverage() throws IOException { - HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, - PLAYLIST_WITH_AVG_BANDWIDTH); + HlsMasterPlaylist masterPlaylist = + parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH); List variants = masterPlaylist.variants; @@ -139,6 +156,7 @@ public void testMasterPlaylistWithBandwdithAverage() throws IOException { assertThat(variants.get(1).format.bitrate).isEqualTo(1270000); } + @Test public void testPlaylistWithInvalidHeader() throws IOException { try { parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER); @@ -148,6 +166,7 @@ public void testPlaylistWithInvalidHeader() throws IOException { } } + @Test public void testPlaylistWithClosedCaption() throws IOException { HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC); assertThat(playlist.muxedCaptionFormats).hasSize(1); @@ -157,11 +176,13 @@ public void testPlaylistWithClosedCaption() throws IOException { assertThat(closedCaptionFormat.language).isEqualTo("es"); } + @Test public void testPlaylistWithoutClosedCaptions() throws IOException { HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITHOUT_CC); assertThat(playlist.muxedCaptionFormats).isEmpty(); } + @Test public void testCodecPropagation() throws IOException { HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AUDIO_MEDIA_TAG); @@ -177,9 +198,8 @@ public void testCodecPropagation() throws IOException { private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString) throws IOException { Uri playlistUri = Uri.parse(uri); - ByteArrayInputStream inputStream = new ByteArrayInputStream( - playlistString.getBytes(Charset.forName(C.UTF8_NAME))); + ByteArrayInputStream inputStream = + new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); } - } diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java similarity index 76% rename from library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java rename to library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java index 97a5386b041..b10997cfe96 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java @@ -26,49 +26,53 @@ import java.nio.charset.Charset; import java.util.List; import java.util.Locale; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Test for {@link HlsMediaPlaylistParserTest}. - */ -public class HlsMediaPlaylistParserTest extends TestCase { +/** Test for {@link HlsMediaPlaylistParserTest}. */ +@RunWith(RobolectricTestRunner.class) +public class HlsMediaPlaylistParserTest { - public void testParseMediaPlaylist() throws IOException { + @Test + public void testParseMediaPlaylist() throws Exception { Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); - String playlistString = "#EXTM3U\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXT-X-START:TIME-OFFSET=-25" - + "#EXT-X-TARGETDURATION:8\n" - + "#EXT-X-MEDIA-SEQUENCE:2679\n" - + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" - + "#EXT-X-ALLOW-CACHE:YES\n" - + "\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51370@0\n" - + "https://priv.example.com/fileSequence2679.ts\n" - + "\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51501@2147483648\n" - + "https://priv.example.com/fileSequence2680.ts\n" - + "\n" - + "#EXT-X-KEY:METHOD=NONE\n" - + "#EXTINF:7.941,\n" - + "#EXT-X-BYTERANGE:51501\n" // @2147535149 - + "https://priv.example.com/fileSequence2681.ts\n" - + "\n" - + "#EXT-X-DISCONTINUITY\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51740\n" // @2147586650 - + "https://priv.example.com/fileSequence2682.ts\n" - + "\n" - + "#EXTINF:7.975,\n" - + "https://priv.example.com/fileSequence2683.ts\n" - + "#EXT-X-ENDLIST"; - InputStream inputStream = new ByteArrayInputStream( - playlistString.getBytes(Charset.forName(C.UTF8_NAME))); + String playlistString = + "#EXTM3U\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXT-X-START:TIME-OFFSET=-25" + + "#EXT-X-TARGETDURATION:8\n" + + "#EXT-X-MEDIA-SEQUENCE:2679\n" + + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" + + "#EXT-X-ALLOW-CACHE:YES\n" + + "\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51370@0\n" + + "https://priv.example.com/fileSequence2679.ts\n" + + "\n" + + "#EXT-X-KEY:METHOD=AES-128," + + "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51501@2147483648\n" + + "https://priv.example.com/fileSequence2680.ts\n" + + "\n" + + "#EXT-X-KEY:METHOD=NONE\n" + + "#EXTINF:7.941,\n" + + "#EXT-X-BYTERANGE:51501\n" // @2147535149 + + "https://priv.example.com/fileSequence2681.ts\n" + + "\n" + + "#EXT-X-DISCONTINUITY\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51740\n" // @2147586650 + + "https://priv.example.com/fileSequence2682.ts\n" + + "\n" + + "#EXTINF:7.975,\n" + + "https://priv.example.com/fileSequence2683.ts\n" + + "#EXT-X-ENDLIST"; + InputStream inputStream = + new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream); HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist; @@ -136,6 +140,7 @@ public void testParseMediaPlaylist() throws IOException { assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts"); } + @Test public void testGapTag() throws IOException { Uri playlistUri = Uri.parse("https://example.com/test2.m3u8"); String playlistString = @@ -170,5 +175,4 @@ public void testGapTag() throws IOException { assertThat(playlist.segments.get(2).hasGapTag).isTrue(); assertThat(playlist.segments.get(3).hasGapTag).isFalse(); } - } diff --git a/library/hls/src/test/resources/robolectric.properties b/library/hls/src/test/resources/robolectric.properties new file mode 100644 index 00000000000..2f3210368ec --- /dev/null +++ b/library/hls/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/library/smoothstreaming/build.gradle b/library/smoothstreaming/build.gradle index ee5a8c4e739..b85f25e6564 100644 --- a/library/smoothstreaming/build.gradle +++ b/library/smoothstreaming/build.gradle @@ -35,10 +35,7 @@ android { dependencies { compile project(modulePrefix + 'library-core') compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/smoothstreaming/src/androidTest/AndroidManifest.xml b/library/smoothstreaming/src/test/AndroidManifest.xml similarity index 70% rename from library/smoothstreaming/src/androidTest/AndroidManifest.xml rename to library/smoothstreaming/src/test/AndroidManifest.xml index 8e1e69509d7..1a8f8ee9c40 100644 --- a/library/smoothstreaming/src/androidTest/AndroidManifest.xml +++ b/library/smoothstreaming/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.source.smoothstreaming.test"> - - - - - - - + diff --git a/library/smoothstreaming/src/androidTest/assets/sample_ismc_1 b/library/smoothstreaming/src/test/assets/sample_ismc_1 similarity index 100% rename from library/smoothstreaming/src/androidTest/assets/sample_ismc_1 rename to library/smoothstreaming/src/test/assets/sample_ismc_1 diff --git a/library/smoothstreaming/src/androidTest/assets/sample_ismc_2 b/library/smoothstreaming/src/test/assets/sample_ismc_2 similarity index 100% rename from library/smoothstreaming/src/androidTest/assets/sample_ismc_2 rename to library/smoothstreaming/src/test/assets/sample_ismc_2 diff --git a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java similarity index 60% rename from library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java rename to library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java index 4663f014ff6..2ce9fec970a 100644 --- a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java @@ -16,27 +16,29 @@ package com.google.android.exoplayer2.source.smoothstreaming.manifest; import android.net.Uri; -import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link SsManifestParser}. - */ -public final class SsManifestParserTest extends InstrumentationTestCase { +/** Unit tests for {@link SsManifestParser}. */ +@RunWith(RobolectricTestRunner.class) +public final class SsManifestParserTest { private static final String SAMPLE_ISMC_1 = "sample_ismc_1"; private static final String SAMPLE_ISMC_2 = "sample_ismc_2"; - /** - * Simple test to ensure the sample manifests parse without any exceptions being thrown. - */ + /** Simple test to ensure the sample manifests parse without any exceptions being thrown. */ + @Test public void testParseSmoothStreamingManifest() throws IOException { SsManifestParser parser = new SsManifestParser(); - parser.parse(Uri.parse("https://example.com/test.ismc"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_1)); - parser.parse(Uri.parse("https://example.com/test.ismc"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_2)); + parser.parse( + Uri.parse("https://example.com/test.ismc"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_1)); + parser.parse( + Uri.parse("https://example.com/test.ismc"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_2)); } - } diff --git a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java similarity index 77% rename from library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java rename to library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java index ecf9e77d28c..fbb2c3d4c4a 100644 --- a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java @@ -26,52 +26,49 @@ import java.util.Collections; import java.util.List; import java.util.Random; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link SsManifest}. - */ -public class SsManifestTest extends TestCase { +/** Unit tests for {@link SsManifest}. */ +@RunWith(RobolectricTestRunner.class) +public class SsManifestTest { private static final ProtectionElement DUMMY_PROTECTION_ELEMENT = new ProtectionElement(C.WIDEVINE_UUID, new byte[] {0, 1, 2}); + @Test public void testCopy() throws Exception { Format[][] formats = newFormats(2, 3); - SsManifest sourceManifest = newSsManifest( - newStreamElement("1",formats[0]), - newStreamElement("2", formats[1])); - - List keys = Arrays.asList( - new TrackKey(0, 0), - new TrackKey(0, 2), - new TrackKey(1, 0)); + SsManifest sourceManifest = + newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1])); + + List keys = Arrays.asList(new TrackKey(0, 0), new TrackKey(0, 2), new TrackKey(1, 0)); // Keys don't need to be in any particular order Collections.shuffle(keys, new Random(0)); SsManifest copyManifest = sourceManifest.copy(keys); - SsManifest expectedManifest = newSsManifest( - newStreamElement("1", formats[0][0], formats[0][2]), - newStreamElement("2", formats[1][0])); + SsManifest expectedManifest = + newSsManifest( + newStreamElement("1", formats[0][0], formats[0][2]), + newStreamElement("2", formats[1][0])); assertManifestEquals(expectedManifest, copyManifest); } + @Test public void testCopyRemoveStreamElement() throws Exception { Format[][] formats = newFormats(2, 3); - SsManifest sourceManifest = newSsManifest( - newStreamElement("1", formats[0]), - newStreamElement("2", formats[1])); + SsManifest sourceManifest = + newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1])); - List keys = Arrays.asList( - new TrackKey(1, 0)); + List keys = Arrays.asList(new TrackKey(1, 0)); // Keys don't need to be in any particular order Collections.shuffle(keys, new Random(0)); SsManifest copyManifest = sourceManifest.copy(keys); - SsManifest expectedManifest = newSsManifest( - newStreamElement("2", formats[1][0])); + SsManifest expectedManifest = newSsManifest(newStreamElement("2", formats[1][0])); assertManifestEquals(expectedManifest, copyManifest); } @@ -117,13 +114,25 @@ private static SsManifest newSsManifest(StreamElement... streamElements) { } private static StreamElement newStreamElement(String name, Format... formats) { - return new StreamElement("baseUri", "chunkTemplate", C.TRACK_TYPE_VIDEO, "subType", - 1000, name, 1024, 768, 1024, 768, null, formats, Collections.emptyList(), 0); + return new StreamElement( + "baseUri", + "chunkTemplate", + C.TRACK_TYPE_VIDEO, + "subType", + 1000, + name, + 1024, + 768, + 1024, + 768, + null, + formats, + Collections.emptyList(), + 0); } private static Format newFormat(String id) { - return Format.createContainerFormat(id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, - Format.NO_VALUE, 0, null); + return Format.createContainerFormat( + id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, Format.NO_VALUE, 0, null); } - } diff --git a/library/smoothstreaming/src/test/resources/robolectric.properties b/library/smoothstreaming/src/test/resources/robolectric.properties new file mode 100644 index 00000000000..2f3210368ec --- /dev/null +++ b/library/smoothstreaming/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/testutils/build.gradle b/testutils/build.gradle index 7c4b163713f..11ec55c0473 100644 --- a/testutils/build.gradle +++ b/testutils/build.gradle @@ -35,4 +35,5 @@ dependencies { compile project(modulePrefix + 'library-core') compile 'org.mockito:mockito-core:' + mockitoVersion compile 'com.google.truth:truth:' + truthVersion + testCompile project(modulePrefix + 'testutils-robolectric') } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java deleted file mode 100644 index 6bd1048bc00..00000000000 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java +++ /dev/null @@ -1,54 +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.testutil; - -import android.content.Context; -import android.test.InstrumentationTestCase; -import org.mockito.MockitoAnnotations; - -/** - * Utility for setting up Mockito for instrumentation tests. - */ -public final class MockitoUtil { - - /** - * Sets up Mockito for an instrumentation test. - * - * @param instrumentationTestCase The instrumentation test case class. - */ - public static void setUpMockito(InstrumentationTestCase instrumentationTestCase) { - // Workaround for https://code.google.com/p/dexmaker/issues/detail?id=2. - System.setProperty("dexmaker.dexcache", - instrumentationTestCase.getInstrumentation().getTargetContext().getCacheDir().getPath()); - MockitoAnnotations.initMocks(instrumentationTestCase); - } - - /** - * Sets up Mockito for a JUnit4 test. - * - * @param targetContext The target context. Usually obtained from - * {@code InstrumentationRegistry.getTargetContext()} - * @param testClass The JUnit4 test class. - */ - public static void setUpMockito(Context targetContext, Object testClass) { - // Workaround for https://code.google.com/p/dexmaker/issues/detail?id=2. - System.setProperty("dexmaker.dexcache", targetContext.getCacheDir().getPath()); - MockitoAnnotations.initMocks(testClass); - } - - private MockitoUtil() {} - -} diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java deleted file mode 100644 index 7cae7094385..00000000000 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java +++ /dev/null @@ -1,1073 +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.testutil; - -/** - * Provides ogg/vorbis test data in bytes for unit tests. - */ -public 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); - } - - /** - * Returns the initial two pages of bytes which by spec contain the three vorbis header packets: - * identification, comment and setup header. - */ - public static byte[] getVorbisHeaderPages() { - byte[] data = new byte[VORBIS_HEADER_PAGES.length]; - System.arraycopy(VORBIS_HEADER_PAGES, 0, data, 0, - VORBIS_HEADER_PAGES.length); - return data; - } - - /** - * Returns a valid vorbis identification header in bytes. - */ - public static byte[] getIdentificationHeaderData() { - int idHeaderStart = 28; - int idHeaderLength = 30; - byte[] idHeaderData = new byte[idHeaderLength]; - System.arraycopy(VORBIS_HEADER_PAGES, idHeaderStart, idHeaderData, 0, idHeaderLength); - return idHeaderData; - } - - /** - * Returns a valid vorbis comment header with 3 comments including utf8 chars in bytes. - */ - public static byte[] getCommentHeaderDataUTF8() { - byte[] commentHeaderData = new byte[COMMENT_HEADER_WITH_UTF8.length]; - System.arraycopy(COMMENT_HEADER_WITH_UTF8, 0, commentHeaderData, 0, - COMMENT_HEADER_WITH_UTF8.length); - return commentHeaderData; - } - - /** - * Returns a valid vorbis setup header in bytes. - */ - public static byte[] getSetupHeaderData() { - int setupHeaderStart = 146; - int setupHeaderLength = VORBIS_HEADER_PAGES.length - setupHeaderStart; - byte[] setupHeaderData = new byte[setupHeaderLength]; - System.arraycopy(VORBIS_HEADER_PAGES, setupHeaderStart, setupHeaderData, 0, setupHeaderLength); - return setupHeaderData; - } - - private static final byte[] COMMENT_HEADER_WITH_UTF8 = { - (byte) 0x03, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 3, v, o, r, - (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x2b, // b, i, s, . - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x58, - (byte) 0x69, (byte) 0x70, (byte) 0x68, (byte) 0x2e, - (byte) 0x4f, (byte) 0x72, (byte) 0x67, (byte) 0x20, - (byte) 0x6c, (byte) 0x69, (byte) 0x62, (byte) 0x56, - (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, - (byte) 0x73, (byte) 0x20, (byte) 0x49, (byte) 0x20, - (byte) 0x32, (byte) 0x30, (byte) 0x31, (byte) 0x32, - (byte) 0x30, (byte) 0x32, (byte) 0x30, (byte) 0x33, - (byte) 0x20, (byte) 0x28, (byte) 0x4f, (byte) 0x6d, - (byte) 0x6e, (byte) 0x69, (byte) 0x70, (byte) 0x72, - (byte) 0x65, (byte) 0x73, (byte) 0x65, (byte) 0x6e, - (byte) 0x74, (byte) 0x29, (byte) 0x03, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x4c, - (byte) 0x42, (byte) 0x55, (byte) 0x4d, (byte) 0x3d, - (byte) 0xc3, (byte) 0xa4, (byte) 0xc3, (byte) 0xb6, - (byte) 0x13, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x54, (byte) 0x49, (byte) 0x54, (byte) 0x4c, - (byte) 0x45, (byte) 0x3d, (byte) 0x41, (byte) 0x20, - (byte) 0x73, (byte) 0x61, (byte) 0x6d, (byte) 0x70, - (byte) 0x6c, (byte) 0x65, (byte) 0x20, (byte) 0x73, - (byte) 0x6f, (byte) 0x6e, (byte) 0x67, (byte) 0x0d, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x41, - (byte) 0x52, (byte) 0x54, (byte) 0x49, (byte) 0x53, - (byte) 0x54, (byte) 0x3d, (byte) 0x47, (byte) 0x6f, - (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, - (byte) 0x01 - }; - - // two OGG pages with 3 packets (id, comment and setup header) - // length: 3743 bytes - private static final byte[] VORBIS_HEADER_PAGES = { /* capture pattern ogg header 1 */ - (byte) 0x4f, (byte) 0x67, (byte) 0x67, (byte) 0x53, // O,g,g,S : start pos 0 - (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x5e, (byte) 0x5f, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x83, (byte) 0x36, - (byte) 0xe3, (byte) 0x49, (byte) 0x01, (byte) 0x1e, /* capture pattern vorbis id header */ - (byte) 0x01, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 1,v,o,r : start pos 28 - (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x00, // b,i,s,. - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, - (byte) 0x22, (byte) 0x56, (byte) 0x00, (byte) 0x00, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0x6a, (byte) 0x04, (byte) 0x01, (byte) 0x00, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern ogg header 2 */ - (byte) 0xa9, (byte) 0x01, (byte) 0x4f, (byte) 0x67, // .,.,O,g : start pos 86 - (byte) 0x67, (byte) 0x53, (byte) 0x00, (byte) 0x00, // g,S,.,. - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x5e, (byte) 0x5f, (byte) 0x00, (byte) 0x00, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x69, (byte) 0xf8, (byte) 0xeb, (byte) 0xe1, - (byte) 0x10, (byte) 0x2d, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern vorbis comment header*/ - (byte) 0x1b, (byte) 0x03, (byte) 0x76, (byte) 0x6f, // .,3,v,o : start pos 101 - (byte) 0x72, (byte) 0x62, (byte) 0x69, (byte) 0x73, // r,b,i,s - (byte) 0x1d, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x58, (byte) 0x69, (byte) 0x70, (byte) 0x68, - (byte) 0x2e, (byte) 0x4f, (byte) 0x72, (byte) 0x67, - (byte) 0x20, (byte) 0x6c, (byte) 0x69, (byte) 0x62, - (byte) 0x56, (byte) 0x6f, (byte) 0x72, (byte) 0x62, - (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x49, - (byte) 0x20, (byte) 0x32, (byte) 0x30, (byte) 0x30, - (byte) 0x33, (byte) 0x30, (byte) 0x39, (byte) 0x30, - (byte) 0x39, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* capture pattern vorbis setup header */ - (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x76, // .,.,5,v : start pos 146 - (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, // o,r,b,i - (byte) 0x73, (byte) 0x22, (byte) 0x42, (byte) 0x43, // s,. - (byte) 0x56, (byte) 0x01, (byte) 0x00, (byte) 0x40, - (byte) 0x00, (byte) 0x00, (byte) 0x18, (byte) 0x42, - (byte) 0x10, (byte) 0x2a, (byte) 0x05, (byte) 0xad, - (byte) 0x63, (byte) 0x8e, (byte) 0x3a, (byte) 0xc8, - (byte) 0x15, (byte) 0x21, (byte) 0x8c, (byte) 0x19, - (byte) 0xa2, (byte) 0xa0, (byte) 0x42, (byte) 0xca, - (byte) 0x29, (byte) 0xc7, (byte) 0x1d, (byte) 0x42, - (byte) 0xd0, (byte) 0x21, (byte) 0xa3, (byte) 0x24, - (byte) 0x43, (byte) 0x88, (byte) 0x3a, (byte) 0xc6, - (byte) 0x35, (byte) 0xc7, (byte) 0x18, (byte) 0x63, - (byte) 0x47, (byte) 0xb9, (byte) 0x64, (byte) 0x8a, - (byte) 0x42, (byte) 0xc9, (byte) 0x81, (byte) 0xd0, - (byte) 0x90, (byte) 0x55, (byte) 0x00, (byte) 0x00, - (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0xa4, - (byte) 0x1c, (byte) 0x57, (byte) 0x50, (byte) 0x72, - (byte) 0x49, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xa3, (byte) 0x18, (byte) 0x57, - (byte) 0xcc, (byte) 0x71, (byte) 0xe8, (byte) 0x20, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe5, - (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, - (byte) 0x09, (byte) 0x25, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x8e, (byte) 0x39, (byte) 0xe7, - (byte) 0x92, (byte) 0x72, (byte) 0x8e, (byte) 0x31, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa3, - (byte) 0x18, (byte) 0x57, (byte) 0x0e, (byte) 0x72, - (byte) 0x29, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x81, (byte) 0x14, (byte) 0x47, - (byte) 0x8a, (byte) 0x71, (byte) 0xa7, (byte) 0x18, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa4, - (byte) 0x1c, (byte) 0x47, (byte) 0x8a, (byte) 0x71, - (byte) 0xa8, (byte) 0x18, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x6d, (byte) 0x31, (byte) 0xb7, - (byte) 0x92, (byte) 0x72, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe6, - (byte) 0x20, (byte) 0x87, (byte) 0x52, (byte) 0x72, - (byte) 0xae, (byte) 0x35, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xa4, (byte) 0x18, (byte) 0x67, - (byte) 0x0e, (byte) 0x72, (byte) 0x0b, (byte) 0x25, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xc6, - (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, - (byte) 0xeb, (byte) 0x20, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x8c, (byte) 0x35, (byte) 0xb7, - (byte) 0xd4, (byte) 0x72, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x8c, (byte) 0x31, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x6e, - (byte) 0x31, (byte) 0xe7, (byte) 0x16, (byte) 0x73, - (byte) 0xae, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x1c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x20, - (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, - (byte) 0x90, (byte) 0x00, (byte) 0x00, (byte) 0xa0, - (byte) 0xa1, (byte) 0x28, (byte) 0x8a, (byte) 0xe2, - (byte) 0x28, (byte) 0x0e, (byte) 0x10, (byte) 0x1a, - (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0xc8, - (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x40, - (byte) 0x71, (byte) 0x14, (byte) 0x47, (byte) 0x91, - (byte) 0x14, (byte) 0x4b, (byte) 0xb1, (byte) 0x1c, - (byte) 0xcb, (byte) 0xd1, (byte) 0x24, (byte) 0x0d, - (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, - (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, - (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0xa0, - (byte) 0x48, (byte) 0x86, (byte) 0xa4, (byte) 0x48, - (byte) 0x8a, (byte) 0xa5, (byte) 0x58, (byte) 0x8e, - (byte) 0x66, (byte) 0x69, (byte) 0x9e, (byte) 0x26, - (byte) 0x7a, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, - (byte) 0xa2, (byte) 0x2a, (byte) 0xab, (byte) 0xb2, - (byte) 0x69, (byte) 0xca, (byte) 0xb2, (byte) 0x2c, - (byte) 0xcb, (byte) 0xb2, (byte) 0xeb, (byte) 0xba, - (byte) 0x2e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, - (byte) 0x0a, (byte) 0x00, (byte) 0x48, (byte) 0x00, - (byte) 0x00, (byte) 0x50, (byte) 0x51, (byte) 0x14, - (byte) 0xc5, (byte) 0x70, (byte) 0x14, (byte) 0x07, - (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, - (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x00, - (byte) 0x08, (byte) 0x60, (byte) 0x28, (byte) 0x8a, - (byte) 0xa3, (byte) 0x38, (byte) 0x8e, (byte) 0xe4, - (byte) 0x58, (byte) 0x92, (byte) 0xa5, (byte) 0x59, - (byte) 0x9e, (byte) 0x07, (byte) 0x84, (byte) 0x86, - (byte) 0xac, (byte) 0x02, (byte) 0x00, (byte) 0x80, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x00, - (byte) 0x00, (byte) 0x50, (byte) 0x0c, (byte) 0x47, - (byte) 0xb1, (byte) 0x14, (byte) 0x4d, (byte) 0xf1, - (byte) 0x24, (byte) 0xcf, (byte) 0xf2, (byte) 0x3c, - (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, - (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, - (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, - (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, - (byte) 0xf3, (byte) 0x3c, (byte) 0x0d, (byte) 0x08, - (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, - (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x82, (byte) 0x28, (byte) 0x64, (byte) 0x18, - (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, - (byte) 0x01, (byte) 0x00, (byte) 0x40, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0x21, (byte) 0x1a, - (byte) 0x19, (byte) 0x43, (byte) 0x9d, (byte) 0x52, - (byte) 0x12, (byte) 0x5c, (byte) 0x0a, (byte) 0x16, - (byte) 0x42, (byte) 0x1c, (byte) 0x11, (byte) 0x43, - (byte) 0x1d, (byte) 0x42, (byte) 0xce, (byte) 0x43, - (byte) 0xa9, (byte) 0xa5, (byte) 0x83, (byte) 0xe0, - (byte) 0x29, (byte) 0x85, (byte) 0x25, (byte) 0x63, - (byte) 0xd2, (byte) 0x53, (byte) 0xac, (byte) 0x41, - (byte) 0x08, (byte) 0x21, (byte) 0x7c, (byte) 0xef, - (byte) 0x3d, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, - (byte) 0xef, (byte) 0x81, (byte) 0xd0, (byte) 0x90, - (byte) 0x55, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x00, (byte) 0x00, (byte) 0x61, (byte) 0x14, - (byte) 0x38, (byte) 0x88, (byte) 0x81, (byte) 0xc7, - (byte) 0x24, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x62, (byte) 0x14, (byte) 0x27, (byte) 0x44, - (byte) 0x71, (byte) 0xa6, (byte) 0x20, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0xe5, (byte) 0x24, - (byte) 0x58, (byte) 0xca, (byte) 0x79, (byte) 0xe8, - (byte) 0x24, (byte) 0x08, (byte) 0xdd, (byte) 0x83, - (byte) 0x10, (byte) 0x42, (byte) 0xb8, (byte) 0x9c, - (byte) 0x7b, (byte) 0xcb, (byte) 0xb9, (byte) 0xf7, - (byte) 0xde, (byte) 0x7b, (byte) 0x20, (byte) 0x34, - (byte) 0x64, (byte) 0x15, (byte) 0x00, (byte) 0x00, - (byte) 0x08, (byte) 0x00, (byte) 0xc0, (byte) 0x20, - (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, - (byte) 0x08, (byte) 0x29, (byte) 0xa4, (byte) 0x94, - (byte) 0x52, (byte) 0x48, (byte) 0x29, (byte) 0xa6, - (byte) 0x98, (byte) 0x62, (byte) 0x8a, (byte) 0x29, - (byte) 0xc7, (byte) 0x1c, (byte) 0x73, (byte) 0xcc, - (byte) 0x31, (byte) 0xc7, (byte) 0x20, (byte) 0x83, - (byte) 0x0c, (byte) 0x32, (byte) 0xe8, (byte) 0xa0, - (byte) 0x93, (byte) 0x4e, (byte) 0x3a, (byte) 0xc9, - (byte) 0xa4, (byte) 0x92, (byte) 0x4e, (byte) 0x3a, - (byte) 0xca, (byte) 0x24, (byte) 0xa3, (byte) 0x8e, - (byte) 0x52, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, - (byte) 0x14, (byte) 0x53, (byte) 0x4c, (byte) 0xb1, - (byte) 0xe5, (byte) 0x16, (byte) 0x63, (byte) 0xad, - (byte) 0xb5, (byte) 0xd6, (byte) 0x9c, (byte) 0x73, - (byte) 0xaf, (byte) 0x41, (byte) 0x29, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x23, (byte) 0x08, - (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, - (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x06, (byte) 0x19, (byte) 0x64, (byte) 0x90, - (byte) 0x41, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x14, (byte) 0x52, (byte) 0x48, (byte) 0x29, - (byte) 0xa6, (byte) 0x98, (byte) 0x72, (byte) 0xcc, - (byte) 0x31, (byte) 0xc7, (byte) 0x1c, (byte) 0x03, - (byte) 0x42, (byte) 0x43, (byte) 0x56, (byte) 0x01, - (byte) 0x00, (byte) 0x80, (byte) 0x00, (byte) 0x00, - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x1c, (byte) 0x45, (byte) 0x52, (byte) 0x24, - (byte) 0x47, (byte) 0x72, (byte) 0x24, (byte) 0x47, - (byte) 0x92, (byte) 0x24, (byte) 0xc9, (byte) 0x92, - (byte) 0x2c, (byte) 0x49, (byte) 0x93, (byte) 0x3c, - (byte) 0xcb, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, - (byte) 0xb3, (byte) 0x3c, (byte) 0x4d, (byte) 0xd4, - (byte) 0x44, (byte) 0x4d, (byte) 0x15, (byte) 0x55, - (byte) 0xd5, (byte) 0x55, (byte) 0x6d, (byte) 0xd7, - (byte) 0xf6, (byte) 0x6d, (byte) 0x5f, (byte) 0xf6, - (byte) 0x6d, (byte) 0xdf, (byte) 0xd5, (byte) 0x65, - (byte) 0xdf, (byte) 0xf6, (byte) 0x65, (byte) 0xdb, - (byte) 0xd5, (byte) 0x65, (byte) 0x5d, (byte) 0x96, - (byte) 0x65, (byte) 0xdd, (byte) 0xb5, (byte) 0x6d, - (byte) 0x5d, (byte) 0xd6, (byte) 0x5d, (byte) 0x5d, - (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, - (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x75, - (byte) 0x5d, (byte) 0xd7, (byte) 0x75, (byte) 0x5d, - (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, - (byte) 0x81, (byte) 0xd0, (byte) 0x90, (byte) 0x55, - (byte) 0x00, (byte) 0x80, (byte) 0x04, (byte) 0x00, - (byte) 0x80, (byte) 0x8e, (byte) 0xe4, (byte) 0x38, - (byte) 0x8e, (byte) 0xe4, (byte) 0x38, (byte) 0x8e, - (byte) 0xe4, (byte) 0x48, (byte) 0x8e, (byte) 0xa4, - (byte) 0x48, (byte) 0x0a, (byte) 0x10, (byte) 0x1a, - (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0x90, - (byte) 0x01, (byte) 0x00, (byte) 0x10, (byte) 0x00, - (byte) 0x80, (byte) 0xa3, (byte) 0x38, (byte) 0x8a, - (byte) 0xe3, (byte) 0x48, (byte) 0x8e, (byte) 0xe4, - (byte) 0x58, (byte) 0x8e, (byte) 0x25, (byte) 0x59, - (byte) 0x92, (byte) 0x26, (byte) 0x69, (byte) 0x96, - (byte) 0x67, (byte) 0x79, (byte) 0x96, (byte) 0xa7, - (byte) 0x79, (byte) 0x9a, (byte) 0xa8, (byte) 0x89, - (byte) 0x1e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, - (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x04, - (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, - (byte) 0xa2, (byte) 0x28, (byte) 0x8a, (byte) 0xa3, - (byte) 0x38, (byte) 0x8e, (byte) 0x24, (byte) 0x59, - (byte) 0x96, (byte) 0xa6, (byte) 0x69, (byte) 0x9e, - (byte) 0xa7, (byte) 0x7a, (byte) 0xa2, (byte) 0x28, - (byte) 0x9a, (byte) 0xaa, (byte) 0xaa, (byte) 0x8a, - (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, (byte) 0xaa, - (byte) 0x6a, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, - (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, - (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0x02, (byte) 0xa1, - (byte) 0x21, (byte) 0xab, (byte) 0x00, (byte) 0x00, - (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x1d, - (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0x47, - (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, - (byte) 0x24, (byte) 0x47, (byte) 0x92, (byte) 0x24, - (byte) 0x20, (byte) 0x34, (byte) 0x64, (byte) 0x15, - (byte) 0x00, (byte) 0x20, (byte) 0x03, (byte) 0x00, - (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x43, - (byte) 0x51, (byte) 0x1c, (byte) 0x45, (byte) 0x72, - (byte) 0x2c, (byte) 0xc7, (byte) 0x92, (byte) 0x34, - (byte) 0x4b, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, - (byte) 0xd3, (byte) 0x44, (byte) 0xcf, (byte) 0xf4, - (byte) 0x5c, (byte) 0x51, (byte) 0x36, (byte) 0x75, - (byte) 0x53, (byte) 0x57, (byte) 0x6d, (byte) 0x20, - (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x20, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0xc7, (byte) 0x73, - (byte) 0x3c, (byte) 0xc7, (byte) 0x73, (byte) 0x3c, - (byte) 0xc9, (byte) 0x93, (byte) 0x3c, (byte) 0xcb, - (byte) 0x73, (byte) 0x3c, (byte) 0xc7, (byte) 0x93, - (byte) 0x3c, (byte) 0x49, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, - (byte) 0x02, (byte) 0x00, (byte) 0x64, (byte) 0x00, - (byte) 0x00, (byte) 0x90, (byte) 0x02, (byte) 0xcf, - (byte) 0x42, (byte) 0x29, (byte) 0x2d, (byte) 0x46, - (byte) 0x02, (byte) 0x1c, (byte) 0x88, (byte) 0x98, - (byte) 0xa3, (byte) 0xd8, (byte) 0x7b, (byte) 0xef, - (byte) 0xbd, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, - (byte) 0x65, (byte) 0x3c, (byte) 0x92, (byte) 0x88, - (byte) 0x49, (byte) 0xed, (byte) 0x31, (byte) 0xf4, - (byte) 0xd4, (byte) 0x31, (byte) 0x07, (byte) 0xb1, - (byte) 0x67, (byte) 0xc6, (byte) 0x23, (byte) 0x66, - (byte) 0x94, (byte) 0xa3, (byte) 0xd8, (byte) 0x29, - (byte) 0xcf, (byte) 0x1c, (byte) 0x42, (byte) 0x0c, - (byte) 0x62, (byte) 0xe8, (byte) 0x3c, (byte) 0x74, - (byte) 0x4a, (byte) 0x31, (byte) 0x88, (byte) 0x29, - (byte) 0xf5, (byte) 0x52, (byte) 0x32, (byte) 0xc6, - (byte) 0x20, (byte) 0xc6, (byte) 0xd8, (byte) 0x63, - (byte) 0x0c, (byte) 0x21, (byte) 0x94, (byte) 0x18, - (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x21, - (byte) 0x00, (byte) 0x84, (byte) 0x66, (byte) 0x00, - (byte) 0x18, (byte) 0x24, (byte) 0x09, (byte) 0x90, - (byte) 0x34, (byte) 0x0d, (byte) 0x90, (byte) 0x34, - (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x24, (byte) 0x4f, (byte) 0x03, (byte) 0x34, - (byte) 0x51, (byte) 0x04, (byte) 0x34, (byte) 0x4f, - (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x49, (byte) 0xf3, (byte) 0x00, (byte) 0x4d, - (byte) 0xf4, (byte) 0x00, (byte) 0x4d, (byte) 0x14, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x90, (byte) 0x3c, (byte) 0x0d, (byte) 0xf0, - (byte) 0x44, (byte) 0x11, (byte) 0xd0, (byte) 0x44, - (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, - (byte) 0x51, (byte) 0x05, (byte) 0x44, (byte) 0xd5, - (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0x4f, - (byte) 0x15, (byte) 0x01, (byte) 0xd1, (byte) 0x54, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x90, (byte) 0x34, (byte) 0x0f, (byte) 0xd0, - (byte) 0x44, (byte) 0x11, (byte) 0xf0, (byte) 0x44, - (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, - (byte) 0xd5, (byte) 0x04, (byte) 0x3c, (byte) 0x51, - (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0xd1, - (byte) 0x54, (byte) 0x01, (byte) 0x51, (byte) 0x15, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x38, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x58, - (byte) 0x08, (byte) 0x85, (byte) 0x86, (byte) 0xac, - (byte) 0x08, (byte) 0x00, (byte) 0xe2, (byte) 0x04, - (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x00, (byte) 0x00, (byte) 0x30, (byte) 0xe0, - (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x60, - (byte) 0x42, (byte) 0x19, (byte) 0x28, (byte) 0x34, - (byte) 0x64, (byte) 0x45, (byte) 0x00, (byte) 0x10, - (byte) 0x27, (byte) 0x00, (byte) 0x60, (byte) 0x70, - (byte) 0x1c, (byte) 0xcb, (byte) 0x02, (byte) 0x00, - (byte) 0x00, (byte) 0x47, (byte) 0x92, (byte) 0x34, - (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x1c, - (byte) 0x49, (byte) 0xd2, (byte) 0x34, (byte) 0x00, - (byte) 0x00, (byte) 0xd0, (byte) 0x34, (byte) 0x4d, - (byte) 0x14, (byte) 0x01, (byte) 0x00, (byte) 0xc0, - (byte) 0xd2, (byte) 0x34, (byte) 0x51, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x30, - (byte) 0xe0, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x60, (byte) 0x42, (byte) 0x19, (byte) 0x28, - (byte) 0x34, (byte) 0x64, (byte) 0x25, (byte) 0x00, - (byte) 0x10, (byte) 0x05, (byte) 0x00, (byte) 0x60, - (byte) 0x30, (byte) 0x14, (byte) 0x4d, (byte) 0x03, - (byte) 0x58, (byte) 0x16, (byte) 0xc0, (byte) 0xb2, - (byte) 0x00, (byte) 0x9a, (byte) 0x06, (byte) 0xd0, - (byte) 0x34, (byte) 0x80, (byte) 0xe7, (byte) 0x01, - (byte) 0x3c, (byte) 0x11, (byte) 0x60, (byte) 0x9a, - (byte) 0x00, (byte) 0x40, (byte) 0x00, (byte) 0x00, - (byte) 0x40, (byte) 0x81, (byte) 0x03, (byte) 0x00, - (byte) 0x40, (byte) 0x80, (byte) 0x0d, (byte) 0x9a, - (byte) 0x12, (byte) 0x8b, (byte) 0x03, (byte) 0x14, - (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, - (byte) 0x88, (byte) 0x02, (byte) 0x00, (byte) 0x30, - (byte) 0x28, (byte) 0x8a, (byte) 0x24, (byte) 0x59, - (byte) 0x96, (byte) 0xe7, (byte) 0x41, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0x14, (byte) 0xa1, - (byte) 0x69, (byte) 0x9a, (byte) 0x26, (byte) 0x8a, - (byte) 0xf0, (byte) 0x3c, (byte) 0xcf, (byte) 0x13, - (byte) 0x45, (byte) 0x78, (byte) 0x9e, (byte) 0xe7, - (byte) 0x99, (byte) 0x26, (byte) 0x44, (byte) 0xd1, - (byte) 0xf3, (byte) 0x4c, (byte) 0x13, (byte) 0xa2, - (byte) 0xe8, (byte) 0x79, (byte) 0xa6, (byte) 0x09, - (byte) 0xd3, (byte) 0x14, (byte) 0x45, (byte) 0xd3, - (byte) 0x04, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x0a, - (byte) 0x1c, (byte) 0x00, (byte) 0x00, (byte) 0x02, - (byte) 0x6c, (byte) 0xd0, (byte) 0x94, (byte) 0x58, - (byte) 0x1c, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, - (byte) 0x95, (byte) 0x00, (byte) 0x40, (byte) 0x48, - (byte) 0x00, (byte) 0x80, (byte) 0x41, (byte) 0x51, - (byte) 0x2c, (byte) 0xcb, (byte) 0xf3, (byte) 0x44, - (byte) 0x51, (byte) 0x14, (byte) 0x4d, (byte) 0x53, - (byte) 0x55, (byte) 0x5d, (byte) 0x17, (byte) 0x9a, - (byte) 0xe6, (byte) 0x79, (byte) 0xa2, (byte) 0x28, - (byte) 0x8a, (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, - (byte) 0xae, (byte) 0x0b, (byte) 0x4d, (byte) 0xf3, - (byte) 0x3c, (byte) 0x51, (byte) 0x14, (byte) 0x45, - (byte) 0xd3, (byte) 0x54, (byte) 0x55, (byte) 0xd7, - (byte) 0x85, (byte) 0xe7, (byte) 0x79, (byte) 0xa2, - (byte) 0x29, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0xaa, (byte) 0xaa, (byte) 0xeb, (byte) 0xc2, - (byte) 0xf3, (byte) 0x44, (byte) 0xd1, (byte) 0x34, - (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x55, - (byte) 0xd7, (byte) 0x75, (byte) 0xe1, (byte) 0x79, - (byte) 0xa2, (byte) 0x68, (byte) 0x9a, (byte) 0xa6, - (byte) 0xa9, (byte) 0xaa, (byte) 0xae, (byte) 0xeb, - (byte) 0xba, (byte) 0xf0, (byte) 0x3c, (byte) 0x51, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x54, - (byte) 0x55, (byte) 0xd7, (byte) 0x95, (byte) 0x65, - (byte) 0x88, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0xaa, (byte) 0xaa, - (byte) 0xeb, (byte) 0xca, (byte) 0x32, (byte) 0x10, - (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x59, - (byte) 0x06, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, - (byte) 0xaa, (byte) 0xea, (byte) 0xba, (byte) 0xae, - (byte) 0x2b, (byte) 0xcb, (byte) 0x40, (byte) 0x14, - (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x5d, - (byte) 0xd7, (byte) 0x75, (byte) 0x65, (byte) 0x19, - (byte) 0x98, (byte) 0xa6, (byte) 0x6a, (byte) 0xaa, - (byte) 0xaa, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, - (byte) 0x2c, (byte) 0x03, (byte) 0x4c, (byte) 0x53, - (byte) 0x55, (byte) 0x5d, (byte) 0x57, (byte) 0x96, - (byte) 0x65, (byte) 0x19, (byte) 0xa0, (byte) 0xaa, - (byte) 0xae, (byte) 0xeb, (byte) 0xba, (byte) 0xb2, - (byte) 0x6c, (byte) 0xdb, (byte) 0x00, (byte) 0x55, - (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x95, - (byte) 0x65, (byte) 0xdb, (byte) 0x06, (byte) 0xb8, - (byte) 0xae, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, - (byte) 0x2c, (byte) 0xdb, (byte) 0x36, (byte) 0x00, - (byte) 0xd7, (byte) 0x95, (byte) 0x65, (byte) 0x59, - (byte) 0xb6, (byte) 0x6d, (byte) 0x01, (byte) 0x00, - (byte) 0x00, (byte) 0x07, (byte) 0x0e, (byte) 0x00, - (byte) 0x00, (byte) 0x01, (byte) 0x46, (byte) 0xd0, - (byte) 0x49, (byte) 0x46, (byte) 0x95, (byte) 0x45, - (byte) 0xd8, (byte) 0x68, (byte) 0xc2, (byte) 0x85, - (byte) 0x07, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, - (byte) 0x15, (byte) 0x01, (byte) 0x40, (byte) 0x14, - (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x8c, - (byte) 0x52, (byte) 0x8a, (byte) 0x29, (byte) 0x65, - (byte) 0x18, (byte) 0x93, (byte) 0x50, (byte) 0x4a, - (byte) 0x09, (byte) 0x0d, (byte) 0x63, (byte) 0x52, - (byte) 0x4a, (byte) 0x2a, (byte) 0xa5, (byte) 0x92, - (byte) 0x92, (byte) 0x52, (byte) 0x4a, (byte) 0xa5, - (byte) 0x54, (byte) 0x12, (byte) 0x52, (byte) 0x4a, - (byte) 0xa9, (byte) 0x94, (byte) 0x4a, (byte) 0x4a, - (byte) 0x4a, (byte) 0x29, (byte) 0x95, (byte) 0x92, - (byte) 0x51, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, - (byte) 0x96, (byte) 0x2a, (byte) 0x29, (byte) 0xa9, - (byte) 0x94, (byte) 0x94, (byte) 0x52, (byte) 0x25, - (byte) 0xa5, (byte) 0xa4, (byte) 0x92, (byte) 0x52, - (byte) 0x2a, (byte) 0x00, (byte) 0x00, (byte) 0xec, - (byte) 0xc0, (byte) 0x01, (byte) 0x00, (byte) 0xec, - (byte) 0xc0, (byte) 0x42, (byte) 0x28, (byte) 0x34, - (byte) 0x64, (byte) 0x25, (byte) 0x00, (byte) 0x90, - (byte) 0x07, (byte) 0x00, (byte) 0x40, (byte) 0x10, - (byte) 0x82, (byte) 0x14, (byte) 0x63, (byte) 0x8c, - (byte) 0x39, (byte) 0x27, (byte) 0xa5, (byte) 0x54, - (byte) 0x8a, (byte) 0x31, (byte) 0xe7, (byte) 0x9c, - (byte) 0x93, (byte) 0x52, (byte) 0x2a, (byte) 0xc5, - (byte) 0x98, (byte) 0x73, (byte) 0xce, (byte) 0x49, - (byte) 0x29, (byte) 0x19, (byte) 0x63, (byte) 0xcc, - (byte) 0x39, (byte) 0xe7, (byte) 0xa4, (byte) 0x94, - (byte) 0x8c, (byte) 0x31, (byte) 0xe6, (byte) 0x9c, - (byte) 0x73, (byte) 0x52, (byte) 0x4a, (byte) 0xc6, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0x29, (byte) 0x25, (byte) 0x63, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x94, - (byte) 0xd2, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x83, (byte) 0x50, (byte) 0x4a, (byte) 0x29, - (byte) 0xa5, (byte) 0x73, (byte) 0xce, (byte) 0x41, - (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x12, - (byte) 0x42, (byte) 0xe7, (byte) 0x20, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0xe9, (byte) 0x9c, - (byte) 0x73, (byte) 0x10, (byte) 0x0a, (byte) 0x00, - (byte) 0x00, (byte) 0x2a, (byte) 0x70, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0xb0, (byte) 0x51, - (byte) 0x64, (byte) 0x73, (byte) 0x82, (byte) 0x91, - (byte) 0xa0, (byte) 0x42, (byte) 0x43, (byte) 0x56, - (byte) 0x02, (byte) 0x00, (byte) 0xa9, (byte) 0x00, - (byte) 0x00, (byte) 0x06, (byte) 0xc7, (byte) 0xb1, - (byte) 0x2c, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0xcf, (byte) 0x13, (byte) 0x45, (byte) 0x4b, - (byte) 0x92, (byte) 0x34, (byte) 0xcf, (byte) 0x13, - (byte) 0x3d, (byte) 0x4f, (byte) 0x14, (byte) 0x4d, - (byte) 0xd5, (byte) 0x92, (byte) 0x24, (byte) 0xcf, - (byte) 0x13, (byte) 0x45, (byte) 0xcf, (byte) 0x13, - (byte) 0x4d, (byte) 0x53, (byte) 0xe5, (byte) 0x79, - (byte) 0x9e, (byte) 0x28, (byte) 0x8a, (byte) 0xa2, - (byte) 0x68, (byte) 0x9a, (byte) 0xaa, (byte) 0x4a, - (byte) 0x14, (byte) 0x45, (byte) 0x4f, (byte) 0x14, - (byte) 0x45, (byte) 0xd1, (byte) 0x34, (byte) 0x55, - (byte) 0x95, (byte) 0x2c, (byte) 0x8b, (byte) 0xa2, - (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0xaa, - (byte) 0xba, (byte) 0x2e, (byte) 0x5b, (byte) 0x16, - (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x98, - (byte) 0xa6, (byte) 0x28, (byte) 0xaa, (byte) 0xaa, - (byte) 0xeb, (byte) 0xca, (byte) 0x2e, (byte) 0x4c, - (byte) 0x53, (byte) 0x14, (byte) 0x4d, (byte) 0xd3, - (byte) 0x75, (byte) 0x65, (byte) 0x19, (byte) 0xb2, - (byte) 0xad, (byte) 0x9a, (byte) 0xaa, (byte) 0xea, - (byte) 0xba, (byte) 0xb2, (byte) 0x0d, (byte) 0xdb, - (byte) 0x36, (byte) 0x4d, (byte) 0x55, (byte) 0x75, - (byte) 0x5d, (byte) 0x59, (byte) 0x06, (byte) 0xae, - (byte) 0xeb, (byte) 0xba, (byte) 0xb2, (byte) 0x6c, - (byte) 0xeb, (byte) 0xc0, (byte) 0x75, (byte) 0x5d, - (byte) 0x57, (byte) 0x96, (byte) 0x6d, (byte) 0x5d, - (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x82, - (byte) 0x03, (byte) 0x00, (byte) 0x50, (byte) 0x81, - (byte) 0x0d, (byte) 0xab, (byte) 0x23, (byte) 0x9c, - (byte) 0x14, (byte) 0x8d, (byte) 0x05, (byte) 0x16, - (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, - (byte) 0xc8, (byte) 0x00, (byte) 0x00, (byte) 0x20, - (byte) 0x08, (byte) 0x41, (byte) 0x48, (byte) 0x29, - (byte) 0x85, (byte) 0x90, (byte) 0x52, (byte) 0x0a, - (byte) 0x21, (byte) 0xa5, (byte) 0x14, (byte) 0x42, - (byte) 0x4a, (byte) 0x29, (byte) 0x84, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x0c, (byte) 0x38, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x98, - (byte) 0x50, (byte) 0x06, (byte) 0x0a, (byte) 0x0d, - (byte) 0x59, (byte) 0x09, (byte) 0x00, (byte) 0xa4, - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, - (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, - (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, - (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, - (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, - (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, - (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, - (byte) 0x84, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0x02, (byte) 0x00, (byte) 0xb1, (byte) 0x2b, - (byte) 0x1c, (byte) 0x00, (byte) 0x76, (byte) 0x22, - (byte) 0x6c, (byte) 0x58, (byte) 0x1d, (byte) 0xe1, - (byte) 0xa4, (byte) 0x68, (byte) 0x2c, (byte) 0xb0, - (byte) 0xd0, (byte) 0x90, (byte) 0x95, (byte) 0x00, - (byte) 0x40, (byte) 0x38, (byte) 0x00, (byte) 0x00, - (byte) 0x60, (byte) 0x8c, (byte) 0x31, (byte) 0xce, - (byte) 0x59, (byte) 0xac, (byte) 0xb5, (byte) 0xd6, - (byte) 0x5a, (byte) 0x2b, (byte) 0xa5, (byte) 0x94, - (byte) 0x92, (byte) 0x50, (byte) 0x6b, (byte) 0xad, - (byte) 0xb5, (byte) 0xd6, (byte) 0x9a, (byte) 0x29, - (byte) 0xa4, (byte) 0x94, (byte) 0x84, (byte) 0x16, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x98, (byte) 0x31, (byte) 0x08, (byte) 0x29, - (byte) 0xb5, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xc6, (byte) 0x8c, (byte) 0x39, - (byte) 0x47, (byte) 0x2d, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xb6, - (byte) 0x56, (byte) 0x4a, (byte) 0x6c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0xb1, (byte) 0xb5, (byte) 0x52, (byte) 0x62, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x16, (byte) 0x5b, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x6c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, - (byte) 0x2c, (byte) 0x00, (byte) 0xc0, (byte) 0xe4, - (byte) 0xc1, (byte) 0x01, (byte) 0x00, (byte) 0x2a, - (byte) 0xc1, (byte) 0xc6, (byte) 0x19, (byte) 0x56, - (byte) 0x92, (byte) 0xce, (byte) 0x0a, (byte) 0x47, - (byte) 0x83, (byte) 0x0b, (byte) 0x0d, (byte) 0x59, - (byte) 0x09, (byte) 0x00, (byte) 0xe4, (byte) 0x06, - (byte) 0x00, (byte) 0x00, (byte) 0xc6, (byte) 0x28, - (byte) 0xc5, (byte) 0x98, (byte) 0x63, (byte) 0xce, - (byte) 0x41, (byte) 0x08, (byte) 0xa1, (byte) 0x94, - (byte) 0x12, (byte) 0x4a, (byte) 0x49, (byte) 0xad, - (byte) 0x75, (byte) 0xce, (byte) 0x39, (byte) 0x08, - (byte) 0x21, (byte) 0x94, (byte) 0x52, (byte) 0x4a, - (byte) 0x49, (byte) 0xa9, (byte) 0xb4, (byte) 0x94, - (byte) 0x62, (byte) 0xca, (byte) 0x98, (byte) 0x73, - (byte) 0xce, (byte) 0x41, (byte) 0x08, (byte) 0xa5, - (byte) 0x94, (byte) 0x12, (byte) 0x4a, (byte) 0x49, - (byte) 0xa9, (byte) 0xa5, (byte) 0xd4, (byte) 0x39, - (byte) 0xe7, (byte) 0x20, (byte) 0x94, (byte) 0x52, - (byte) 0x4a, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, - (byte) 0x94, (byte) 0x5a, (byte) 0x6a, (byte) 0xad, - (byte) 0x73, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4a, - (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0xd4, - (byte) 0x52, (byte) 0x08, (byte) 0x21, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0x2a, (byte) 0x29, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, - (byte) 0xad, (byte) 0xa5, (byte) 0x10, (byte) 0x42, - (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, - (byte) 0xd4, (byte) 0x5a, (byte) 0x8b, (byte) 0xa1, - (byte) 0x94, (byte) 0x90, (byte) 0x4a, (byte) 0x29, - (byte) 0x25, (byte) 0xa5, (byte) 0x94, (byte) 0x52, - (byte) 0x49, (byte) 0x2d, (byte) 0xb5, (byte) 0x96, - (byte) 0x5a, (byte) 0x2a, (byte) 0xa1, (byte) 0x94, - (byte) 0x54, (byte) 0x52, (byte) 0x4a, (byte) 0x29, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, - (byte) 0xa9, (byte) 0xb5, (byte) 0x56, (byte) 0x4a, - (byte) 0x49, (byte) 0x25, (byte) 0xa5, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, - (byte) 0xd4, (byte) 0x62, (byte) 0x6b, (byte) 0x29, - (byte) 0x94, (byte) 0x92, (byte) 0x52, (byte) 0x49, - (byte) 0x29, (byte) 0xb5, (byte) 0x94, (byte) 0x52, - (byte) 0x4a, (byte) 0xad, (byte) 0xc5, (byte) 0xd8, - (byte) 0x62, (byte) 0x29, (byte) 0xad, (byte) 0xa4, - (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0x29, - (byte) 0xa5, (byte) 0xd6, (byte) 0x52, (byte) 0x6c, - (byte) 0xad, (byte) 0xb5, (byte) 0xd8, (byte) 0x52, - (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0x96, - (byte) 0x5a, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, - (byte) 0x16, (byte) 0x5b, (byte) 0x6a, (byte) 0x2d, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4b, - (byte) 0x29, (byte) 0xa5, (byte) 0x96, (byte) 0x52, - (byte) 0x4b, (byte) 0x2d, (byte) 0xc6, (byte) 0xd6, - (byte) 0x5a, (byte) 0x4b, (byte) 0x29, (byte) 0xa5, - (byte) 0xd4, (byte) 0x52, (byte) 0x6a, (byte) 0xa9, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6c, - (byte) 0xad, (byte) 0xb5, (byte) 0x98, (byte) 0x52, - (byte) 0x6a, (byte) 0x2d, (byte) 0xa5, (byte) 0xd4, - (byte) 0x52, (byte) 0x6b, (byte) 0x2d, (byte) 0xb5, - (byte) 0xd8, (byte) 0x52, (byte) 0x6a, (byte) 0x2d, - (byte) 0xb5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, - (byte) 0xa9, (byte) 0xa5, (byte) 0x94, (byte) 0x5a, - (byte) 0x6b, (byte) 0x2d, (byte) 0xb6, (byte) 0xd8, - (byte) 0x5a, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, - (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0xa9, - (byte) 0xb5, (byte) 0x16, (byte) 0x5b, (byte) 0x8a, - (byte) 0xb1, (byte) 0xb5, (byte) 0xd4, (byte) 0x4a, - (byte) 0x4a, (byte) 0x29, (byte) 0xb5, (byte) 0xd4, - (byte) 0x5a, (byte) 0x6a, (byte) 0x2d, (byte) 0xb6, - (byte) 0x16, (byte) 0x5b, (byte) 0x6b, (byte) 0xad, - (byte) 0xa5, (byte) 0xd6, (byte) 0x5a, (byte) 0x6a, - (byte) 0x29, (byte) 0xa5, (byte) 0x16, (byte) 0x5b, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x16, - (byte) 0x63, (byte) 0x6b, (byte) 0x31, (byte) 0xa5, - (byte) 0x94, (byte) 0x52, (byte) 0x4b, (byte) 0xa9, - (byte) 0xa5, (byte) 0x02, (byte) 0x00, (byte) 0x80, - (byte) 0x0e, (byte) 0x1c, (byte) 0x00, (byte) 0x00, - (byte) 0x02, (byte) 0x8c, (byte) 0xa8, (byte) 0xb4, - (byte) 0x10, (byte) 0x3b, (byte) 0xcd, (byte) 0xb8, - (byte) 0xf2, (byte) 0x08, (byte) 0x1c, (byte) 0x51, - (byte) 0xc8, (byte) 0x30, (byte) 0x01, (byte) 0x15, - (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, - (byte) 0x20, (byte) 0x03, (byte) 0x00, (byte) 0x20, - (byte) 0x90, (byte) 0x69, (byte) 0x92, (byte) 0x39, - (byte) 0x49, (byte) 0xa9, (byte) 0x11, (byte) 0x26, - (byte) 0x39, (byte) 0xc5, (byte) 0xa0, (byte) 0x94, - (byte) 0xe6, (byte) 0x9c, (byte) 0x53, (byte) 0x4a, - (byte) 0x29, (byte) 0xa5, (byte) 0x34, (byte) 0x44, - (byte) 0x96, (byte) 0x64, (byte) 0x90, (byte) 0x62, - (byte) 0x50, (byte) 0x1d, (byte) 0x99, (byte) 0x8c, - (byte) 0x39, (byte) 0x49, (byte) 0x39, (byte) 0x43, - (byte) 0xa4, (byte) 0x31, (byte) 0xa4, (byte) 0x20, - (byte) 0xf5, (byte) 0x4c, (byte) 0x91, (byte) 0xc7, - (byte) 0x94, (byte) 0x62, (byte) 0x10, (byte) 0x43, - (byte) 0x48, (byte) 0x2a, (byte) 0x74, (byte) 0x8a, - (byte) 0x39, (byte) 0x6c, (byte) 0x35, (byte) 0xf9, - (byte) 0x58, (byte) 0x42, (byte) 0x07, (byte) 0xb1, - (byte) 0x06, (byte) 0x65, (byte) 0x8c, (byte) 0x70, - (byte) 0x29, (byte) 0xc5, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0x02, (byte) 0x00, - (byte) 0x04, (byte) 0x84, (byte) 0x04, (byte) 0x00, - (byte) 0x18, (byte) 0x20, (byte) 0x28, (byte) 0x98, - (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x07, - (byte) 0x08, (byte) 0x23, (byte) 0x07, (byte) 0x02, - (byte) 0x1d, (byte) 0x01, (byte) 0x04, (byte) 0x0e, - (byte) 0x6d, (byte) 0x00, (byte) 0x80, (byte) 0x81, - (byte) 0x08, (byte) 0x99, (byte) 0x09, (byte) 0x0c, - (byte) 0x0a, (byte) 0xa1, (byte) 0xc1, (byte) 0x41, - (byte) 0x26, (byte) 0x00, (byte) 0x3c, (byte) 0x40, - (byte) 0x44, (byte) 0x48, (byte) 0x05, (byte) 0x00, - (byte) 0x89, (byte) 0x09, (byte) 0x8a, (byte) 0xd2, - (byte) 0x85, (byte) 0x2e, (byte) 0x08, (byte) 0x21, - (byte) 0x82, (byte) 0x74, (byte) 0x11, (byte) 0x64, - (byte) 0xf1, (byte) 0xc0, (byte) 0x85, (byte) 0x13, - (byte) 0x37, (byte) 0x9e, (byte) 0xb8, (byte) 0xe1, - (byte) 0x84, (byte) 0x0e, (byte) 0x6d, (byte) 0x20, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0xf0, - (byte) 0x01, (byte) 0x00, (byte) 0x90, (byte) 0x50, - (byte) 0x00, (byte) 0x11, (byte) 0x11, (byte) 0xd1, - (byte) 0xcc, (byte) 0x55, (byte) 0x58, (byte) 0x5c, - (byte) 0x60, (byte) 0x64, (byte) 0x68, (byte) 0x6c, - (byte) 0x70, (byte) 0x74, (byte) 0x78, (byte) 0x7c, - (byte) 0x80, (byte) 0x84, (byte) 0x08, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x10, (byte) 0x00, (byte) 0x7c, (byte) 0x00, - (byte) 0x00, (byte) 0x24, (byte) 0x22, (byte) 0x40, - (byte) 0x44, (byte) 0x44, (byte) 0x34, (byte) 0x73, - (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, - (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c, - (byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20, - (byte) 0x21, (byte) 0x01, (byte) 0x00, (byte) 0x80, - (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x20, (byte) 0x80, - (byte) 0x00, (byte) 0x04, (byte) 0x04, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x04, (byte) 0x04 - }; - -} 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 fb50ef131b2..ee170682423 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 @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import android.app.Instrumentation; import android.content.Context; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.Extractor; @@ -132,20 +131,10 @@ public static byte[] joinByteArrays(byte[]... byteArrays) { return joined; } - public static byte[] getByteArray(Instrumentation instrumentation, String fileName) - throws IOException { - return getByteArray(instrumentation.getContext(), fileName); - } - public static byte[] getByteArray(Context context, String fileName) throws IOException { return Util.toByteArray(getInputStream(context, fileName)); } - public static InputStream getInputStream(Instrumentation instrumentation, String fileName) - throws IOException { - return getInputStream(instrumentation.getContext(), fileName); - } - public static InputStream getInputStream(Context context, String fileName) throws IOException { return context.getResources().getAssets().open(fileName); } diff --git a/testutils/src/test/AndroidManifest.xml b/testutils/src/test/AndroidManifest.xml new file mode 100644 index 00000000000..9602d016338 --- /dev/null +++ b/testutils/src/test/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java new file mode 100644 index 00000000000..7fd84f6287e --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java @@ -0,0 +1,147 @@ +/* + * 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.testutil; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData; +import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; +import com.google.android.exoplayer2.util.MimeTypes; +import java.util.List; +import java.util.Random; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link FakeAdaptiveDataSet}. */ +@RunWith(RobolectricTestRunner.class) +public final class FakeAdaptiveDataSetTest { + + private static final Format[] TEST_FORMATS = { + Format.createVideoSampleFormat( + null, + MimeTypes.VIDEO_H264, + null, + 1000000, + Format.NO_VALUE, + 1280, + 720, + Format.NO_VALUE, + null, + null), + Format.createVideoSampleFormat( + null, + MimeTypes.VIDEO_H264, + null, + 300000, + Format.NO_VALUE, + 640, + 360, + Format.NO_VALUE, + null, + null) + }; + private static final TrackGroup TRACK_GROUP = new TrackGroup(TEST_FORMATS); + + @Test + public void testAdaptiveDataSet() { + long chunkDuration = 2 * C.MICROS_PER_SECOND; + FakeAdaptiveDataSet dataSet = + new FakeAdaptiveDataSet( + TRACK_GROUP, 10 * C.MICROS_PER_SECOND, chunkDuration, 0.0, new Random(0)); + assertThat(dataSet.getAllData().size()).isEqualTo(TEST_FORMATS.length); + assertThat(dataSet.getUri(0).equals(dataSet.getUri(1))).isFalse(); + assertThat(dataSet.getChunkCount()).isEqualTo(5); + assertThat(dataSet.getChunkIndexByPosition(4 * C.MICROS_PER_SECOND)).isEqualTo(2); + assertThat(dataSet.getChunkIndexByPosition(9 * C.MICROS_PER_SECOND)).isEqualTo(4); + for (int i = 0; i < dataSet.getChunkCount(); i++) { + assertThat(dataSet.getChunkDuration(i)).isEqualTo(chunkDuration); + } + assertChunkData(dataSet, chunkDuration); + } + + @Test + public void testAdaptiveDataSetTrailingSmallChunk() { + long chunkDuration = 3 * C.MICROS_PER_SECOND; + FakeAdaptiveDataSet dataSet = + new FakeAdaptiveDataSet( + TRACK_GROUP, 10 * C.MICROS_PER_SECOND, chunkDuration, 0.0, new Random(0)); + assertThat(dataSet.getAllData().size()).isEqualTo(TEST_FORMATS.length); + assertThat(dataSet.getUri(0).equals(dataSet.getUri(1))).isFalse(); + assertThat(dataSet.getChunkCount()).isEqualTo(4); + assertThat(dataSet.getChunkIndexByPosition(4 * C.MICROS_PER_SECOND)).isEqualTo(1); + assertThat(dataSet.getChunkIndexByPosition(9 * C.MICROS_PER_SECOND)).isEqualTo(3); + for (int i = 0; i < dataSet.getChunkCount() - 1; i++) { + assertThat(dataSet.getChunkDuration(i)).isEqualTo(chunkDuration); + } + assertThat(dataSet.getChunkDuration(3)).isEqualTo(1 * C.MICROS_PER_SECOND); + assertChunkData(dataSet, chunkDuration); + } + + @Test + public void testAdaptiveDataSetChunkSizeDistribution() { + double expectedStdDev = 4.0; + FakeAdaptiveDataSet dataSet = + new FakeAdaptiveDataSet( + TRACK_GROUP, + 100000 * C.MICROS_PER_SECOND, + 1 * C.MICROS_PER_SECOND, + expectedStdDev, + new Random(0)); + for (int i = 0; i < TEST_FORMATS.length; i++) { + FakeData data = dataSet.getData(dataSet.getUri(i)); + double mean = computeSegmentSizeMean(data.getSegments()); + double stddev = computeSegmentSizeStdDev(data.getSegments(), mean); + double relativePercentStdDev = stddev / mean * 100.0; + assertThat(relativePercentStdDev).isWithin(0.02).of(expectedStdDev); + assertThat(mean * 8 / TEST_FORMATS[i].bitrate).isWithin(0.01).of(1.0); + } + } + + private void assertChunkData(FakeAdaptiveDataSet dataSet, long chunkDuration) { + for (int i = 0; i < dataSet.getChunkCount(); i++) { + assertThat(dataSet.getStartTime(i)).isEqualTo(chunkDuration * i); + } + for (int s = 0; s < TEST_FORMATS.length; s++) { + FakeData data = dataSet.getData(dataSet.getUri(s)); + assertThat(data.getSegments().size()).isEqualTo(dataSet.getChunkCount()); + for (int i = 0; i < data.getSegments().size(); i++) { + long expectedLength = + TEST_FORMATS[s].bitrate * dataSet.getChunkDuration(i) / (8 * C.MICROS_PER_SECOND); + assertThat(data.getSegments().get(i).length).isEqualTo(expectedLength); + } + } + } + + private static double computeSegmentSizeMean(List segments) { + double totalSize = 0.0; + for (Segment segment : segments) { + totalSize += segment.length; + } + return totalSize / segments.size(); + } + + private static double computeSegmentSizeStdDev(List segments, double mean) { + double totalSquaredSize = 0.0; + for (Segment segment : segments) { + totalSquaredSize += (double) segment.length * segment.length; + } + return Math.sqrt(totalSquaredSize / segments.size() - mean * mean); + } +} diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java new file mode 100644 index 00000000000..725753ce461 --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java @@ -0,0 +1,201 @@ +/* + * 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.testutil; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.ConditionVariable; +import android.os.HandlerThread; +import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.util.HandlerWrapper; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Unit test for {@link FakeClock}. */ +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class}) +public final class FakeClockTest { + + private static final long TIMEOUT_MS = 10000; + + @Test + public void testAdvanceTime() { + FakeClock fakeClock = new FakeClock(2000); + assertThat(fakeClock.elapsedRealtime()).isEqualTo(2000); + fakeClock.advanceTime(500); + assertThat(fakeClock.elapsedRealtime()).isEqualTo(2500); + fakeClock.advanceTime(0); + assertThat(fakeClock.elapsedRealtime()).isEqualTo(2500); + } + + @Test + public void testSleep() throws InterruptedException { + FakeClock fakeClock = new FakeClock(0); + SleeperThread sleeperThread = new SleeperThread(fakeClock, 1000); + sleeperThread.start(); + assertThat(sleeperThread.waitUntilAsleep(TIMEOUT_MS)).isTrue(); + assertThat(sleeperThread.isSleeping()).isTrue(); + fakeClock.advanceTime(1000); + sleeperThread.join(TIMEOUT_MS); + assertThat(sleeperThread.isSleeping()).isFalse(); + + sleeperThread = new SleeperThread(fakeClock, 0); + sleeperThread.start(); + sleeperThread.join(); + assertThat(sleeperThread.isSleeping()).isFalse(); + + SleeperThread[] sleeperThreads = new SleeperThread[5]; + sleeperThreads[0] = new SleeperThread(fakeClock, 1000); + sleeperThreads[1] = new SleeperThread(fakeClock, 1000); + sleeperThreads[2] = new SleeperThread(fakeClock, 2000); + sleeperThreads[3] = new SleeperThread(fakeClock, 3000); + sleeperThreads[4] = new SleeperThread(fakeClock, 4000); + for (SleeperThread thread : sleeperThreads) { + thread.start(); + assertThat(thread.waitUntilAsleep(TIMEOUT_MS)).isTrue(); + } + assertSleepingStates(new boolean[] {true, true, true, true, true}, sleeperThreads); + fakeClock.advanceTime(1500); + assertThat(sleeperThreads[0].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertThat(sleeperThreads[1].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertSleepingStates(new boolean[] {false, false, true, true, true}, sleeperThreads); + fakeClock.advanceTime(2000); + assertThat(sleeperThreads[2].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertThat(sleeperThreads[3].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertSleepingStates(new boolean[] {false, false, false, false, true}, sleeperThreads); + fakeClock.advanceTime(2000); + for (SleeperThread thread : sleeperThreads) { + thread.join(TIMEOUT_MS); + } + assertSleepingStates(new boolean[] {false, false, false, false, false}, sleeperThreads); + } + + @Test + public void testPostDelayed() { + HandlerThread handlerThread = new HandlerThread("FakeClockTest thread"); + handlerThread.start(); + FakeClock fakeClock = new FakeClock(0); + HandlerWrapper handler = + fakeClock.createHandler(handlerThread.getLooper(), /* callback= */ null); + + TestRunnable[] testRunnables = { + new TestRunnable(), + new TestRunnable(), + new TestRunnable(), + new TestRunnable(), + new TestRunnable() + }; + handler.postDelayed(testRunnables[0], 0); + handler.postDelayed(testRunnables[1], 100); + handler.postDelayed(testRunnables[2], 200); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, false, false, false, false}, testRunnables); + + fakeClock.advanceTime(150); + handler.postDelayed(testRunnables[3], 50); + handler.postDelayed(testRunnables[4], 100); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, true, false, false, false}, testRunnables); + + fakeClock.advanceTime(50); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, true, true, true, false}, testRunnables); + + fakeClock.advanceTime(1000); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, true, true, true, true}, testRunnables); + } + + private static void assertSleepingStates(boolean[] states, SleeperThread[] sleeperThreads) { + for (int i = 0; i < sleeperThreads.length; i++) { + assertThat(sleeperThreads[i].isSleeping()).isEqualTo(states[i]); + } + } + + private static void waitForHandler(HandlerWrapper handler) { + final ConditionVariable handlerFinished = new ConditionVariable(); + handler.post( + new Runnable() { + @Override + public void run() { + handlerFinished.open(); + } + }); + handlerFinished.block(); + } + + private static void assertTestRunnableStates(boolean[] states, TestRunnable[] testRunnables) { + for (int i = 0; i < testRunnables.length; i++) { + assertThat(testRunnables[i].hasRun).isEqualTo(states[i]); + } + } + + private static final class SleeperThread extends Thread { + + private final Clock clock; + private final long sleepDurationMs; + private final CountDownLatch fallAsleepCountDownLatch; + private final CountDownLatch wakeUpCountDownLatch; + + private volatile boolean isSleeping; + + public SleeperThread(Clock clock, long sleepDurationMs) { + this.clock = clock; + this.sleepDurationMs = sleepDurationMs; + this.fallAsleepCountDownLatch = new CountDownLatch(1); + this.wakeUpCountDownLatch = new CountDownLatch(1); + } + + public boolean waitUntilAsleep(long timeoutMs) throws InterruptedException { + return fallAsleepCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS); + } + + public boolean waitUntilAwake(long timeoutMs) throws InterruptedException { + return wakeUpCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS); + } + + public boolean isSleeping() { + return isSleeping; + } + + @Override + public void run() { + // This relies on the FakeClock's methods synchronizing on its own monitor to ensure that + // any interactions with it occur only after sleep() has called wait() or returned. + synchronized (clock) { + isSleeping = true; + fallAsleepCountDownLatch.countDown(); + clock.sleep(sleepDurationMs); + isSleeping = false; + wakeUpCountDownLatch.countDown(); + } + } + } + + private static final class TestRunnable implements Runnable { + + public boolean hasRun; + + @Override + public void run() { + hasRun = true; + } + } +} diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java new file mode 100644 index 00000000000..75c6f886c2b --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java @@ -0,0 +1,119 @@ +/* + * 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.testutil; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; +import java.io.IOException; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link FakeDataSet} */ +@RunWith(RobolectricTestRunner.class) +public final class FakeDataSetTest { + + @Test + public void testMultipleDataSets() { + byte[][] testData = new byte[4][]; + Uri[] uris = new Uri[3]; + for (int i = 0; i < 4; i++) { + testData[i] = TestUtil.buildTestData(10, i); + if (i > 0) { + uris[i - 1] = Uri.parse("test_uri_" + i); + } + } + FakeDataSet fakeDataSet = + new FakeDataSet() + .newDefaultData() + .appendReadData(testData[0]) + .endData() + .setData(uris[0], testData[1]) + .newData(uris[1]) + .appendReadData(testData[2]) + .endData() + .setData(uris[2], testData[3]); + + assertThat(fakeDataSet.getAllData().size()).isEqualTo(4); + assertThat(fakeDataSet.getData("unseen_uri")).isEqualTo(fakeDataSet.getData((Uri) null)); + for (int i = 0; i < 3; i++) { + assertThat(fakeDataSet.getData(uris[i]).uri).isEqualTo(uris[i]); + } + assertThat(fakeDataSet.getData((Uri) null).getData()).isEqualTo(testData[0]); + for (int i = 1; i < 4; i++) { + assertThat(fakeDataSet.getData(uris[i - 1]).getData()).isEqualTo(testData[i]); + } + } + + @Test + public void testSegmentTypes() { + byte[] testData = TestUtil.buildTestData(3); + Runnable runnable = + new Runnable() { + @Override + public void run() { + // Do nothing. + } + }; + IOException exception = new IOException(); + FakeDataSet fakeDataSet = + new FakeDataSet() + .newDefaultData() + .appendReadData(testData) + .appendReadData(testData) + .appendReadData(50) + .appendReadAction(runnable) + .appendReadError(exception) + .endData(); + + List segments = fakeDataSet.getData((Uri) null).getSegments(); + assertThat(segments.size()).isEqualTo(5); + assertSegment(segments.get(0), testData, 3, 0, null, null); + assertSegment(segments.get(1), testData, 3, 3, null, null); + assertSegment(segments.get(2), null, 50, 6, null, null); + assertSegment(segments.get(3), null, 0, 56, runnable, null); + assertSegment(segments.get(4), null, 0, 56, null, exception); + + byte[] allData = new byte[6]; + System.arraycopy(testData, 0, allData, 0, 3); + System.arraycopy(testData, 0, allData, 3, 3); + assertThat(fakeDataSet.getData((Uri) null).getData()).isEqualTo(allData); + } + + private static void assertSegment( + Segment segment, + byte[] data, + int length, + long byteOffset, + Runnable runnable, + IOException exception) { + if (data != null) { + assertThat(segment.data).isEqualTo(data); + assertThat(data).hasLength(length); + } else { + assertThat(segment.data).isNull(); + } + assertThat(segment.length).isEqualTo(length); + assertThat(segment.byteOffset).isEqualTo(byteOffset); + assertThat(segment.action).isEqualTo(runnable); + assertThat(segment.isActionSegment()).isEqualTo(runnable != null); + assertThat(segment.exception).isEqualTo(exception); + assertThat(segment.isErrorSegment()).isEqualTo(exception != null); + } +} diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java new file mode 100644 index 00000000000..c88aba4e087 --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java @@ -0,0 +1,251 @@ +/* + * 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.testutil; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import android.net.Uri; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.upstream.DataSpec; +import java.io.IOException; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link FakeDataSource}. */ +@RunWith(RobolectricTestRunner.class) +public final class FakeDataSourceTest { + + private static final String URI_STRING = "test://test.test"; + private static final byte[] BUFFER = new byte[500]; + private static final byte[] TEST_DATA = TestUtil.buildTestData(15); + private static final byte[] TEST_DATA_PART_1 = Arrays.copyOf(TEST_DATA, 10); + private static final byte[] TEST_DATA_PART_2 = Arrays.copyOfRange(TEST_DATA, 10, 15); + + private static Uri uri; + private static FakeDataSet fakeDataSet; + + @Before + public void setUp() { + uri = Uri.parse(URI_STRING); + fakeDataSet = + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(TEST_DATA_PART_1) + .appendReadData(TEST_DATA_PART_2) + .endData(); + } + + @Test + public void testReadFull() throws IOException { + FakeDataSource dataSource = new FakeDataSource(fakeDataSet); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(15); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(10); + assertBuffer(TEST_DATA_PART_1); + assertThat(dataSource.read(BUFFER, 10, BUFFER.length)).isEqualTo(5); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 15, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 20, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testReadPartialOpenEnded() throws IOException { + FakeDataSource dataSource = new FakeDataSource(fakeDataSet); + assertThat(dataSource.open(new DataSpec(uri, 7, C.LENGTH_UNSET, null))).isEqualTo(8); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(3); + assertBuffer(TEST_DATA_PART_1, 7, 3); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(5); + assertBuffer(TEST_DATA_PART_2); + assertThat(dataSource.read(BUFFER, 15, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testReadPartialBounded() throws IOException { + FakeDataSource dataSource = new FakeDataSource(fakeDataSet); + assertThat(dataSource.open(new DataSpec(uri, 9, 3, null))).isEqualTo(3); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(1); + assertBuffer(TEST_DATA_PART_1, 9, 1); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(2); + assertBuffer(TEST_DATA_PART_2, 0, 2); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + + assertThat(dataSource.open(new DataSpec(uri, 11, 4, null))).isEqualTo(4); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(4); + assertBuffer(TEST_DATA_PART_2, 1, 4); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testDummyData() throws IOException { + FakeDataSource dataSource = + new FakeDataSource( + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(100) + .appendReadData(TEST_DATA) + .appendReadData(200) + .endData()); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(315); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(100); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(200); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testException() throws IOException { + String errorMessage = "error, error, error"; + IOException exception = new IOException(errorMessage); + FakeDataSource dataSource = + new FakeDataSource( + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(TEST_DATA) + .appendReadError(exception) + .appendReadData(TEST_DATA) + .endData()); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(30); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + try { + dataSource.read(BUFFER, 0, BUFFER.length); + fail("IOException expected."); + } catch (IOException e) { + assertThat(e).hasMessageThat().isEqualTo(errorMessage); + } + try { + dataSource.read(BUFFER, 0, BUFFER.length); + fail("IOException expected."); + } catch (IOException e) { + assertThat(e).hasMessageThat().isEqualTo(errorMessage); + } + dataSource.close(); + assertThat(dataSource.open(new DataSpec(uri, 15, 15, null))).isEqualTo(15); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testRunnable() throws IOException { + TestRunnable[] runnables = new TestRunnable[3]; + for (int i = 0; i < 3; i++) { + runnables[i] = new TestRunnable(); + } + FakeDataSource dataSource = + new FakeDataSource( + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(TEST_DATA) + .appendReadAction(runnables[0]) + .appendReadData(TEST_DATA) + .appendReadAction(runnables[1]) + .appendReadAction(runnables[2]) + .appendReadData(TEST_DATA) + .endData()); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(45); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + for (int i = 0; i < 3; i++) { + assertThat(runnables[i].ran).isFalse(); + } + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + assertThat(runnables[0].ran).isTrue(); + assertThat(runnables[1].ran).isFalse(); + assertThat(runnables[2].ran).isFalse(); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + for (int i = 0; i < 3; i++) { + assertThat(runnables[i].ran).isTrue(); + } + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testOpenSourceFailures() throws IOException { + // Empty data. + FakeDataSource dataSource = + new FakeDataSource(new FakeDataSet().newData(uri.toString()).endData()); + try { + dataSource.open(new DataSpec(uri)); + fail("IOException expected."); + } catch (IOException e) { + // Expected. + } finally { + dataSource.close(); + } + + // Non-existent data + dataSource = new FakeDataSource(new FakeDataSet()); + try { + dataSource.open(new DataSpec(uri)); + fail("IOException expected."); + } catch (IOException e) { + // Expected. + } finally { + dataSource.close(); + } + + // DataSpec out of bounds. + dataSource = + new FakeDataSource( + new FakeDataSet() + .newDefaultData() + .appendReadData(TestUtil.buildTestData(10)) + .endData()); + try { + dataSource.open(new DataSpec(uri, 5, 10, null)); + fail("IOException expected."); + } catch (IOException e) { + // Expected. + } finally { + dataSource.close(); + } + } + + private static void assertBuffer(byte[] expected) { + assertBuffer(expected, 0, expected.length); + } + + private static void assertBuffer(byte[] expected, int expectedStart, int expectedLength) { + for (int i = 0; i < expectedLength; i++) { + assertThat(BUFFER[i]).isEqualTo(expected[i + expectedStart]); + } + } + + private static final class TestRunnable implements Runnable { + + public boolean ran; + + @Override + public void run() { + ran = true; + } + } +} diff --git a/testutils/src/test/resources/robolectric.properties b/testutils/src/test/resources/robolectric.properties new file mode 100644 index 00000000000..2f3210368ec --- /dev/null +++ b/testutils/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/testutils_robolectric/build.gradle b/testutils_robolectric/build.gradle new file mode 100644 index 00000000000..f5a427a4b39 --- /dev/null +++ b/testutils_robolectric/build.gradle @@ -0,0 +1,37 @@ +// Copyright (C) 2018 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. +apply from: '../constants.gradle' +apply plugin: 'com.android.library' + +android { + compileSdkVersion project.ext.compileSdkVersion + buildToolsVersion project.ext.buildToolsVersion + + defaultConfig { + minSdkVersion project.ext.minSdkVersion + targetSdkVersion project.ext.targetSdkVersion + } + + lintOptions { + // Truth depends on JUnit, which depends on java.lang.management, which + // is not part of Android. Remove this when JUnit 4.13 or later is used. + // See: https://github.com/junit-team/junit4/pull/1187. + disable 'InvalidPackage' + } +} + +dependencies { + compile project(modulePrefix + 'testutils') + compile 'org.robolectric:robolectric:' + robolectricVersion +} diff --git a/testutils_robolectric/src/main/AndroidManifest.xml b/testutils_robolectric/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..057caad8673 --- /dev/null +++ b/testutils_robolectric/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java similarity index 99% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java index f9ababe3893..6d3b15ac7ad 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java @@ -29,9 +29,7 @@ import java.io.IOException; import java.util.ArrayList; -/** - * Assertion methods for {@link Cache}. - */ +/** Assertion methods for {@link Cache}. */ public final class CacheAsserts { /** @@ -135,5 +133,4 @@ public static void assertCacheEmpty(Cache cache) { } private CacheAsserts() {} - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java similarity index 93% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java index 4d118f92883..009afd1ff7e 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java @@ -19,9 +19,7 @@ import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.util.MediaClock; -/** - * Fake abstract {@link Renderer} which is also a {@link MediaClock}. - */ +/** Fake abstract {@link Renderer} which is also a {@link MediaClock}. */ public abstract class FakeMediaClockRenderer extends FakeRenderer implements MediaClock { public FakeMediaClockRenderer(Format... expectedFormats) { @@ -32,5 +30,4 @@ public FakeMediaClockRenderer(Format... expectedFormats) { public MediaClock getMediaClock() { return this; } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java similarity index 100% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java similarity index 94% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java index f5f1987f31d..8ceb5338a6c 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java @@ -25,8 +25,8 @@ import java.util.List; /** - * A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number - * of calls to its methods. + * A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number of + * calls to its methods. */ public final class FakeTrackSelection implements TrackSelection { @@ -118,8 +118,8 @@ public void onPlaybackSpeed(float speed) { } @Override - public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, - long availableDurationUs) { + public void updateSelectedTrack( + long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) { assertThat(isEnabled).isTrue(); } @@ -134,5 +134,4 @@ public boolean blacklist(int index, long blacklistDurationMs) { assertThat(isEnabled).isTrue(); return false; } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java similarity index 83% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java index da9a1a18ade..2daafbbb0b1 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java @@ -25,9 +25,7 @@ import java.util.ArrayList; import java.util.List; -/** - * A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. - */ +/** A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. */ public class FakeTrackSelector extends MappingTrackSelector { private final List selectedTrackSelections = new ArrayList<>(); @@ -38,17 +36,19 @@ public FakeTrackSelector() { } /** - * @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse - * {@link TrackSelection}s during track selection, when it finds previously-selected track - * selection using the same {@link TrackGroup}. + * @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse {@link + * TrackSelection}s during track selection, when it finds previously-selected track selection + * using the same {@link TrackGroup}. */ public FakeTrackSelector(boolean mayReuseTrackSelection) { this.mayReuseTrackSelection = mayReuseTrackSelection; } @Override - protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities, - TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports) + protected TrackSelection[] selectTracks( + RendererCapabilities[] rendererCapabilities, + TrackGroupArray[] rendererTrackGroupArrays, + int[][][] rendererFormatSupports) throws ExoPlaybackException { List resultList = new ArrayList<>(); for (TrackGroupArray trackGroupArray : rendererTrackGroupArrays) { @@ -76,11 +76,8 @@ private FakeTrackSelection reuseOrCreateTrackSelection(TrackGroup trackGroup) { return trackSelectionForRenderer; } - /** - * Returns list of all {@link FakeTrackSelection}s that this track selector has made so far. - */ + /** Returns list of all {@link FakeTrackSelection}s that this track selector has made so far. */ public List getSelectedTrackSelections() { return selectedTrackSelections; } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java similarity index 85% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java index 16389112ca9..fbb48c95291 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java @@ -37,9 +37,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -/** - * A runner for {@link MediaSource} tests. - */ +/** A runner for {@link MediaSource} tests. */ public class MediaSourceTestRunner { public static final int TIMEOUT_MS = 10000; @@ -78,18 +76,19 @@ public MediaSourceTestRunner(MediaSource mediaSource, Allocator allocator) { public void runOnPlaybackThread(final Runnable runnable) { final Throwable[] throwable = new Throwable[1]; final ConditionVariable finishedCondition = new ConditionVariable(); - playbackHandler.post(new Runnable() { - @Override - public void run() { - try { - runnable.run(); - } catch (Throwable e) { - throwable[0] = e; - } finally { - finishedCondition.open(); - } - } - }); + playbackHandler.post( + new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } catch (Throwable e) { + throwable[0] = e; + } finally { + finishedCondition.open(); + } + } + }); assertThat(finishedCondition.block(TIMEOUT_MS)).isTrue(); if (throwable[0] != null) { Util.sneakyThrow(throwable[0]); @@ -103,20 +102,21 @@ public void run() { */ public Timeline prepareSource() throws IOException { final IOException[] prepareError = new IOException[1]; - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - mediaSource.prepareSource(player, true, mediaSourceListener); - try { - // TODO: This only catches errors that are set synchronously in prepareSource. To capture - // async errors we'll need to poll maybeThrowSourceInfoRefreshError until the first call - // to onSourceInfoRefreshed. - mediaSource.maybeThrowSourceInfoRefreshError(); - } catch (IOException e) { - prepareError[0] = e; - } - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + mediaSource.prepareSource(player, true, mediaSourceListener); + try { + // TODO: This only catches errors that are set synchronously in prepareSource. To + // capture async errors we'll need to poll maybeThrowSourceInfoRefreshError until the + // first call to onSourceInfoRefreshed. + mediaSource.maybeThrowSourceInfoRefreshError(); + } catch (IOException e) { + prepareError[0] = e; + } + } + }); if (prepareError[0] != null) { throw prepareError[0]; } @@ -132,12 +132,13 @@ public void run() { */ public MediaPeriod createPeriod(final MediaPeriodId periodId) { final MediaPeriod[] holder = new MediaPeriod[1]; - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - holder[0] = mediaSource.createPeriod(periodId, allocator); - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + holder[0] = mediaSource.createPeriod(periodId, allocator); + } + }); assertThat(holder[0]).isNotNull(); return holder[0]; } @@ -183,24 +184,24 @@ public void onContinueLoadingRequested(MediaPeriod source) { * @param mediaPeriod The {@link MediaPeriod} to release. */ public void releasePeriod(final MediaPeriod mediaPeriod) { - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - mediaSource.releasePeriod(mediaPeriod); - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + mediaSource.releasePeriod(mediaPeriod); + } + }); } - /** - * Calls {@link MediaSource#releaseSource()} on the playback thread. - */ + /** Calls {@link MediaSource#releaseSource()} on the playback thread. */ public void releaseSource() { - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - mediaSource.releaseSource(); - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + mediaSource.releaseSource(); + } + }); } /** @@ -276,9 +277,7 @@ private void assertPrepareAndReleasePeriod(MediaPeriodId mediaPeriodId) releasePeriod(secondMediaPeriod); } - /** - * Releases the runner. Should be called when the runner is no longer required. - */ + /** Releases the runner. Should be called when the runner is no longer required. */ public void release() { playbackThread.quit(); } @@ -290,7 +289,6 @@ public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object Assertions.checkState(Looper.myLooper() == playbackThread.getLooper()); timelines.addLast(timeline); } - } private static class EventHandlingExoPlayer extends StubExoPlayer @@ -326,5 +324,4 @@ public boolean handleMessage(Message msg) { return true; } } - } diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java new file mode 100644 index 00000000000..8dd0cd16b17 --- /dev/null +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java @@ -0,0 +1,1070 @@ +/* + * 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.testutil; + +/** Provides ogg/vorbis test data in bytes for unit tests. */ +public 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); + } + + /** + * Returns the initial two pages of bytes which by spec contain the three vorbis header packets: + * identification, comment and setup header. + */ + public static byte[] getVorbisHeaderPages() { + byte[] data = new byte[VORBIS_HEADER_PAGES.length]; + System.arraycopy(VORBIS_HEADER_PAGES, 0, data, 0, VORBIS_HEADER_PAGES.length); + return data; + } + + /** Returns a valid vorbis identification header in bytes. */ + public static byte[] getIdentificationHeaderData() { + int idHeaderStart = 28; + int idHeaderLength = 30; + byte[] idHeaderData = new byte[idHeaderLength]; + System.arraycopy(VORBIS_HEADER_PAGES, idHeaderStart, idHeaderData, 0, idHeaderLength); + return idHeaderData; + } + + /** Returns a valid vorbis comment header with 3 comments including utf8 chars in bytes. */ + public static byte[] getCommentHeaderDataUTF8() { + byte[] commentHeaderData = new byte[COMMENT_HEADER_WITH_UTF8.length]; + System.arraycopy( + COMMENT_HEADER_WITH_UTF8, 0, commentHeaderData, 0, COMMENT_HEADER_WITH_UTF8.length); + return commentHeaderData; + } + + /** Returns a valid vorbis setup header in bytes. */ + public static byte[] getSetupHeaderData() { + int setupHeaderStart = 146; + int setupHeaderLength = VORBIS_HEADER_PAGES.length - setupHeaderStart; + byte[] setupHeaderData = new byte[setupHeaderLength]; + System.arraycopy(VORBIS_HEADER_PAGES, setupHeaderStart, setupHeaderData, 0, setupHeaderLength); + return setupHeaderData; + } + + private static final byte[] COMMENT_HEADER_WITH_UTF8 = { + (byte) 0x03, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 3, v, o, r, + (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x2b, // b, i, s, . + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x58, + (byte) 0x69, (byte) 0x70, (byte) 0x68, (byte) 0x2e, + (byte) 0x4f, (byte) 0x72, (byte) 0x67, (byte) 0x20, + (byte) 0x6c, (byte) 0x69, (byte) 0x62, (byte) 0x56, + (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, + (byte) 0x73, (byte) 0x20, (byte) 0x49, (byte) 0x20, + (byte) 0x32, (byte) 0x30, (byte) 0x31, (byte) 0x32, + (byte) 0x30, (byte) 0x32, (byte) 0x30, (byte) 0x33, + (byte) 0x20, (byte) 0x28, (byte) 0x4f, (byte) 0x6d, + (byte) 0x6e, (byte) 0x69, (byte) 0x70, (byte) 0x72, + (byte) 0x65, (byte) 0x73, (byte) 0x65, (byte) 0x6e, + (byte) 0x74, (byte) 0x29, (byte) 0x03, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x4c, + (byte) 0x42, (byte) 0x55, (byte) 0x4d, (byte) 0x3d, + (byte) 0xc3, (byte) 0xa4, (byte) 0xc3, (byte) 0xb6, + (byte) 0x13, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x54, (byte) 0x49, (byte) 0x54, (byte) 0x4c, + (byte) 0x45, (byte) 0x3d, (byte) 0x41, (byte) 0x20, + (byte) 0x73, (byte) 0x61, (byte) 0x6d, (byte) 0x70, + (byte) 0x6c, (byte) 0x65, (byte) 0x20, (byte) 0x73, + (byte) 0x6f, (byte) 0x6e, (byte) 0x67, (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x41, + (byte) 0x52, (byte) 0x54, (byte) 0x49, (byte) 0x53, + (byte) 0x54, (byte) 0x3d, (byte) 0x47, (byte) 0x6f, + (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, + (byte) 0x01 + }; + + // two OGG pages with 3 packets (id, comment and setup header) + // length: 3743 bytes + private static final byte[] VORBIS_HEADER_PAGES = { /* capture pattern ogg header 1 */ + (byte) 0x4f, (byte) 0x67, (byte) 0x67, (byte) 0x53, // O,g,g,S : start pos 0 + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x5e, (byte) 0x5f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x83, (byte) 0x36, + (byte) 0xe3, (byte) 0x49, (byte) 0x01, (byte) 0x1e, /* capture pattern vorbis id header */ + (byte) 0x01, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 1,v,o,r : start pos 28 + (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x00, // b,i,s,. + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x22, (byte) 0x56, (byte) 0x00, (byte) 0x00, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x6a, (byte) 0x04, (byte) 0x01, (byte) 0x00, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern ogg header 2 */ + (byte) 0xa9, (byte) 0x01, (byte) 0x4f, (byte) 0x67, // .,.,O,g : start pos 86 + (byte) 0x67, (byte) 0x53, (byte) 0x00, (byte) 0x00, // g,S,.,. + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x5e, (byte) 0x5f, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x69, (byte) 0xf8, (byte) 0xeb, (byte) 0xe1, + (byte) 0x10, (byte) 0x2d, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern vorbis comment header*/ + (byte) 0x1b, (byte) 0x03, (byte) 0x76, (byte) 0x6f, // .,3,v,o : start pos 101 + (byte) 0x72, (byte) 0x62, (byte) 0x69, (byte) 0x73, // r,b,i,s + (byte) 0x1d, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x58, (byte) 0x69, (byte) 0x70, (byte) 0x68, + (byte) 0x2e, (byte) 0x4f, (byte) 0x72, (byte) 0x67, + (byte) 0x20, (byte) 0x6c, (byte) 0x69, (byte) 0x62, + (byte) 0x56, (byte) 0x6f, (byte) 0x72, (byte) 0x62, + (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x49, + (byte) 0x20, (byte) 0x32, (byte) 0x30, (byte) 0x30, + (byte) 0x33, (byte) 0x30, (byte) 0x39, (byte) 0x30, + (byte) 0x39, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* capture pattern vorbis setup header */ + (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x76, // .,.,5,v : start pos 146 + (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, // o,r,b,i + (byte) 0x73, (byte) 0x22, (byte) 0x42, (byte) 0x43, // s,. + (byte) 0x56, (byte) 0x01, (byte) 0x00, (byte) 0x40, + (byte) 0x00, (byte) 0x00, (byte) 0x18, (byte) 0x42, + (byte) 0x10, (byte) 0x2a, (byte) 0x05, (byte) 0xad, + (byte) 0x63, (byte) 0x8e, (byte) 0x3a, (byte) 0xc8, + (byte) 0x15, (byte) 0x21, (byte) 0x8c, (byte) 0x19, + (byte) 0xa2, (byte) 0xa0, (byte) 0x42, (byte) 0xca, + (byte) 0x29, (byte) 0xc7, (byte) 0x1d, (byte) 0x42, + (byte) 0xd0, (byte) 0x21, (byte) 0xa3, (byte) 0x24, + (byte) 0x43, (byte) 0x88, (byte) 0x3a, (byte) 0xc6, + (byte) 0x35, (byte) 0xc7, (byte) 0x18, (byte) 0x63, + (byte) 0x47, (byte) 0xb9, (byte) 0x64, (byte) 0x8a, + (byte) 0x42, (byte) 0xc9, (byte) 0x81, (byte) 0xd0, + (byte) 0x90, (byte) 0x55, (byte) 0x00, (byte) 0x00, + (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0xa4, + (byte) 0x1c, (byte) 0x57, (byte) 0x50, (byte) 0x72, + (byte) 0x49, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xa3, (byte) 0x18, (byte) 0x57, + (byte) 0xcc, (byte) 0x71, (byte) 0xe8, (byte) 0x20, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe5, + (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, + (byte) 0x09, (byte) 0x25, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x8e, (byte) 0x39, (byte) 0xe7, + (byte) 0x92, (byte) 0x72, (byte) 0x8e, (byte) 0x31, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa3, + (byte) 0x18, (byte) 0x57, (byte) 0x0e, (byte) 0x72, + (byte) 0x29, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x81, (byte) 0x14, (byte) 0x47, + (byte) 0x8a, (byte) 0x71, (byte) 0xa7, (byte) 0x18, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa4, + (byte) 0x1c, (byte) 0x47, (byte) 0x8a, (byte) 0x71, + (byte) 0xa8, (byte) 0x18, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x6d, (byte) 0x31, (byte) 0xb7, + (byte) 0x92, (byte) 0x72, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe6, + (byte) 0x20, (byte) 0x87, (byte) 0x52, (byte) 0x72, + (byte) 0xae, (byte) 0x35, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xa4, (byte) 0x18, (byte) 0x67, + (byte) 0x0e, (byte) 0x72, (byte) 0x0b, (byte) 0x25, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xc6, + (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, + (byte) 0xeb, (byte) 0x20, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x8c, (byte) 0x35, (byte) 0xb7, + (byte) 0xd4, (byte) 0x72, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x8c, (byte) 0x31, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x6e, + (byte) 0x31, (byte) 0xe7, (byte) 0x16, (byte) 0x73, + (byte) 0xae, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x1c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x20, + (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, + (byte) 0x90, (byte) 0x00, (byte) 0x00, (byte) 0xa0, + (byte) 0xa1, (byte) 0x28, (byte) 0x8a, (byte) 0xe2, + (byte) 0x28, (byte) 0x0e, (byte) 0x10, (byte) 0x1a, + (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0xc8, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x40, + (byte) 0x71, (byte) 0x14, (byte) 0x47, (byte) 0x91, + (byte) 0x14, (byte) 0x4b, (byte) 0xb1, (byte) 0x1c, + (byte) 0xcb, (byte) 0xd1, (byte) 0x24, (byte) 0x0d, + (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, + (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0xa0, + (byte) 0x48, (byte) 0x86, (byte) 0xa4, (byte) 0x48, + (byte) 0x8a, (byte) 0xa5, (byte) 0x58, (byte) 0x8e, + (byte) 0x66, (byte) 0x69, (byte) 0x9e, (byte) 0x26, + (byte) 0x7a, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, + (byte) 0xa2, (byte) 0x2a, (byte) 0xab, (byte) 0xb2, + (byte) 0x69, (byte) 0xca, (byte) 0xb2, (byte) 0x2c, + (byte) 0xcb, (byte) 0xb2, (byte) 0xeb, (byte) 0xba, + (byte) 0x2e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, + (byte) 0x0a, (byte) 0x00, (byte) 0x48, (byte) 0x00, + (byte) 0x00, (byte) 0x50, (byte) 0x51, (byte) 0x14, + (byte) 0xc5, (byte) 0x70, (byte) 0x14, (byte) 0x07, + (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, + (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x00, + (byte) 0x08, (byte) 0x60, (byte) 0x28, (byte) 0x8a, + (byte) 0xa3, (byte) 0x38, (byte) 0x8e, (byte) 0xe4, + (byte) 0x58, (byte) 0x92, (byte) 0xa5, (byte) 0x59, + (byte) 0x9e, (byte) 0x07, (byte) 0x84, (byte) 0x86, + (byte) 0xac, (byte) 0x02, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x00, + (byte) 0x00, (byte) 0x50, (byte) 0x0c, (byte) 0x47, + (byte) 0xb1, (byte) 0x14, (byte) 0x4d, (byte) 0xf1, + (byte) 0x24, (byte) 0xcf, (byte) 0xf2, (byte) 0x3c, + (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, + (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, + (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, + (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, + (byte) 0xf3, (byte) 0x3c, (byte) 0x0d, (byte) 0x08, + (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x82, (byte) 0x28, (byte) 0x64, (byte) 0x18, + (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, + (byte) 0x01, (byte) 0x00, (byte) 0x40, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0x21, (byte) 0x1a, + (byte) 0x19, (byte) 0x43, (byte) 0x9d, (byte) 0x52, + (byte) 0x12, (byte) 0x5c, (byte) 0x0a, (byte) 0x16, + (byte) 0x42, (byte) 0x1c, (byte) 0x11, (byte) 0x43, + (byte) 0x1d, (byte) 0x42, (byte) 0xce, (byte) 0x43, + (byte) 0xa9, (byte) 0xa5, (byte) 0x83, (byte) 0xe0, + (byte) 0x29, (byte) 0x85, (byte) 0x25, (byte) 0x63, + (byte) 0xd2, (byte) 0x53, (byte) 0xac, (byte) 0x41, + (byte) 0x08, (byte) 0x21, (byte) 0x7c, (byte) 0xef, + (byte) 0x3d, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, + (byte) 0xef, (byte) 0x81, (byte) 0xd0, (byte) 0x90, + (byte) 0x55, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x61, (byte) 0x14, + (byte) 0x38, (byte) 0x88, (byte) 0x81, (byte) 0xc7, + (byte) 0x24, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x62, (byte) 0x14, (byte) 0x27, (byte) 0x44, + (byte) 0x71, (byte) 0xa6, (byte) 0x20, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0xe5, (byte) 0x24, + (byte) 0x58, (byte) 0xca, (byte) 0x79, (byte) 0xe8, + (byte) 0x24, (byte) 0x08, (byte) 0xdd, (byte) 0x83, + (byte) 0x10, (byte) 0x42, (byte) 0xb8, (byte) 0x9c, + (byte) 0x7b, (byte) 0xcb, (byte) 0xb9, (byte) 0xf7, + (byte) 0xde, (byte) 0x7b, (byte) 0x20, (byte) 0x34, + (byte) 0x64, (byte) 0x15, (byte) 0x00, (byte) 0x00, + (byte) 0x08, (byte) 0x00, (byte) 0xc0, (byte) 0x20, + (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, + (byte) 0x08, (byte) 0x29, (byte) 0xa4, (byte) 0x94, + (byte) 0x52, (byte) 0x48, (byte) 0x29, (byte) 0xa6, + (byte) 0x98, (byte) 0x62, (byte) 0x8a, (byte) 0x29, + (byte) 0xc7, (byte) 0x1c, (byte) 0x73, (byte) 0xcc, + (byte) 0x31, (byte) 0xc7, (byte) 0x20, (byte) 0x83, + (byte) 0x0c, (byte) 0x32, (byte) 0xe8, (byte) 0xa0, + (byte) 0x93, (byte) 0x4e, (byte) 0x3a, (byte) 0xc9, + (byte) 0xa4, (byte) 0x92, (byte) 0x4e, (byte) 0x3a, + (byte) 0xca, (byte) 0x24, (byte) 0xa3, (byte) 0x8e, + (byte) 0x52, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, + (byte) 0x14, (byte) 0x53, (byte) 0x4c, (byte) 0xb1, + (byte) 0xe5, (byte) 0x16, (byte) 0x63, (byte) 0xad, + (byte) 0xb5, (byte) 0xd6, (byte) 0x9c, (byte) 0x73, + (byte) 0xaf, (byte) 0x41, (byte) 0x29, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x23, (byte) 0x08, + (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x06, (byte) 0x19, (byte) 0x64, (byte) 0x90, + (byte) 0x41, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x14, (byte) 0x52, (byte) 0x48, (byte) 0x29, + (byte) 0xa6, (byte) 0x98, (byte) 0x72, (byte) 0xcc, + (byte) 0x31, (byte) 0xc7, (byte) 0x1c, (byte) 0x03, + (byte) 0x42, (byte) 0x43, (byte) 0x56, (byte) 0x01, + (byte) 0x00, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x1c, (byte) 0x45, (byte) 0x52, (byte) 0x24, + (byte) 0x47, (byte) 0x72, (byte) 0x24, (byte) 0x47, + (byte) 0x92, (byte) 0x24, (byte) 0xc9, (byte) 0x92, + (byte) 0x2c, (byte) 0x49, (byte) 0x93, (byte) 0x3c, + (byte) 0xcb, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, + (byte) 0xb3, (byte) 0x3c, (byte) 0x4d, (byte) 0xd4, + (byte) 0x44, (byte) 0x4d, (byte) 0x15, (byte) 0x55, + (byte) 0xd5, (byte) 0x55, (byte) 0x6d, (byte) 0xd7, + (byte) 0xf6, (byte) 0x6d, (byte) 0x5f, (byte) 0xf6, + (byte) 0x6d, (byte) 0xdf, (byte) 0xd5, (byte) 0x65, + (byte) 0xdf, (byte) 0xf6, (byte) 0x65, (byte) 0xdb, + (byte) 0xd5, (byte) 0x65, (byte) 0x5d, (byte) 0x96, + (byte) 0x65, (byte) 0xdd, (byte) 0xb5, (byte) 0x6d, + (byte) 0x5d, (byte) 0xd6, (byte) 0x5d, (byte) 0x5d, + (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, + (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x75, + (byte) 0x5d, (byte) 0xd7, (byte) 0x75, (byte) 0x5d, + (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, + (byte) 0x81, (byte) 0xd0, (byte) 0x90, (byte) 0x55, + (byte) 0x00, (byte) 0x80, (byte) 0x04, (byte) 0x00, + (byte) 0x80, (byte) 0x8e, (byte) 0xe4, (byte) 0x38, + (byte) 0x8e, (byte) 0xe4, (byte) 0x38, (byte) 0x8e, + (byte) 0xe4, (byte) 0x48, (byte) 0x8e, (byte) 0xa4, + (byte) 0x48, (byte) 0x0a, (byte) 0x10, (byte) 0x1a, + (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0x90, + (byte) 0x01, (byte) 0x00, (byte) 0x10, (byte) 0x00, + (byte) 0x80, (byte) 0xa3, (byte) 0x38, (byte) 0x8a, + (byte) 0xe3, (byte) 0x48, (byte) 0x8e, (byte) 0xe4, + (byte) 0x58, (byte) 0x8e, (byte) 0x25, (byte) 0x59, + (byte) 0x92, (byte) 0x26, (byte) 0x69, (byte) 0x96, + (byte) 0x67, (byte) 0x79, (byte) 0x96, (byte) 0xa7, + (byte) 0x79, (byte) 0x9a, (byte) 0xa8, (byte) 0x89, + (byte) 0x1e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, + (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x04, + (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xa2, (byte) 0x28, (byte) 0x8a, (byte) 0xa3, + (byte) 0x38, (byte) 0x8e, (byte) 0x24, (byte) 0x59, + (byte) 0x96, (byte) 0xa6, (byte) 0x69, (byte) 0x9e, + (byte) 0xa7, (byte) 0x7a, (byte) 0xa2, (byte) 0x28, + (byte) 0x9a, (byte) 0xaa, (byte) 0xaa, (byte) 0x8a, + (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, (byte) 0xaa, + (byte) 0x6a, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, + (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, + (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0x02, (byte) 0xa1, + (byte) 0x21, (byte) 0xab, (byte) 0x00, (byte) 0x00, + (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x1d, + (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0x47, + (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, + (byte) 0x24, (byte) 0x47, (byte) 0x92, (byte) 0x24, + (byte) 0x20, (byte) 0x34, (byte) 0x64, (byte) 0x15, + (byte) 0x00, (byte) 0x20, (byte) 0x03, (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x43, + (byte) 0x51, (byte) 0x1c, (byte) 0x45, (byte) 0x72, + (byte) 0x2c, (byte) 0xc7, (byte) 0x92, (byte) 0x34, + (byte) 0x4b, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, + (byte) 0xd3, (byte) 0x44, (byte) 0xcf, (byte) 0xf4, + (byte) 0x5c, (byte) 0x51, (byte) 0x36, (byte) 0x75, + (byte) 0x53, (byte) 0x57, (byte) 0x6d, (byte) 0x20, + (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x20, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xc7, (byte) 0x73, + (byte) 0x3c, (byte) 0xc7, (byte) 0x73, (byte) 0x3c, + (byte) 0xc9, (byte) 0x93, (byte) 0x3c, (byte) 0xcb, + (byte) 0x73, (byte) 0x3c, (byte) 0xc7, (byte) 0x93, + (byte) 0x3c, (byte) 0x49, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, + (byte) 0x02, (byte) 0x00, (byte) 0x64, (byte) 0x00, + (byte) 0x00, (byte) 0x90, (byte) 0x02, (byte) 0xcf, + (byte) 0x42, (byte) 0x29, (byte) 0x2d, (byte) 0x46, + (byte) 0x02, (byte) 0x1c, (byte) 0x88, (byte) 0x98, + (byte) 0xa3, (byte) 0xd8, (byte) 0x7b, (byte) 0xef, + (byte) 0xbd, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, + (byte) 0x65, (byte) 0x3c, (byte) 0x92, (byte) 0x88, + (byte) 0x49, (byte) 0xed, (byte) 0x31, (byte) 0xf4, + (byte) 0xd4, (byte) 0x31, (byte) 0x07, (byte) 0xb1, + (byte) 0x67, (byte) 0xc6, (byte) 0x23, (byte) 0x66, + (byte) 0x94, (byte) 0xa3, (byte) 0xd8, (byte) 0x29, + (byte) 0xcf, (byte) 0x1c, (byte) 0x42, (byte) 0x0c, + (byte) 0x62, (byte) 0xe8, (byte) 0x3c, (byte) 0x74, + (byte) 0x4a, (byte) 0x31, (byte) 0x88, (byte) 0x29, + (byte) 0xf5, (byte) 0x52, (byte) 0x32, (byte) 0xc6, + (byte) 0x20, (byte) 0xc6, (byte) 0xd8, (byte) 0x63, + (byte) 0x0c, (byte) 0x21, (byte) 0x94, (byte) 0x18, + (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x21, + (byte) 0x00, (byte) 0x84, (byte) 0x66, (byte) 0x00, + (byte) 0x18, (byte) 0x24, (byte) 0x09, (byte) 0x90, + (byte) 0x34, (byte) 0x0d, (byte) 0x90, (byte) 0x34, + (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x24, (byte) 0x4f, (byte) 0x03, (byte) 0x34, + (byte) 0x51, (byte) 0x04, (byte) 0x34, (byte) 0x4f, + (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x49, (byte) 0xf3, (byte) 0x00, (byte) 0x4d, + (byte) 0xf4, (byte) 0x00, (byte) 0x4d, (byte) 0x14, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x90, (byte) 0x3c, (byte) 0x0d, (byte) 0xf0, + (byte) 0x44, (byte) 0x11, (byte) 0xd0, (byte) 0x44, + (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, + (byte) 0x51, (byte) 0x05, (byte) 0x44, (byte) 0xd5, + (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0x4f, + (byte) 0x15, (byte) 0x01, (byte) 0xd1, (byte) 0x54, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x90, (byte) 0x34, (byte) 0x0f, (byte) 0xd0, + (byte) 0x44, (byte) 0x11, (byte) 0xf0, (byte) 0x44, + (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, + (byte) 0xd5, (byte) 0x04, (byte) 0x3c, (byte) 0x51, + (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0xd1, + (byte) 0x54, (byte) 0x01, (byte) 0x51, (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x38, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x58, + (byte) 0x08, (byte) 0x85, (byte) 0x86, (byte) 0xac, + (byte) 0x08, (byte) 0x00, (byte) 0xe2, (byte) 0x04, + (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x30, (byte) 0xe0, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x60, + (byte) 0x42, (byte) 0x19, (byte) 0x28, (byte) 0x34, + (byte) 0x64, (byte) 0x45, (byte) 0x00, (byte) 0x10, + (byte) 0x27, (byte) 0x00, (byte) 0x60, (byte) 0x70, + (byte) 0x1c, (byte) 0xcb, (byte) 0x02, (byte) 0x00, + (byte) 0x00, (byte) 0x47, (byte) 0x92, (byte) 0x34, + (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x1c, + (byte) 0x49, (byte) 0xd2, (byte) 0x34, (byte) 0x00, + (byte) 0x00, (byte) 0xd0, (byte) 0x34, (byte) 0x4d, + (byte) 0x14, (byte) 0x01, (byte) 0x00, (byte) 0xc0, + (byte) 0xd2, (byte) 0x34, (byte) 0x51, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x30, + (byte) 0xe0, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x60, (byte) 0x42, (byte) 0x19, (byte) 0x28, + (byte) 0x34, (byte) 0x64, (byte) 0x25, (byte) 0x00, + (byte) 0x10, (byte) 0x05, (byte) 0x00, (byte) 0x60, + (byte) 0x30, (byte) 0x14, (byte) 0x4d, (byte) 0x03, + (byte) 0x58, (byte) 0x16, (byte) 0xc0, (byte) 0xb2, + (byte) 0x00, (byte) 0x9a, (byte) 0x06, (byte) 0xd0, + (byte) 0x34, (byte) 0x80, (byte) 0xe7, (byte) 0x01, + (byte) 0x3c, (byte) 0x11, (byte) 0x60, (byte) 0x9a, + (byte) 0x00, (byte) 0x40, (byte) 0x00, (byte) 0x00, + (byte) 0x40, (byte) 0x81, (byte) 0x03, (byte) 0x00, + (byte) 0x40, (byte) 0x80, (byte) 0x0d, (byte) 0x9a, + (byte) 0x12, (byte) 0x8b, (byte) 0x03, (byte) 0x14, + (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, + (byte) 0x88, (byte) 0x02, (byte) 0x00, (byte) 0x30, + (byte) 0x28, (byte) 0x8a, (byte) 0x24, (byte) 0x59, + (byte) 0x96, (byte) 0xe7, (byte) 0x41, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0x14, (byte) 0xa1, + (byte) 0x69, (byte) 0x9a, (byte) 0x26, (byte) 0x8a, + (byte) 0xf0, (byte) 0x3c, (byte) 0xcf, (byte) 0x13, + (byte) 0x45, (byte) 0x78, (byte) 0x9e, (byte) 0xe7, + (byte) 0x99, (byte) 0x26, (byte) 0x44, (byte) 0xd1, + (byte) 0xf3, (byte) 0x4c, (byte) 0x13, (byte) 0xa2, + (byte) 0xe8, (byte) 0x79, (byte) 0xa6, (byte) 0x09, + (byte) 0xd3, (byte) 0x14, (byte) 0x45, (byte) 0xd3, + (byte) 0x04, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x0a, + (byte) 0x1c, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x6c, (byte) 0xd0, (byte) 0x94, (byte) 0x58, + (byte) 0x1c, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, + (byte) 0x95, (byte) 0x00, (byte) 0x40, (byte) 0x48, + (byte) 0x00, (byte) 0x80, (byte) 0x41, (byte) 0x51, + (byte) 0x2c, (byte) 0xcb, (byte) 0xf3, (byte) 0x44, + (byte) 0x51, (byte) 0x14, (byte) 0x4d, (byte) 0x53, + (byte) 0x55, (byte) 0x5d, (byte) 0x17, (byte) 0x9a, + (byte) 0xe6, (byte) 0x79, (byte) 0xa2, (byte) 0x28, + (byte) 0x8a, (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, + (byte) 0xae, (byte) 0x0b, (byte) 0x4d, (byte) 0xf3, + (byte) 0x3c, (byte) 0x51, (byte) 0x14, (byte) 0x45, + (byte) 0xd3, (byte) 0x54, (byte) 0x55, (byte) 0xd7, + (byte) 0x85, (byte) 0xe7, (byte) 0x79, (byte) 0xa2, + (byte) 0x29, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0xaa, (byte) 0xaa, (byte) 0xeb, (byte) 0xc2, + (byte) 0xf3, (byte) 0x44, (byte) 0xd1, (byte) 0x34, + (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x55, + (byte) 0xd7, (byte) 0x75, (byte) 0xe1, (byte) 0x79, + (byte) 0xa2, (byte) 0x68, (byte) 0x9a, (byte) 0xa6, + (byte) 0xa9, (byte) 0xaa, (byte) 0xae, (byte) 0xeb, + (byte) 0xba, (byte) 0xf0, (byte) 0x3c, (byte) 0x51, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x54, + (byte) 0x55, (byte) 0xd7, (byte) 0x95, (byte) 0x65, + (byte) 0x88, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0xaa, (byte) 0xaa, + (byte) 0xeb, (byte) 0xca, (byte) 0x32, (byte) 0x10, + (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x59, + (byte) 0x06, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, + (byte) 0xaa, (byte) 0xea, (byte) 0xba, (byte) 0xae, + (byte) 0x2b, (byte) 0xcb, (byte) 0x40, (byte) 0x14, + (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x5d, + (byte) 0xd7, (byte) 0x75, (byte) 0x65, (byte) 0x19, + (byte) 0x98, (byte) 0xa6, (byte) 0x6a, (byte) 0xaa, + (byte) 0xaa, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, + (byte) 0x2c, (byte) 0x03, (byte) 0x4c, (byte) 0x53, + (byte) 0x55, (byte) 0x5d, (byte) 0x57, (byte) 0x96, + (byte) 0x65, (byte) 0x19, (byte) 0xa0, (byte) 0xaa, + (byte) 0xae, (byte) 0xeb, (byte) 0xba, (byte) 0xb2, + (byte) 0x6c, (byte) 0xdb, (byte) 0x00, (byte) 0x55, + (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x95, + (byte) 0x65, (byte) 0xdb, (byte) 0x06, (byte) 0xb8, + (byte) 0xae, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, + (byte) 0x2c, (byte) 0xdb, (byte) 0x36, (byte) 0x00, + (byte) 0xd7, (byte) 0x95, (byte) 0x65, (byte) 0x59, + (byte) 0xb6, (byte) 0x6d, (byte) 0x01, (byte) 0x00, + (byte) 0x00, (byte) 0x07, (byte) 0x0e, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x46, (byte) 0xd0, + (byte) 0x49, (byte) 0x46, (byte) 0x95, (byte) 0x45, + (byte) 0xd8, (byte) 0x68, (byte) 0xc2, (byte) 0x85, + (byte) 0x07, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, + (byte) 0x15, (byte) 0x01, (byte) 0x40, (byte) 0x14, + (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x8c, + (byte) 0x52, (byte) 0x8a, (byte) 0x29, (byte) 0x65, + (byte) 0x18, (byte) 0x93, (byte) 0x50, (byte) 0x4a, + (byte) 0x09, (byte) 0x0d, (byte) 0x63, (byte) 0x52, + (byte) 0x4a, (byte) 0x2a, (byte) 0xa5, (byte) 0x92, + (byte) 0x92, (byte) 0x52, (byte) 0x4a, (byte) 0xa5, + (byte) 0x54, (byte) 0x12, (byte) 0x52, (byte) 0x4a, + (byte) 0xa9, (byte) 0x94, (byte) 0x4a, (byte) 0x4a, + (byte) 0x4a, (byte) 0x29, (byte) 0x95, (byte) 0x92, + (byte) 0x51, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, + (byte) 0x96, (byte) 0x2a, (byte) 0x29, (byte) 0xa9, + (byte) 0x94, (byte) 0x94, (byte) 0x52, (byte) 0x25, + (byte) 0xa5, (byte) 0xa4, (byte) 0x92, (byte) 0x52, + (byte) 0x2a, (byte) 0x00, (byte) 0x00, (byte) 0xec, + (byte) 0xc0, (byte) 0x01, (byte) 0x00, (byte) 0xec, + (byte) 0xc0, (byte) 0x42, (byte) 0x28, (byte) 0x34, + (byte) 0x64, (byte) 0x25, (byte) 0x00, (byte) 0x90, + (byte) 0x07, (byte) 0x00, (byte) 0x40, (byte) 0x10, + (byte) 0x82, (byte) 0x14, (byte) 0x63, (byte) 0x8c, + (byte) 0x39, (byte) 0x27, (byte) 0xa5, (byte) 0x54, + (byte) 0x8a, (byte) 0x31, (byte) 0xe7, (byte) 0x9c, + (byte) 0x93, (byte) 0x52, (byte) 0x2a, (byte) 0xc5, + (byte) 0x98, (byte) 0x73, (byte) 0xce, (byte) 0x49, + (byte) 0x29, (byte) 0x19, (byte) 0x63, (byte) 0xcc, + (byte) 0x39, (byte) 0xe7, (byte) 0xa4, (byte) 0x94, + (byte) 0x8c, (byte) 0x31, (byte) 0xe6, (byte) 0x9c, + (byte) 0x73, (byte) 0x52, (byte) 0x4a, (byte) 0xc6, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0x29, (byte) 0x25, (byte) 0x63, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x94, + (byte) 0xd2, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x83, (byte) 0x50, (byte) 0x4a, (byte) 0x29, + (byte) 0xa5, (byte) 0x73, (byte) 0xce, (byte) 0x41, + (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x12, + (byte) 0x42, (byte) 0xe7, (byte) 0x20, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0xe9, (byte) 0x9c, + (byte) 0x73, (byte) 0x10, (byte) 0x0a, (byte) 0x00, + (byte) 0x00, (byte) 0x2a, (byte) 0x70, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0xb0, (byte) 0x51, + (byte) 0x64, (byte) 0x73, (byte) 0x82, (byte) 0x91, + (byte) 0xa0, (byte) 0x42, (byte) 0x43, (byte) 0x56, + (byte) 0x02, (byte) 0x00, (byte) 0xa9, (byte) 0x00, + (byte) 0x00, (byte) 0x06, (byte) 0xc7, (byte) 0xb1, + (byte) 0x2c, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0xcf, (byte) 0x13, (byte) 0x45, (byte) 0x4b, + (byte) 0x92, (byte) 0x34, (byte) 0xcf, (byte) 0x13, + (byte) 0x3d, (byte) 0x4f, (byte) 0x14, (byte) 0x4d, + (byte) 0xd5, (byte) 0x92, (byte) 0x24, (byte) 0xcf, + (byte) 0x13, (byte) 0x45, (byte) 0xcf, (byte) 0x13, + (byte) 0x4d, (byte) 0x53, (byte) 0xe5, (byte) 0x79, + (byte) 0x9e, (byte) 0x28, (byte) 0x8a, (byte) 0xa2, + (byte) 0x68, (byte) 0x9a, (byte) 0xaa, (byte) 0x4a, + (byte) 0x14, (byte) 0x45, (byte) 0x4f, (byte) 0x14, + (byte) 0x45, (byte) 0xd1, (byte) 0x34, (byte) 0x55, + (byte) 0x95, (byte) 0x2c, (byte) 0x8b, (byte) 0xa2, + (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0xaa, + (byte) 0xba, (byte) 0x2e, (byte) 0x5b, (byte) 0x16, + (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x98, + (byte) 0xa6, (byte) 0x28, (byte) 0xaa, (byte) 0xaa, + (byte) 0xeb, (byte) 0xca, (byte) 0x2e, (byte) 0x4c, + (byte) 0x53, (byte) 0x14, (byte) 0x4d, (byte) 0xd3, + (byte) 0x75, (byte) 0x65, (byte) 0x19, (byte) 0xb2, + (byte) 0xad, (byte) 0x9a, (byte) 0xaa, (byte) 0xea, + (byte) 0xba, (byte) 0xb2, (byte) 0x0d, (byte) 0xdb, + (byte) 0x36, (byte) 0x4d, (byte) 0x55, (byte) 0x75, + (byte) 0x5d, (byte) 0x59, (byte) 0x06, (byte) 0xae, + (byte) 0xeb, (byte) 0xba, (byte) 0xb2, (byte) 0x6c, + (byte) 0xeb, (byte) 0xc0, (byte) 0x75, (byte) 0x5d, + (byte) 0x57, (byte) 0x96, (byte) 0x6d, (byte) 0x5d, + (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x82, + (byte) 0x03, (byte) 0x00, (byte) 0x50, (byte) 0x81, + (byte) 0x0d, (byte) 0xab, (byte) 0x23, (byte) 0x9c, + (byte) 0x14, (byte) 0x8d, (byte) 0x05, (byte) 0x16, + (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, + (byte) 0xc8, (byte) 0x00, (byte) 0x00, (byte) 0x20, + (byte) 0x08, (byte) 0x41, (byte) 0x48, (byte) 0x29, + (byte) 0x85, (byte) 0x90, (byte) 0x52, (byte) 0x0a, + (byte) 0x21, (byte) 0xa5, (byte) 0x14, (byte) 0x42, + (byte) 0x4a, (byte) 0x29, (byte) 0x84, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x0c, (byte) 0x38, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x98, + (byte) 0x50, (byte) 0x06, (byte) 0x0a, (byte) 0x0d, + (byte) 0x59, (byte) 0x09, (byte) 0x00, (byte) 0xa4, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, + (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, + (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, + (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, + (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, + (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, + (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, + (byte) 0x84, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0x02, (byte) 0x00, (byte) 0xb1, (byte) 0x2b, + (byte) 0x1c, (byte) 0x00, (byte) 0x76, (byte) 0x22, + (byte) 0x6c, (byte) 0x58, (byte) 0x1d, (byte) 0xe1, + (byte) 0xa4, (byte) 0x68, (byte) 0x2c, (byte) 0xb0, + (byte) 0xd0, (byte) 0x90, (byte) 0x95, (byte) 0x00, + (byte) 0x40, (byte) 0x38, (byte) 0x00, (byte) 0x00, + (byte) 0x60, (byte) 0x8c, (byte) 0x31, (byte) 0xce, + (byte) 0x59, (byte) 0xac, (byte) 0xb5, (byte) 0xd6, + (byte) 0x5a, (byte) 0x2b, (byte) 0xa5, (byte) 0x94, + (byte) 0x92, (byte) 0x50, (byte) 0x6b, (byte) 0xad, + (byte) 0xb5, (byte) 0xd6, (byte) 0x9a, (byte) 0x29, + (byte) 0xa4, (byte) 0x94, (byte) 0x84, (byte) 0x16, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x98, (byte) 0x31, (byte) 0x08, (byte) 0x29, + (byte) 0xb5, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xc6, (byte) 0x8c, (byte) 0x39, + (byte) 0x47, (byte) 0x2d, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xb6, + (byte) 0x56, (byte) 0x4a, (byte) 0x6c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0xb1, (byte) 0xb5, (byte) 0x52, (byte) 0x62, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x16, (byte) 0x5b, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x6c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, + (byte) 0x2c, (byte) 0x00, (byte) 0xc0, (byte) 0xe4, + (byte) 0xc1, (byte) 0x01, (byte) 0x00, (byte) 0x2a, + (byte) 0xc1, (byte) 0xc6, (byte) 0x19, (byte) 0x56, + (byte) 0x92, (byte) 0xce, (byte) 0x0a, (byte) 0x47, + (byte) 0x83, (byte) 0x0b, (byte) 0x0d, (byte) 0x59, + (byte) 0x09, (byte) 0x00, (byte) 0xe4, (byte) 0x06, + (byte) 0x00, (byte) 0x00, (byte) 0xc6, (byte) 0x28, + (byte) 0xc5, (byte) 0x98, (byte) 0x63, (byte) 0xce, + (byte) 0x41, (byte) 0x08, (byte) 0xa1, (byte) 0x94, + (byte) 0x12, (byte) 0x4a, (byte) 0x49, (byte) 0xad, + (byte) 0x75, (byte) 0xce, (byte) 0x39, (byte) 0x08, + (byte) 0x21, (byte) 0x94, (byte) 0x52, (byte) 0x4a, + (byte) 0x49, (byte) 0xa9, (byte) 0xb4, (byte) 0x94, + (byte) 0x62, (byte) 0xca, (byte) 0x98, (byte) 0x73, + (byte) 0xce, (byte) 0x41, (byte) 0x08, (byte) 0xa5, + (byte) 0x94, (byte) 0x12, (byte) 0x4a, (byte) 0x49, + (byte) 0xa9, (byte) 0xa5, (byte) 0xd4, (byte) 0x39, + (byte) 0xe7, (byte) 0x20, (byte) 0x94, (byte) 0x52, + (byte) 0x4a, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, + (byte) 0x94, (byte) 0x5a, (byte) 0x6a, (byte) 0xad, + (byte) 0x73, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4a, + (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0xd4, + (byte) 0x52, (byte) 0x08, (byte) 0x21, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0x2a, (byte) 0x29, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, + (byte) 0xad, (byte) 0xa5, (byte) 0x10, (byte) 0x42, + (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, + (byte) 0xd4, (byte) 0x5a, (byte) 0x8b, (byte) 0xa1, + (byte) 0x94, (byte) 0x90, (byte) 0x4a, (byte) 0x29, + (byte) 0x25, (byte) 0xa5, (byte) 0x94, (byte) 0x52, + (byte) 0x49, (byte) 0x2d, (byte) 0xb5, (byte) 0x96, + (byte) 0x5a, (byte) 0x2a, (byte) 0xa1, (byte) 0x94, + (byte) 0x54, (byte) 0x52, (byte) 0x4a, (byte) 0x29, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, + (byte) 0xa9, (byte) 0xb5, (byte) 0x56, (byte) 0x4a, + (byte) 0x49, (byte) 0x25, (byte) 0xa5, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, + (byte) 0xd4, (byte) 0x62, (byte) 0x6b, (byte) 0x29, + (byte) 0x94, (byte) 0x92, (byte) 0x52, (byte) 0x49, + (byte) 0x29, (byte) 0xb5, (byte) 0x94, (byte) 0x52, + (byte) 0x4a, (byte) 0xad, (byte) 0xc5, (byte) 0xd8, + (byte) 0x62, (byte) 0x29, (byte) 0xad, (byte) 0xa4, + (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0x29, + (byte) 0xa5, (byte) 0xd6, (byte) 0x52, (byte) 0x6c, + (byte) 0xad, (byte) 0xb5, (byte) 0xd8, (byte) 0x52, + (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0x96, + (byte) 0x5a, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, + (byte) 0x16, (byte) 0x5b, (byte) 0x6a, (byte) 0x2d, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4b, + (byte) 0x29, (byte) 0xa5, (byte) 0x96, (byte) 0x52, + (byte) 0x4b, (byte) 0x2d, (byte) 0xc6, (byte) 0xd6, + (byte) 0x5a, (byte) 0x4b, (byte) 0x29, (byte) 0xa5, + (byte) 0xd4, (byte) 0x52, (byte) 0x6a, (byte) 0xa9, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6c, + (byte) 0xad, (byte) 0xb5, (byte) 0x98, (byte) 0x52, + (byte) 0x6a, (byte) 0x2d, (byte) 0xa5, (byte) 0xd4, + (byte) 0x52, (byte) 0x6b, (byte) 0x2d, (byte) 0xb5, + (byte) 0xd8, (byte) 0x52, (byte) 0x6a, (byte) 0x2d, + (byte) 0xb5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, + (byte) 0xa9, (byte) 0xa5, (byte) 0x94, (byte) 0x5a, + (byte) 0x6b, (byte) 0x2d, (byte) 0xb6, (byte) 0xd8, + (byte) 0x5a, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, + (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0xa9, + (byte) 0xb5, (byte) 0x16, (byte) 0x5b, (byte) 0x8a, + (byte) 0xb1, (byte) 0xb5, (byte) 0xd4, (byte) 0x4a, + (byte) 0x4a, (byte) 0x29, (byte) 0xb5, (byte) 0xd4, + (byte) 0x5a, (byte) 0x6a, (byte) 0x2d, (byte) 0xb6, + (byte) 0x16, (byte) 0x5b, (byte) 0x6b, (byte) 0xad, + (byte) 0xa5, (byte) 0xd6, (byte) 0x5a, (byte) 0x6a, + (byte) 0x29, (byte) 0xa5, (byte) 0x16, (byte) 0x5b, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x16, + (byte) 0x63, (byte) 0x6b, (byte) 0x31, (byte) 0xa5, + (byte) 0x94, (byte) 0x52, (byte) 0x4b, (byte) 0xa9, + (byte) 0xa5, (byte) 0x02, (byte) 0x00, (byte) 0x80, + (byte) 0x0e, (byte) 0x1c, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x8c, (byte) 0xa8, (byte) 0xb4, + (byte) 0x10, (byte) 0x3b, (byte) 0xcd, (byte) 0xb8, + (byte) 0xf2, (byte) 0x08, (byte) 0x1c, (byte) 0x51, + (byte) 0xc8, (byte) 0x30, (byte) 0x01, (byte) 0x15, + (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, + (byte) 0x20, (byte) 0x03, (byte) 0x00, (byte) 0x20, + (byte) 0x90, (byte) 0x69, (byte) 0x92, (byte) 0x39, + (byte) 0x49, (byte) 0xa9, (byte) 0x11, (byte) 0x26, + (byte) 0x39, (byte) 0xc5, (byte) 0xa0, (byte) 0x94, + (byte) 0xe6, (byte) 0x9c, (byte) 0x53, (byte) 0x4a, + (byte) 0x29, (byte) 0xa5, (byte) 0x34, (byte) 0x44, + (byte) 0x96, (byte) 0x64, (byte) 0x90, (byte) 0x62, + (byte) 0x50, (byte) 0x1d, (byte) 0x99, (byte) 0x8c, + (byte) 0x39, (byte) 0x49, (byte) 0x39, (byte) 0x43, + (byte) 0xa4, (byte) 0x31, (byte) 0xa4, (byte) 0x20, + (byte) 0xf5, (byte) 0x4c, (byte) 0x91, (byte) 0xc7, + (byte) 0x94, (byte) 0x62, (byte) 0x10, (byte) 0x43, + (byte) 0x48, (byte) 0x2a, (byte) 0x74, (byte) 0x8a, + (byte) 0x39, (byte) 0x6c, (byte) 0x35, (byte) 0xf9, + (byte) 0x58, (byte) 0x42, (byte) 0x07, (byte) 0xb1, + (byte) 0x06, (byte) 0x65, (byte) 0x8c, (byte) 0x70, + (byte) 0x29, (byte) 0xc5, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0x02, (byte) 0x00, + (byte) 0x04, (byte) 0x84, (byte) 0x04, (byte) 0x00, + (byte) 0x18, (byte) 0x20, (byte) 0x28, (byte) 0x98, + (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x07, + (byte) 0x08, (byte) 0x23, (byte) 0x07, (byte) 0x02, + (byte) 0x1d, (byte) 0x01, (byte) 0x04, (byte) 0x0e, + (byte) 0x6d, (byte) 0x00, (byte) 0x80, (byte) 0x81, + (byte) 0x08, (byte) 0x99, (byte) 0x09, (byte) 0x0c, + (byte) 0x0a, (byte) 0xa1, (byte) 0xc1, (byte) 0x41, + (byte) 0x26, (byte) 0x00, (byte) 0x3c, (byte) 0x40, + (byte) 0x44, (byte) 0x48, (byte) 0x05, (byte) 0x00, + (byte) 0x89, (byte) 0x09, (byte) 0x8a, (byte) 0xd2, + (byte) 0x85, (byte) 0x2e, (byte) 0x08, (byte) 0x21, + (byte) 0x82, (byte) 0x74, (byte) 0x11, (byte) 0x64, + (byte) 0xf1, (byte) 0xc0, (byte) 0x85, (byte) 0x13, + (byte) 0x37, (byte) 0x9e, (byte) 0xb8, (byte) 0xe1, + (byte) 0x84, (byte) 0x0e, (byte) 0x6d, (byte) 0x20, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0xf0, + (byte) 0x01, (byte) 0x00, (byte) 0x90, (byte) 0x50, + (byte) 0x00, (byte) 0x11, (byte) 0x11, (byte) 0xd1, + (byte) 0xcc, (byte) 0x55, (byte) 0x58, (byte) 0x5c, + (byte) 0x60, (byte) 0x64, (byte) 0x68, (byte) 0x6c, + (byte) 0x70, (byte) 0x74, (byte) 0x78, (byte) 0x7c, + (byte) 0x80, (byte) 0x84, (byte) 0x08, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0x7c, (byte) 0x00, + (byte) 0x00, (byte) 0x24, (byte) 0x22, (byte) 0x40, + (byte) 0x44, (byte) 0x44, (byte) 0x34, (byte) 0x73, + (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, + (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c, + (byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20, + (byte) 0x21, (byte) 0x01, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x20, (byte) 0x80, + (byte) 0x00, (byte) 0x04, (byte) 0x04, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x04, (byte) 0x04 + }; +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/RobolectricUtil.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java similarity index 99% rename from library/core/src/test/java/com/google/android/exoplayer2/RobolectricUtil.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java index 0b5740b72cc..93611b9b0a1 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/RobolectricUtil.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2; +package com.google.android.exoplayer2.testutil; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.util.ReflectionHelpers.callInstanceMethod; diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java similarity index 99% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 40d5b6c3f9e..af8b10e6d34 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -271,5 +271,4 @@ public int getCurrentAdIndexInAdGroup() { public long getContentPosition() { throw new UnsupportedOperationException(); } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java similarity index 84% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java index 42136bfe4d1..abef8e06be2 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java @@ -23,9 +23,7 @@ import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Window; -/** - * Unit test for {@link Timeline}. - */ +/** Unit test for {@link Timeline}. */ public final class TimelineAsserts { private static final int[] REPEAT_MODES = { @@ -34,9 +32,7 @@ public final class TimelineAsserts { private TimelineAsserts() {} - /** - * Assert that timeline is empty (i.e. has no windows or periods). - */ + /** Assert that timeline is empty (i.e. has no windows or periods). */ public static void assertEmpty(Timeline timeline) { assertWindowIds(timeline); assertPeriodCounts(timeline); @@ -63,9 +59,7 @@ public static void assertWindowIds(Timeline timeline, Object... expectedWindowId } } - /** - * Asserts that window properties {@link Window}.isDynamic are set correctly. - */ + /** Asserts that window properties {@link Window}.isDynamic are set correctly. */ public static void assertWindowIsDynamic(Timeline timeline, boolean... windowIsDynamic) { Window window = new Window(); for (int i = 0; i < timeline.getWindowCount(); i++) { @@ -78,8 +72,10 @@ public static void assertWindowIsDynamic(Timeline timeline, boolean... windowIsD * Asserts that previous window indices for each window depending on the repeat mode and the * shuffle mode are equal to the given sequence. */ - public static void assertPreviousWindowIndices(Timeline timeline, - @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled, + public static void assertPreviousWindowIndices( + Timeline timeline, + @Player.RepeatMode int repeatMode, + boolean shuffleModeEnabled, int... expectedPreviousWindowIndices) { for (int i = 0; i < timeline.getWindowCount(); i++) { assertThat(timeline.getPreviousWindowIndex(i, repeatMode, shuffleModeEnabled)) @@ -88,11 +84,14 @@ public static void assertPreviousWindowIndices(Timeline timeline, } /** - * Asserts that next window indices for each window depending on the repeat mode and the - * shuffle mode are equal to the given sequence. + * Asserts that next window indices for each window depending on the repeat mode and the shuffle + * mode are equal to the given sequence. */ - public static void assertNextWindowIndices(Timeline timeline, @Player.RepeatMode int repeatMode, - boolean shuffleModeEnabled, int... expectedNextWindowIndices) { + public static void assertNextWindowIndices( + Timeline timeline, + @Player.RepeatMode int repeatMode, + boolean shuffleModeEnabled, + int... expectedNextWindowIndices) { for (int i = 0; i < timeline.getWindowCount(); i++) { assertThat(timeline.getNextWindowIndex(i, repeatMode, shuffleModeEnabled)) .isEqualTo(expectedNextWindowIndices[i]); @@ -113,9 +112,9 @@ public static void assertPeriodDurations(Timeline timeline, long... durationsUs) } /** - * Asserts that period counts for each window are set correctly. Also asserts that - * {@link Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it - * asserts the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}. + * Asserts that period counts for each window are set correctly. Also asserts that {@link + * Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it asserts + * the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}. */ public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCounts) { int windowCount = timeline.getWindowCount(); @@ -147,8 +146,8 @@ public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCo .isEqualTo(i + 1); } else { int nextWindow = timeline.getNextWindowIndex(expectedWindowIndex, repeatMode, false); - int nextPeriod = nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET - : accumulatedPeriodCounts[nextWindow]; + int nextPeriod = + nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET : accumulatedPeriodCounts[nextWindow]; assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false)) .isEqualTo(nextPeriod); } @@ -156,9 +155,7 @@ public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCo } } - /** - * Asserts that periods' {@link Period#getAdGroupCount()} are set correctly. - */ + /** Asserts that periods' {@link Period#getAdGroupCount()} are set correctly. */ public static void assertAdGroupCounts(Timeline timeline, int... expectedAdGroupCounts) { Period period = new Period(); for (int i = 0; i < timeline.getPeriodCount(); i++) { @@ -166,5 +163,4 @@ public static void assertAdGroupCounts(Timeline timeline, int... expectedAdGroup assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]); } } - }