Move extension tests to Robolectric.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=187021822
This commit is contained in:
parent
d400efd0f7
commit
b47fb2826b
@ -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'
|
||||
@ -44,6 +45,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')
|
||||
|
@ -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 {
|
||||
|
23
extensions/cast/src/test/AndroidManifest.xml
Normal file
23
extensions/cast/src/test/AndroidManifest.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.ext.cast.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||
|
||||
</manifest>
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.cast;
|
||||
|
||||
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.Test;
|
||||
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 class CastTimelineTrackerTest {
|
||||
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 class CastTimelineTrackerTest {
|
||||
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 class CastTimelineTrackerTest {
|
||||
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 class CastTimelineTrackerTest {
|
||||
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 class CastTimelineTrackerTest {
|
||||
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 @@ public class CastTimelineTrackerTest {
|
||||
int[] itemIds, String[] contentIds, long[] durationsMs) {
|
||||
ArrayList<MediaQueueItem> 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 @@ public class CastTimelineTrackerTest {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
manifest=src/test/AndroidManifest.xml
|
@ -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 {
|
||||
|
@ -18,16 +18,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.ext.cronet">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
||||
|
||||
<application android:debuggable="true"
|
||||
android:allowBackup="false"
|
||||
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="com.google.android.exoplayer2.ext.cronet"/>
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||
|
||||
</manifest>
|
@ -19,9 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
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.Before;
|
||||
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 final class ByteArrayUploadDataProviderTest {
|
||||
assertThat(byteBuffer.array()).isEqualTo(TEST_DATA);
|
||||
verify(mockUploadDataSink).onRewindSucceeded();
|
||||
}
|
||||
|
||||
}
|
@ -31,10 +31,8 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
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.Arrays;
|
||||
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.Before;
|
||||
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<String> mockContentTypePredicate;
|
||||
@Mock
|
||||
private TransferListener<CronetDataSource> mockTransferListener;
|
||||
@Mock
|
||||
private Clock mockClock;
|
||||
@Mock
|
||||
private Executor mockExecutor;
|
||||
@Mock
|
||||
private NetworkException mockNetworkException;
|
||||
@Mock private UrlRequest mockUrlRequest;
|
||||
@Mock private Predicate<String> mockContentTypePredicate;
|
||||
@Mock private TransferListener<CronetDataSource> 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 final class CronetDataSourceTest {
|
||||
// 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<Object>() {
|
||||
@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<Object>() {
|
||||
@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 final class CronetDataSourceTest {
|
||||
@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 final class CronetDataSourceTest {
|
||||
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 final class CronetDataSourceTest {
|
||||
}
|
||||
|
||||
@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 final class CronetDataSourceTest {
|
||||
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 final class CronetDataSourceTest {
|
||||
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 final class CronetDataSourceTest {
|
||||
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 final class CronetDataSourceTest {
|
||||
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 final class CronetDataSourceTest {
|
||||
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 final class CronetDataSourceTest {
|
||||
}
|
||||
|
||||
@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 final class CronetDataSourceTest {
|
||||
}
|
||||
|
||||
@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 final class CronetDataSourceTest {
|
||||
@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 final class CronetDataSourceTest {
|
||||
|
||||
// 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 final class CronetDataSourceTest {
|
||||
}
|
||||
|
||||
@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 final class CronetDataSourceTest {
|
||||
} 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 final class CronetDataSourceTest {
|
||||
// Helper methods.
|
||||
|
||||
private void mockStatusResponse() {
|
||||
doAnswer(new Answer<Object>() {
|
||||
@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<Object>() {
|
||||
@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<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onResponseStarted(
|
||||
mockUrlRequest,
|
||||
testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
}).when(mockUrlRequest).start();
|
||||
doAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(mockUrlRequest)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void mockResponseStartRedirect() {
|
||||
doAnswer(new Answer<Object>() {
|
||||
@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<Object>() {
|
||||
@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<Object>() {
|
||||
@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<Object>() {
|
||||
@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<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onResponseStarted(
|
||||
mockUrlRequest,
|
||||
testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
}).when(mockUrlRequest).followRedirect();
|
||||
doAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(mockUrlRequest)
|
||||
.followRedirect();
|
||||
}
|
||||
|
||||
private void mockResponseStartFailure() {
|
||||
doAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onFailed(
|
||||
mockUrlRequest,
|
||||
createUrlResponseInfo(500), // statusCode
|
||||
mockNetworkException);
|
||||
return null;
|
||||
}
|
||||
}).when(mockUrlRequest).start();
|
||||
doAnswer(
|
||||
new Answer<Object>() {
|
||||
@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<Void>() {
|
||||
@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<Void>() {
|
||||
@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<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onFailed(
|
||||
mockUrlRequest,
|
||||
createUrlResponseInfo(500), // statusCode
|
||||
mockNetworkException);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
new Answer<Object>() {
|
||||
@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 final class CronetDataSourceTest {
|
||||
private ConditionVariable buildReadStartedCondition() {
|
||||
final ConditionVariable startedCondition = new ConditionVariable();
|
||||
doAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
startedCondition.open();
|
||||
return null;
|
||||
}
|
||||
})
|
||||
new Answer<Object>() {
|
||||
@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 final class CronetDataSourceTest {
|
||||
|
||||
private ConditionVariable buildUrlRequestStartedCondition() {
|
||||
final ConditionVariable startedCondition = new ConditionVariable();
|
||||
doAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
startedCondition.open();
|
||||
return null;
|
||||
}
|
||||
}).when(mockUrlRequest).start();
|
||||
doAnswer(
|
||||
new Answer<Object>() {
|
||||
@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 @@ public final class CronetDataSourceTest {
|
||||
testBuffer.flip();
|
||||
return testBuffer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
manifest=src/test/AndroidManifest.xml
|
@ -31,6 +31,7 @@ android {
|
||||
}
|
||||
test {
|
||||
java.srcDirs += "../../testutils/src/main/java/"
|
||||
java.srcDirs += "../../testutils_robolectric/src/main/java/"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ public final class ContentDataSourceTest extends InstrumentationTestCase {
|
||||
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);
|
||||
|
@ -42,6 +42,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -22,8 +22,8 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
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;
|
||||
|
@ -21,11 +21,11 @@ import static org.junit.Assert.fail;
|
||||
import android.os.ConditionVariable;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State;
|
||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.upstream.DummyDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
@ -20,7 +20,6 @@ import static org.junit.Assert.fail;
|
||||
|
||||
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.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;
|
||||
|
@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
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.FakeShuffleOrder;
|
||||
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;
|
||||
|
@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify;
|
||||
import android.os.ConditionVariable;
|
||||
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.DummyMainThread;
|
||||
@ -31,6 +30,7 @@ import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
|
||||
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;
|
||||
|
@ -17,12 +17,12 @@ package com.google.android.exoplayer2.source;
|
||||
|
||||
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;
|
||||
|
@ -19,13 +19,13 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
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;
|
||||
|
@ -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 {
|
||||
|
@ -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 =
|
||||
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" type=\"static\" "
|
||||
+ " mediaPresentationDuration=\"PT31S\">\n"
|
||||
+ " <Period duration=\"PT16S\" >\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentTimeline>\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " </SegmentTimeline>\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentList>\n"
|
||||
// Bounded range data
|
||||
+ " <Initialization range=\"0-9\" sourceURL=\"audio_init_data\" />\n"
|
||||
// Unbounded range data
|
||||
+ " <SegmentURL media=\"audio_segment_1\" />\n"
|
||||
+ " <SegmentURL media=\"audio_segment_2\" />\n"
|
||||
+ " <SegmentURL media=\"audio_segment_3\" />\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
// This segment list has a 1 second offset to make sure the progressive download order
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentTimeline>\n"
|
||||
+ " <S t=\"1\" d=\"5\" />\n" // 1s offset
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " </SegmentTimeline>\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentURL media=\"text_segment_1\" />\n"
|
||||
+ " <SegmentURL media=\"text_segment_2\" />\n"
|
||||
+ " <SegmentURL media=\"text_segment_3\" />\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " </Period>\n"
|
||||
+ " <Period>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentTimeline>\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " </SegmentTimeline>\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentURL media=\"period_2_segment_1\" />\n"
|
||||
+ " <SegmentURL media=\"period_2_segment_2\" />\n"
|
||||
+ " <SegmentURL media=\"period_2_segment_3\" />\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " </Period>\n"
|
||||
+ "</MPD>").getBytes();
|
||||
|
||||
byte[] TEST_MPD_NO_INDEX =
|
||||
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" type=\"dynamic\">\n"
|
||||
+ " <Period start=\"PT6462826.784S\" >\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentBase indexRange='0-10'/>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " </Period>\n"
|
||||
+ "</MPD>").getBytes();
|
||||
}
|
@ -50,6 +50,7 @@ import java.io.BufferedReader;
|
||||
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 final class DashMediaSource implements MediaSource {
|
||||
|
||||
@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()) {
|
||||
|
@ -18,16 +18,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.source.dash.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
||||
|
||||
<application android:debuggable="true"
|
||||
android:allowBackup="false"
|
||||
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:targetPackage="com.google.android.exoplayer2.source.dash.test"
|
||||
android:name="android.test.InstrumentationTestRunner"/>
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="25"/>
|
||||
|
||||
</manifest>
|
@ -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<Long> parser, String data) throws IOException {
|
||||
long actual = parser.parse(null, new ByteArrayInputStream(Util.getUtf8Bytes(data)));
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
}
|
@ -28,33 +28,38 @@ import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegm
|
||||
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 @@ public final class DashUtilTest extends TestCase {
|
||||
}
|
||||
|
||||
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 @@ public final class DashUtilTest extends TestCase {
|
||||
}
|
||||
|
||||
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}));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -18,39 +18,47 @@ package com.google.android.exoplayer2.source.dash.manifest;
|
||||
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 class DashManifestParserTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
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 class DashManifestParserTest extends InstrumentationTestCase {
|
||||
// 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 class DashManifestParserTest extends InstrumentationTestCase {
|
||||
1000000000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCea608AccessibilityChannel() {
|
||||
assertThat(
|
||||
DashManifestParser.parseCea608AccessibilityChannel(
|
||||
@ -175,6 +192,7 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
||||
.isEqualTo(Format.NO_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCea708AccessibilityChannel() {
|
||||
assertThat(
|
||||
DashManifestParser.parseCea708AccessibilityChannel(
|
||||
@ -226,5 +244,4 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
||||
private static List<Descriptor> buildCea708AccessibilityDescriptors(String value) {
|
||||
return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-708:2015", value, null));
|
||||
}
|
||||
|
||||
}
|
@ -24,109 +24,143 @@ import java.util.Arrays;
|
||||
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])));
|
||||
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<RepresentationKey> 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));
|
||||
List<RepresentationKey> 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 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 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 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 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),
|
||||
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)));
|
||||
|
||||
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 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 @@ public class DashManifestTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class DashManifestTest extends TestCase {
|
||||
}
|
||||
|
||||
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 @@ public class DashManifestTest extends TestCase {
|
||||
private static AdaptationSet newAdaptationSet(int seed, Representation... representations) {
|
||||
return new AdaptationSet(++seed, ++seed, Arrays.asList(representations), null, null);
|
||||
}
|
||||
|
||||
}
|
@ -18,17 +18,19 @@ package com.google.android.exoplayer2.source.dash.manifest;
|
||||
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 class RangedUriTest extends TestCase {
|
||||
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 class RangedUriTest extends TestCase {
|
||||
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 class RangedUriTest extends TestCase {
|
||||
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 @@ public class RangedUriTest extends TestCase {
|
||||
merged = rangeB.attemptMerge(rangeA, baseUrl);
|
||||
assertThat(merged).isNull();
|
||||
}
|
||||
|
||||
}
|
@ -20,27 +20,49 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
@ -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 class UrlTemplateTest extends TestCase {
|
||||
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 class UrlTemplateTest extends TestCase {
|
||||
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 class UrlTemplateTest extends TestCase {
|
||||
assertThat(url).isEqualTo("$650000$_a$_abc1_b_5000_c_10$");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidSubstitution() {
|
||||
String template = "$IllegalId$";
|
||||
try {
|
||||
@ -64,5 +70,4 @@ public class UrlTemplateTest extends TestCase {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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 =
|
||||
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" type=\"static\" "
|
||||
+ " mediaPresentationDuration=\"PT31S\">\n"
|
||||
+ " <Period duration=\"PT16S\" >\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentTimeline>\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " </SegmentTimeline>\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentList>\n"
|
||||
// Bounded range data
|
||||
+ " <Initialization\n"
|
||||
+ " range=\"0-9\" sourceURL=\"audio_init_data\" />\n"
|
||||
// Unbounded range data
|
||||
+ " <SegmentURL media=\"audio_segment_1\" />\n"
|
||||
+ " <SegmentURL media=\"audio_segment_2\" />\n"
|
||||
+ " <SegmentURL media=\"audio_segment_3\" />\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
// This segment list has a 1 second offset to make sure the progressive download order
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentTimeline>\n"
|
||||
+ " <S t=\"1\" d=\"5\" />\n" // 1s offset
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " </SegmentTimeline>\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentURL media=\"text_segment_1\" />\n"
|
||||
+ " <SegmentURL media=\"text_segment_2\" />\n"
|
||||
+ " <SegmentURL media=\"text_segment_3\" />\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " </Period>\n"
|
||||
+ " <Period>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentTimeline>\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " <S d=\"5\" />\n"
|
||||
+ " </SegmentTimeline>\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentList>\n"
|
||||
+ " <SegmentURL media=\"period_2_segment_1\" />\n"
|
||||
+ " <SegmentURL media=\"period_2_segment_2\" />\n"
|
||||
+ " <SegmentURL media=\"period_2_segment_3\" />\n"
|
||||
+ " </SegmentList>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " </Period>\n"
|
||||
+ "</MPD>")
|
||||
.getBytes(Charset.forName(C.UTF8_NAME));
|
||||
|
||||
byte[] TEST_MPD_NO_INDEX =
|
||||
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" type=\"dynamic\">\n"
|
||||
+ " <Period start=\"PT6462826.784S\" >\n"
|
||||
+ " <AdaptationSet>\n"
|
||||
+ " <Representation>\n"
|
||||
+ " <SegmentBase indexRange='0-10'/>\n"
|
||||
+ " </Representation>\n"
|
||||
+ " </AdaptationSet>\n"
|
||||
+ " </Period>\n"
|
||||
+ "</MPD>")
|
||||
.getBytes(Charset.forName(C.UTF8_NAME));
|
||||
}
|
@ -22,10 +22,10 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmp
|
||||
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.source.dash.manifest.RepresentationKey;
|
||||
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 com.google.android.exoplayer2.util.Util;
|
||||
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 class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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 class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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 class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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 class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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 class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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
|
||||
@ -190,21 +203,23 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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)});
|
||||
@ -222,21 +237,23 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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)});
|
||||
@ -254,17 +271,19 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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)});
|
||||
@ -280,17 +299,19 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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);
|
||||
@ -314,13 +335,15 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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)});
|
||||
@ -335,16 +358,18 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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)});
|
||||
@ -355,10 +380,12 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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)});
|
||||
@ -374,13 +401,15 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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(
|
||||
@ -396,11 +425,13 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -23,26 +23,33 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.ConditionVariable;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.UiThreadTest;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.testutil.MockitoUtil;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.DataSource.Factory;
|
||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||
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.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/**
|
||||
* Tests {@link DownloadManager}.
|
||||
*/
|
||||
public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
/** Tests {@link DownloadManager}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
public class DownloadManagerDashTest {
|
||||
|
||||
private static final int ASSERT_TRUE_TIMEOUT = 1000;
|
||||
|
||||
@ -56,26 +63,25 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
private File actionFile;
|
||||
private DummyMainThread dummyMainThread;
|
||||
|
||||
@UiThreadTest
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dummyMainThread = new DummyMainThread();
|
||||
Context context = getInstrumentation().getContext();
|
||||
Context context = RuntimeEnvironment.application;
|
||||
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
|
||||
File cacheFolder = new File(tempFolder, "cache");
|
||||
cacheFolder.mkdir();
|
||||
cache = new SimpleCache(cacheFolder, new NoOpCacheEvictor());
|
||||
MockitoUtil.setUpMockito(this);
|
||||
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);
|
||||
MockitoAnnotations.initMocks(this);
|
||||
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);
|
||||
|
||||
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
|
||||
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
|
||||
@ -83,33 +89,35 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
createDownloadManager();
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
downloadManager.release();
|
||||
Util.recursiveDelete(tempFolder);
|
||||
dummyMainThread.release();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
// Disabled due to flakiness.
|
||||
public void disabledTestSaveAndLoadActionFile() throws Throwable {
|
||||
@Ignore
|
||||
@Test
|
||||
public void testSaveAndLoadActionFile() throws Throwable {
|
||||
// Configure fakeDataSet to block until interrupted when TEST_MPD is read.
|
||||
fakeDataSet.newData(TEST_MPD_URI)
|
||||
.appendReadAction(new Runnable() {
|
||||
@SuppressWarnings("InfiniteLoopStatement")
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Wait until interrupted.
|
||||
while (true) {
|
||||
Thread.sleep(100000);
|
||||
fakeDataSet
|
||||
.newData(TEST_MPD_URI)
|
||||
.appendReadAction(
|
||||
new Runnable() {
|
||||
@SuppressWarnings("InfiniteLoopStatement")
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Wait until interrupted.
|
||||
while (true) {
|
||||
Thread.sleep(100000);
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.appendReadData(TEST_MPD)
|
||||
.endData();
|
||||
|
||||
@ -122,15 +130,20 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
// Setup an Action and immediately release the DM.
|
||||
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||
downloadManager.release();
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(actionFile.exists()).isTrue();
|
||||
assertThat(actionFile.length()).isGreaterThan(0L);
|
||||
assertThat(actionFile.exists()).isTrue();
|
||||
assertThat(actionFile.length()).isGreaterThan(0L);
|
||||
assertCacheEmpty(cache);
|
||||
|
||||
assertCacheEmpty(cache);
|
||||
|
||||
// Revert fakeDataSet to normal.
|
||||
fakeDataSet.setData(TEST_MPD_URI, TEST_MPD);
|
||||
// Revert fakeDataSet to normal.
|
||||
fakeDataSet.setData(TEST_MPD_URI, TEST_MPD);
|
||||
|
||||
dummyMainThread.runOnMainThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
createDownloadManager();
|
||||
}
|
||||
});
|
||||
@ -140,12 +153,14 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
assertCachedData(cache, fakeDataSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleDownloadAction() throws Throwable {
|
||||
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||
blockUntilTasksCompleteAndThrowAnyDownloadError();
|
||||
assertCachedData(cache, fakeDataSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMultipleDownloadAction() throws Throwable {
|
||||
handleDownloadAction(fakeRepresentationKey1);
|
||||
handleDownloadAction(fakeRepresentationKey2);
|
||||
@ -153,6 +168,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
assertCachedData(cache, fakeDataSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleInterferingDownloadAction() throws Throwable {
|
||||
fakeDataSet
|
||||
.newData("audio_segment_2")
|
||||
@ -172,6 +188,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
assertCachedData(cache, fakeDataSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleRemoveAction() throws Throwable {
|
||||
handleDownloadAction(fakeRepresentationKey1);
|
||||
|
||||
@ -185,7 +202,9 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
// Disabled due to flakiness.
|
||||
public void disabledTestHandleRemoveActionBeforeDownloadFinish() throws Throwable {
|
||||
@Ignore
|
||||
@Test
|
||||
public void testHandleRemoveActionBeforeDownloadFinish() throws Throwable {
|
||||
handleDownloadAction(fakeRepresentationKey1);
|
||||
handleRemoveAction();
|
||||
|
||||
@ -194,15 +213,18 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
assertCacheEmpty(cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleInterferingRemoveAction() throws Throwable {
|
||||
final ConditionVariable downloadInProgressCondition = new ConditionVariable();
|
||||
fakeDataSet.newData("audio_segment_2")
|
||||
.appendReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
downloadInProgressCondition.open();
|
||||
}
|
||||
})
|
||||
fakeDataSet
|
||||
.newData("audio_segment_2")
|
||||
.appendReadAction(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
downloadInProgressCondition.open();
|
||||
}
|
||||
})
|
||||
.appendReadData(TestUtil.buildTestData(5))
|
||||
.endData();
|
||||
|
||||
@ -250,5 +272,4 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -22,7 +22,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
@ -32,6 +31,7 @@ import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||
@ -40,11 +40,18 @@ import com.google.android.exoplayer2.util.ConditionVariable;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DownloadService}.
|
||||
*/
|
||||
public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
/** Unit tests for {@link DownloadService}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
public class DownloadServiceDashTest {
|
||||
|
||||
private SimpleCache cache;
|
||||
private File tempFolder;
|
||||
@ -57,44 +64,44 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
private TestDownloadListener testDownloadListener;
|
||||
private DummyMainThread dummyMainThread;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dummyMainThread = new DummyMainThread();
|
||||
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
|
||||
context = RuntimeEnvironment.application;
|
||||
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
|
||||
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
||||
|
||||
Runnable pauseAction = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (pauseDownloadCondition != null) {
|
||||
try {
|
||||
pauseDownloadCondition.block();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
Runnable pauseAction =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (pauseDownloadCondition != null) {
|
||||
try {
|
||||
pauseDownloadCondition.block();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
fakeDataSet = new FakeDataSet()
|
||||
.setData(TEST_MPD_URI, TEST_MPD)
|
||||
.newData("audio_init_data")
|
||||
.appendReadAction(pauseAction)
|
||||
.appendReadData(TestUtil.buildTestData(10))
|
||||
.endData()
|
||||
.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 =
|
||||
new FakeDataSet()
|
||||
.setData(TEST_MPD_URI, TEST_MPD)
|
||||
.newData("audio_init_data")
|
||||
.appendReadAction(pauseAction)
|
||||
.appendReadData(TestUtil.buildTestData(10))
|
||||
.endData()
|
||||
.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);
|
||||
final DataSource.Factory fakeDataSourceFactory =
|
||||
new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet);
|
||||
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
|
||||
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
|
||||
|
||||
context = getInstrumentation().getContext();
|
||||
|
||||
try {
|
||||
dummyMainThread.runOnMainThread(
|
||||
new Runnable() {
|
||||
@ -128,7 +135,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
|
||||
@Override
|
||||
protected String getNotificationChannelId() {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -149,7 +156,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
try {
|
||||
dummyMainThread.runOnMainThread(
|
||||
@ -164,9 +171,9 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
}
|
||||
Util.recursiveDelete(tempFolder);
|
||||
dummyMainThread.release();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleDownloadAction() throws Throwable {
|
||||
downloadKeys(fakeRepresentationKey1);
|
||||
downloadKeys(fakeRepresentationKey2);
|
||||
@ -176,6 +183,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
assertCachedData(cache, fakeDataSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAction() throws Throwable {
|
||||
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||
|
||||
@ -188,6 +196,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
assertCacheEmpty(cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveBeforeDownloadComplete() throws Throwable {
|
||||
pauseDownloadCondition = new ConditionVariable();
|
||||
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||
@ -219,5 +228,4 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -21,6 +21,8 @@ import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** A {@link DownloadListener} for testing. */
|
||||
/*package*/ final class TestDownloadListener implements DownloadListener {
|
||||
@ -29,13 +31,12 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
|
||||
private final DownloadManager downloadManager;
|
||||
private final DummyMainThread dummyMainThread;
|
||||
private final android.os.ConditionVariable downloadFinishedCondition;
|
||||
private CountDownLatch downloadFinishedCondition;
|
||||
private Throwable downloadError;
|
||||
|
||||
public TestDownloadListener(DownloadManager downloadManager, DummyMainThread dummyMainThread) {
|
||||
this.downloadManager = downloadManager;
|
||||
this.dummyMainThread = dummyMainThread;
|
||||
this.downloadFinishedCondition = new android.os.ConditionVariable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,8 +47,10 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIdle(DownloadManager downloadManager) {
|
||||
downloadFinishedCondition.open();
|
||||
public synchronized void onIdle(DownloadManager downloadManager) {
|
||||
if (downloadFinishedCondition != null) {
|
||||
downloadFinishedCondition.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,18 +58,19 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
* error.
|
||||
*/
|
||||
public void blockUntilTasksCompleteAndThrowAnyDownloadError() throws Throwable {
|
||||
synchronized (this) {
|
||||
downloadFinishedCondition = new CountDownLatch(1);
|
||||
}
|
||||
dummyMainThread.runOnMainThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (downloadManager.isIdle()) {
|
||||
downloadFinishedCondition.open();
|
||||
} else {
|
||||
downloadFinishedCondition.close();
|
||||
downloadFinishedCondition.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
assertThat(downloadFinishedCondition.block(TIMEOUT)).isTrue();
|
||||
assertThat(downloadFinishedCondition.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
|
||||
if (downloadError != null) {
|
||||
throw new Exception(downloadError);
|
||||
}
|
1
library/dash/src/test/resources/robolectric.properties
Normal file
1
library/dash/src/test/resources/robolectric.properties
Normal file
@ -0,0 +1 @@
|
||||
manifest=src/test/AndroidManifest.xml
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -18,16 +18,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.source.hls.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
||||
|
||||
<application android:debuggable="true"
|
||||
android:allowBackup="false"
|
||||
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:targetPackage="com.google.android.exoplayer2.source.hls.test"
|
||||
android:name="android.test.InstrumentationTestRunner"/>
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||
|
||||
</manifest>
|
@ -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));
|
||||
}
|
@ -33,7 +33,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa
|
||||
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.NoOpCacheEvictor;
|
||||
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 class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
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 class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
.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 class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
.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 class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
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)
|
||||
@ -162,6 +182,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
hlsDownloader.remove();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll() throws Exception {
|
||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
|
||||
hlsDownloader.download(null);
|
||||
@ -170,27 +191,32 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
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);
|
||||
|
||||
@ -199,8 +225,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
@ -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.ByteArrayInputStream;
|
||||
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_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_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_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_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_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_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 class HlsMasterPlaylistParserTest extends TestCase {
|
||||
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<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
|
||||
|
||||
@ -139,6 +156,7 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
||||
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 class HlsMasterPlaylistParserTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaylistWithClosedCaption() throws IOException {
|
||||
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC);
|
||||
assertThat(playlist.muxedCaptionFormats).hasSize(1);
|
||||
@ -157,11 +176,13 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
||||
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 class HlsMasterPlaylistParserTest extends TestCase {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -26,49 +26,53 @@ import java.io.InputStream;
|
||||
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 class HlsMediaPlaylistParserTest extends TestCase {
|
||||
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 class HlsMediaPlaylistParserTest extends TestCase {
|
||||
assertThat(playlist.segments.get(2).hasGapTag).isTrue();
|
||||
assertThat(playlist.segments.get(3).hasGapTag).isFalse();
|
||||
}
|
||||
|
||||
}
|
1
library/hls/src/test/resources/robolectric.properties
Normal file
1
library/hls/src/test/resources/robolectric.properties
Normal file
@ -0,0 +1 @@
|
||||
manifest=src/test/AndroidManifest.xml
|
@ -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 {
|
||||
|
@ -18,16 +18,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.source.smoothstreaming.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
||||
|
||||
<application android:debuggable="true"
|
||||
android:allowBackup="false"
|
||||
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:targetPackage="com.google.android.exoplayer2.source.smoothstreaming.test"
|
||||
android:name="android.test.InstrumentationTestRunner"/>
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||
|
||||
</manifest>
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -26,52 +26,49 @@ import java.util.Arrays;
|
||||
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]));
|
||||
SsManifest sourceManifest =
|
||||
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
|
||||
|
||||
List<TrackKey> keys = Arrays.asList(
|
||||
new TrackKey(0, 0),
|
||||
new TrackKey(0, 2),
|
||||
new TrackKey(1, 0));
|
||||
List<TrackKey> 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<TrackKey> keys = Arrays.asList(
|
||||
new TrackKey(1, 0));
|
||||
List<TrackKey> 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 @@ public class SsManifestTest extends TestCase {
|
||||
}
|
||||
|
||||
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.<Long>emptyList(), 0);
|
||||
return new StreamElement(
|
||||
"baseUri",
|
||||
"chunkTemplate",
|
||||
C.TRACK_TYPE_VIDEO,
|
||||
"subType",
|
||||
1000,
|
||||
name,
|
||||
1024,
|
||||
768,
|
||||
1024,
|
||||
768,
|
||||
null,
|
||||
formats,
|
||||
Collections.<Long>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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
manifest=src/test/AndroidManifest.xml
|
@ -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')
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,6 @@ package com.google.android.exoplayer2.testutil;
|
||||
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 class TestUtil {
|
||||
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);
|
||||
}
|
||||
|
23
testutils/src/test/AndroidManifest.xml
Normal file
23
testutils/src/test/AndroidManifest.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.testutil.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||
|
||||
</manifest>
|
@ -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<Segment> segments) {
|
||||
double totalSize = 0.0;
|
||||
for (Segment segment : segments) {
|
||||
totalSize += segment.length;
|
||||
}
|
||||
return totalSize / segments.size();
|
||||
}
|
||||
|
||||
private static double computeSegmentSizeStdDev(List<Segment> 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Segment> 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
1
testutils/src/test/resources/robolectric.properties
Normal file
1
testutils/src/test/resources/robolectric.properties
Normal file
@ -0,0 +1 @@
|
||||
manifest=src/test/AndroidManifest.xml
|
37
testutils_robolectric/build.gradle
Normal file
37
testutils_robolectric/build.gradle
Normal file
@ -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
|
||||
}
|
17
testutils_robolectric/src/main/AndroidManifest.xml
Normal file
17
testutils_robolectric/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest package="com.google.android.exoplayer2.testutil"/>
|
@ -29,9 +29,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheUtil;
|
||||
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 final class CacheAsserts {
|
||||
}
|
||||
|
||||
private CacheAsserts() {}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
|
||||
/** Helper class to simulate main/UI thread in tests. */
|
||||
public final class DummyMainThread {
|
||||
@ -54,16 +55,20 @@ public final class DummyMainThread {
|
||||
* @param runnable The {@link Runnable} to run.
|
||||
*/
|
||||
public void runOnMainThread(int timeoutMs, final Runnable runnable) {
|
||||
final ConditionVariable finishedCondition = new ConditionVariable();
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runnable.run();
|
||||
finishedCondition.open();
|
||||
}
|
||||
});
|
||||
assertThat(finishedCondition.block(timeoutMs)).isTrue();
|
||||
if (Looper.myLooper() == handler.getLooper()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
final ConditionVariable finishedCondition = new ConditionVariable();
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runnable.run();
|
||||
finishedCondition.open();
|
||||
}
|
||||
});
|
||||
assertThat(finishedCondition.block(timeoutMs)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
@ -19,9 +19,7 @@ import com.google.android.exoplayer2.Format;
|
||||
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 abstract class FakeMediaClockRenderer extends FakeRenderer implements Med
|
||||
public MediaClock getMediaClock() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -25,8 +25,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
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 final class FakeTrackSelection implements TrackSelection {
|
||||
}
|
||||
|
||||
@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 final class FakeTrackSelection implements TrackSelection {
|
||||
assertThat(isEnabled).isTrue();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -25,9 +25,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
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<FakeTrackSelection> selectedTrackSelections = new ArrayList<>();
|
||||
@ -38,17 +36,19 @@ public class FakeTrackSelector extends MappingTrackSelector {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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<FakeTrackSelection> resultList = new ArrayList<>();
|
||||
for (TrackGroupArray trackGroupArray : rendererTrackGroupArrays) {
|
||||
@ -76,11 +76,8 @@ public class FakeTrackSelector extends MappingTrackSelector {
|
||||
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<FakeTrackSelection> getSelectedTrackSelections() {
|
||||
return selectedTrackSelections;
|
||||
}
|
||||
|
||||
}
|
@ -37,9 +37,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
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 class MediaSourceTestRunner {
|
||||
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 class MediaSourceTestRunner {
|
||||
*/
|
||||
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 class MediaSourceTestRunner {
|
||||
*/
|
||||
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 class MediaSourceTestRunner {
|
||||
* @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 @@ public class MediaSourceTestRunner {
|
||||
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 class MediaSourceTestRunner {
|
||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||
timelines.addLast(timeline);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class EventHandlingExoPlayer extends StubExoPlayer
|
||||
@ -326,5 +324,4 @@ public class MediaSourceTestRunner {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
@ -271,5 +271,4 @@ public abstract class StubExoPlayer implements ExoPlayer {
|
||||
public long getContentPosition() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -23,9 +23,7 @@ import com.google.android.exoplayer2.Timeline;
|
||||
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 final class TimelineAsserts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final class TimelineAsserts {
|
||||
* 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 final class TimelineAsserts {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final class TimelineAsserts {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final class TimelineAsserts {
|
||||
.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 final class TimelineAsserts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final class TimelineAsserts {
|
||||
assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user