mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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-smoothstreaming'
|
||||||
include modulePrefix + 'library-ui'
|
include modulePrefix + 'library-ui'
|
||||||
include modulePrefix + 'testutils'
|
include modulePrefix + 'testutils'
|
||||||
|
include modulePrefix + 'testutils-robolectric'
|
||||||
include modulePrefix + 'extension-ffmpeg'
|
include modulePrefix + 'extension-ffmpeg'
|
||||||
include modulePrefix + 'extension-flac'
|
include modulePrefix + 'extension-flac'
|
||||||
include modulePrefix + 'extension-gvr'
|
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-smoothstreaming').projectDir = new File(rootDir, 'library/smoothstreaming')
|
||||||
project(modulePrefix + 'library-ui').projectDir = new File(rootDir, 'library/ui')
|
project(modulePrefix + 'library-ui').projectDir = new File(rootDir, 'library/ui')
|
||||||
project(modulePrefix + 'testutils').projectDir = new File(rootDir, 'testutils')
|
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-ffmpeg').projectDir = new File(rootDir, 'extensions/ffmpeg')
|
||||||
project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac')
|
project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac')
|
||||||
project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr')
|
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 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion
|
||||||
compile project(modulePrefix + 'library-core')
|
compile project(modulePrefix + 'library-core')
|
||||||
compile project(modulePrefix + 'library-ui')
|
compile project(modulePrefix + 'library-ui')
|
||||||
testCompile project(modulePrefix + 'testutils')
|
testCompile project(modulePrefix + 'testutils-robolectric')
|
||||||
testCompile 'junit:junit:' + junitVersion
|
|
||||||
testCompile 'org.mockito:mockito-core:' + mockitoVersion
|
|
||||||
testCompile 'org.robolectric:robolectric:' + robolectricVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
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.C;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
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.MediaInfo;
|
||||||
import com.google.android.gms.cast.MediaQueueItem;
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
import com.google.android.gms.cast.MediaStatus;
|
import com.google.android.gms.cast.MediaStatus;
|
||||||
@ -25,11 +26,9 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
|
|
||||||
/** Tests for {@link CastTimelineTracker}. */
|
/** Tests for {@link CastTimelineTracker}. */
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE)
|
|
||||||
public class CastTimelineTrackerTest {
|
public class CastTimelineTrackerTest {
|
||||||
|
|
||||||
private static final long DURATION_1_MS = 1000;
|
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});
|
new long[] {DURATION_1_MS, MediaInfo.UNKNOWN_DURATION, MediaInfo.UNKNOWN_DURATION});
|
||||||
|
|
||||||
CastTimelineTracker tracker = new CastTimelineTracker();
|
CastTimelineTracker tracker = new CastTimelineTracker();
|
||||||
mediaInfo = mockMediaInfo("contentId1", DURATION_1_MS);
|
mediaInfo = getMediaInfo("contentId1", DURATION_1_MS);
|
||||||
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
TimelineAsserts.assertPeriodDurations(
|
TimelineAsserts.assertPeriodDurations(
|
||||||
tracker.getCastTimeline(status), C.msToUs(DURATION_1_MS), C.TIME_UNSET, C.TIME_UNSET);
|
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);
|
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
TimelineAsserts.assertPeriodDurations(
|
TimelineAsserts.assertPeriodDurations(
|
||||||
tracker.getCastTimeline(status),
|
tracker.getCastTimeline(status),
|
||||||
@ -62,7 +61,7 @@ public class CastTimelineTrackerTest {
|
|||||||
C.TIME_UNSET,
|
C.TIME_UNSET,
|
||||||
C.msToUs(DURATION_3_MS));
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
mediaInfo = mockMediaInfo("contentId2", DURATION_2_MS);
|
mediaInfo = getMediaInfo("contentId2", DURATION_2_MS);
|
||||||
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
TimelineAsserts.assertPeriodDurations(
|
TimelineAsserts.assertPeriodDurations(
|
||||||
tracker.getCastTimeline(status),
|
tracker.getCastTimeline(status),
|
||||||
@ -80,7 +79,7 @@ public class CastTimelineTrackerTest {
|
|||||||
DURATION_5_MS,
|
DURATION_5_MS,
|
||||||
MediaInfo.UNKNOWN_DURATION
|
MediaInfo.UNKNOWN_DURATION
|
||||||
});
|
});
|
||||||
mediaInfo = mockMediaInfo("contentId5", DURATION_5_MS);
|
mediaInfo = getMediaInfo("contentId5", DURATION_5_MS);
|
||||||
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
TimelineAsserts.assertPeriodDurations(
|
TimelineAsserts.assertPeriodDurations(
|
||||||
tracker.getCastTimeline(newStatus),
|
tracker.getCastTimeline(newStatus),
|
||||||
@ -89,7 +88,7 @@ public class CastTimelineTrackerTest {
|
|||||||
C.msToUs(DURATION_5_MS),
|
C.msToUs(DURATION_5_MS),
|
||||||
C.msToUs(DURATION_3_MS));
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS);
|
mediaInfo = getMediaInfo("contentId3", DURATION_3_MS);
|
||||||
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
TimelineAsserts.assertPeriodDurations(
|
TimelineAsserts.assertPeriodDurations(
|
||||||
tracker.getCastTimeline(newStatus),
|
tracker.getCastTimeline(newStatus),
|
||||||
@ -98,7 +97,7 @@ public class CastTimelineTrackerTest {
|
|||||||
C.msToUs(DURATION_5_MS),
|
C.msToUs(DURATION_5_MS),
|
||||||
C.msToUs(DURATION_3_MS));
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
mediaInfo = mockMediaInfo("contentId4", DURATION_4_MS);
|
mediaInfo = getMediaInfo("contentId4", DURATION_4_MS);
|
||||||
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
TimelineAsserts.assertPeriodDurations(
|
TimelineAsserts.assertPeriodDurations(
|
||||||
tracker.getCastTimeline(newStatus),
|
tracker.getCastTimeline(newStatus),
|
||||||
@ -112,7 +111,7 @@ public class CastTimelineTrackerTest {
|
|||||||
int[] itemIds, String[] contentIds, long[] durationsMs) {
|
int[] itemIds, String[] contentIds, long[] durationsMs) {
|
||||||
ArrayList<MediaQueueItem> items = new ArrayList<>();
|
ArrayList<MediaQueueItem> items = new ArrayList<>();
|
||||||
for (int i = 0; i < contentIds.length; i++) {
|
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);
|
MediaQueueItem item = Mockito.mock(MediaQueueItem.class);
|
||||||
Mockito.when(item.getMedia()).thenReturn(mediaInfo);
|
Mockito.when(item.getMedia()).thenReturn(mediaInfo);
|
||||||
Mockito.when(item.getItemId()).thenReturn(itemIds[i]);
|
Mockito.when(item.getItemId()).thenReturn(itemIds[i]);
|
||||||
@ -123,10 +122,11 @@ public class CastTimelineTrackerTest {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaInfo mockMediaInfo(String contentId, long durationMs) {
|
private static MediaInfo getMediaInfo(String contentId, long durationMs) {
|
||||||
MediaInfo mediaInfo = Mockito.mock(MediaInfo.class);
|
return new MediaInfo.Builder(contentId)
|
||||||
Mockito.when(mediaInfo.getContentId()).thenReturn(contentId);
|
.setStreamDuration(durationMs)
|
||||||
Mockito.when(mediaInfo.getStreamDuration()).thenReturn(durationMs);
|
.setContentType(MimeTypes.APPLICATION_MP4)
|
||||||
return mediaInfo;
|
.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_api.jar')
|
||||||
compile files('libs/cronet_impl_common_java.jar')
|
compile files('libs/cronet_impl_common_java.jar')
|
||||||
compile files('libs/cronet_impl_native_java.jar')
|
compile files('libs/cronet_impl_native_java.jar')
|
||||||
androidTestCompile project(modulePrefix + 'library')
|
testCompile project(modulePrefix + 'library')
|
||||||
androidTestCompile project(modulePrefix + 'testutils')
|
testCompile project(modulePrefix + 'testutils-robolectric')
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -18,16 +18,6 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.google.android.exoplayer2.ext.cronet">
|
package="com.google.android.exoplayer2.ext.cronet">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||||
|
|
||||||
<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"/>
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -19,9 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
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.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -30,11 +27,11 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
/**
|
/** Tests for {@link ByteArrayUploadDataProvider}. */
|
||||||
* Tests for {@link ByteArrayUploadDataProvider}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public final class ByteArrayUploadDataProviderTest {
|
public final class ByteArrayUploadDataProviderTest {
|
||||||
|
|
||||||
private static final byte[] TEST_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
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
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this);
|
MockitoAnnotations.initMocks(this);
|
||||||
byteBuffer = ByteBuffer.allocate(TEST_DATA.length);
|
byteBuffer = ByteBuffer.allocate(TEST_DATA.length);
|
||||||
byteArrayUploadDataProvider = new ByteArrayUploadDataProvider(TEST_DATA);
|
byteArrayUploadDataProvider = new ByteArrayUploadDataProvider(TEST_DATA);
|
||||||
}
|
}
|
||||||
@ -90,5 +87,4 @@ public final class ByteArrayUploadDataProviderTest {
|
|||||||
assertThat(byteBuffer.array()).isEqualTo(TEST_DATA);
|
assertThat(byteBuffer.array()).isEqualTo(TEST_DATA);
|
||||||
verify(mockUploadDataSink).onRewindSucceeded();
|
verify(mockUploadDataSink).onRewindSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -31,10 +31,8 @@ import static org.mockito.Mockito.when;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import android.os.SystemClock;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import com.google.android.exoplayer2.C;
|
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.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
||||||
@ -50,6 +48,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.chromium.net.CronetEngine;
|
import org.chromium.net.CronetEngine;
|
||||||
@ -61,13 +60,14 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.shadows.ShadowSystemClock;
|
||||||
|
|
||||||
/**
|
/** Tests for {@link CronetDataSource}. */
|
||||||
* Tests for {@link CronetDataSource}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public final class CronetDataSourceTest {
|
public final class CronetDataSourceTest {
|
||||||
|
|
||||||
private static final int TEST_CONNECT_TIMEOUT_MS = 100;
|
private static final int TEST_CONNECT_TIMEOUT_MS = 100;
|
||||||
@ -85,18 +85,11 @@ public final class CronetDataSourceTest {
|
|||||||
private UrlResponseInfo testUrlResponseInfo;
|
private UrlResponseInfo testUrlResponseInfo;
|
||||||
|
|
||||||
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
|
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
|
||||||
@Mock
|
@Mock private UrlRequest mockUrlRequest;
|
||||||
private UrlRequest mockUrlRequest;
|
@Mock private Predicate<String> mockContentTypePredicate;
|
||||||
@Mock
|
@Mock private TransferListener<CronetDataSource> mockTransferListener;
|
||||||
private Predicate<String> mockContentTypePredicate;
|
@Mock private Executor mockExecutor;
|
||||||
@Mock
|
@Mock private NetworkException mockNetworkException;
|
||||||
private TransferListener<CronetDataSource> mockTransferListener;
|
|
||||||
@Mock
|
|
||||||
private Clock mockClock;
|
|
||||||
@Mock
|
|
||||||
private Executor mockExecutor;
|
|
||||||
@Mock
|
|
||||||
private NetworkException mockNetworkException;
|
|
||||||
@Mock private CronetEngine mockCronetEngine;
|
@Mock private CronetEngine mockCronetEngine;
|
||||||
|
|
||||||
private CronetDataSource dataSourceUnderTest;
|
private CronetDataSource dataSourceUnderTest;
|
||||||
@ -104,30 +97,31 @@ public final class CronetDataSourceTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this);
|
MockitoAnnotations.initMocks(this);
|
||||||
dataSourceUnderTest = spy(
|
dataSourceUnderTest =
|
||||||
new CronetDataSource(
|
spy(
|
||||||
mockCronetEngine,
|
new CronetDataSource(
|
||||||
mockExecutor,
|
mockCronetEngine,
|
||||||
mockContentTypePredicate,
|
mockExecutor,
|
||||||
mockTransferListener,
|
mockContentTypePredicate,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
mockTransferListener,
|
||||||
TEST_READ_TIMEOUT_MS,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
true, // resetTimeoutOnRedirects
|
TEST_READ_TIMEOUT_MS,
|
||||||
mockClock,
|
true, // resetTimeoutOnRedirects
|
||||||
null,
|
Clock.DEFAULT,
|
||||||
false));
|
null,
|
||||||
|
false));
|
||||||
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
|
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
|
||||||
when(mockCronetEngine.newUrlRequestBuilder(
|
when(mockCronetEngine.newUrlRequestBuilder(
|
||||||
anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
|
anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
|
||||||
.thenReturn(mockUrlRequestBuilder);
|
.thenReturn(mockUrlRequestBuilder);
|
||||||
when(mockUrlRequestBuilder.allowDirectExecutor()).thenReturn(mockUrlRequestBuilder);
|
when(mockUrlRequestBuilder.allowDirectExecutor()).thenReturn(mockUrlRequestBuilder);
|
||||||
when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest);
|
when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest);
|
||||||
mockStatusResponse();
|
mockStatusResponse();
|
||||||
|
|
||||||
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null);
|
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null);
|
||||||
testPostDataSpec = new DataSpec(
|
testPostDataSpec =
|
||||||
Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0);
|
new DataSpec(Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0);
|
||||||
testResponseHeader = new HashMap<>();
|
testResponseHeader = new HashMap<>();
|
||||||
testResponseHeader.put("Content-Type", TEST_CONTENT_TYPE);
|
testResponseHeader.put("Content-Type", TEST_CONTENT_TYPE);
|
||||||
// This value can be anything since the DataSpec is unset.
|
// 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.
|
// Prepare a mock UrlRequest to be used in the second open() call.
|
||||||
final UrlRequest mockUrlRequest2 = mock(UrlRequest.class);
|
final UrlRequest mockUrlRequest2 = mock(UrlRequest.class);
|
||||||
when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest2);
|
when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest2);
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
// Invoke the callback for the previous request.
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onFailed(
|
// Invoke the callback for the previous request.
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onFailed(
|
||||||
testUrlResponseInfo,
|
mockUrlRequest, testUrlResponseInfo, mockNetworkException);
|
||||||
mockNetworkException);
|
dataSourceUnderTest.onResponseStarted(mockUrlRequest2, testUrlResponseInfo);
|
||||||
dataSourceUnderTest.onResponseStarted(
|
return null;
|
||||||
mockUrlRequest2,
|
}
|
||||||
testUrlResponseInfo);
|
})
|
||||||
return null;
|
.when(mockUrlRequest2)
|
||||||
}
|
.start();
|
||||||
}).when(mockUrlRequest2).start();
|
|
||||||
dataSourceUnderTest.open(testDataSpec);
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,8 +246,8 @@ public final class CronetDataSourceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRequestOpenFailDueToDnsFailure() {
|
public void testRequestOpenFailDueToDnsFailure() {
|
||||||
mockResponseStartFailure();
|
mockResponseStartFailure();
|
||||||
when(mockNetworkException.getErrorCode()).thenReturn(
|
when(mockNetworkException.getErrorCode())
|
||||||
NetworkException.ERROR_HOSTNAME_NOT_RESOLVED);
|
.thenReturn(NetworkException.ERROR_HOSTNAME_NOT_RESOLVED);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dataSourceUnderTest.open(testDataSpec);
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
@ -524,8 +517,8 @@ public final class CronetDataSourceTest {
|
|||||||
assertThat(bytesOverRead).isEqualTo(C.RESULT_END_OF_INPUT);
|
assertThat(bytesOverRead).isEqualTo(C.RESULT_END_OF_INPUT);
|
||||||
assertThat(returnedBuffer).isEqualTo(new byte[16]);
|
assertThat(returnedBuffer).isEqualTo(new byte[16]);
|
||||||
// C.RESULT_END_OF_INPUT should not be reported though the TransferListener.
|
// C.RESULT_END_OF_INPUT should not be reported though the TransferListener.
|
||||||
verify(mockTransferListener, never()).onBytesTransferred(dataSourceUnderTest,
|
verify(mockTransferListener, never())
|
||||||
C.RESULT_END_OF_INPUT);
|
.onBytesTransferred(dataSourceUnderTest, C.RESULT_END_OF_INPUT);
|
||||||
// There should still be only one call to read on cronet.
|
// There should still be only one call to read on cronet.
|
||||||
verify(mockUrlRequest, times(1)).read(any(ByteBuffer.class));
|
verify(mockUrlRequest, times(1)).read(any(ByteBuffer.class));
|
||||||
// Check for connection not automatically closed.
|
// Check for connection not automatically closed.
|
||||||
@ -534,10 +527,10 @@ public final class CronetDataSourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectTimeout() {
|
public void testConnectTimeout() throws InterruptedException {
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(0L);
|
long startTimeMs = SystemClock.elapsedRealtime();
|
||||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||||
final ConditionVariable timedOutCondition = new ConditionVariable();
|
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
@ -551,29 +544,29 @@ public final class CronetDataSourceTest {
|
|||||||
assertThat(e.getCause() instanceof SocketTimeoutException).isTrue();
|
assertThat(e.getCause() instanceof SocketTimeoutException).isTrue();
|
||||||
assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus)
|
assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus)
|
||||||
.isEqualTo(TEST_CONNECTION_STATUS);
|
.isEqualTo(TEST_CONNECTION_STATUS);
|
||||||
timedOutCondition.open();
|
timedOutLatch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
startCondition.block();
|
startCondition.block();
|
||||||
|
|
||||||
// We should still be trying to open.
|
// 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.
|
// We should still be trying to open as we approach the timeout.
|
||||||
when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertThat(timedOutCondition.block(50)).isFalse();
|
assertNotCountedDown(timedOutLatch);
|
||||||
// Now we timeout.
|
// Now we timeout.
|
||||||
when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS + 10);
|
||||||
timedOutCondition.block();
|
timedOutLatch.await();
|
||||||
|
|
||||||
verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec);
|
verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectInterrupted() {
|
public void testConnectInterrupted() throws InterruptedException {
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(0L);
|
long startTimeMs = SystemClock.elapsedRealtime();
|
||||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||||
final ConditionVariable timedOutCondition = new ConditionVariable();
|
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
Thread thread =
|
Thread thread =
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@ -588,7 +581,7 @@ public final class CronetDataSourceTest {
|
|||||||
assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue();
|
assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue();
|
||||||
assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus)
|
assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus)
|
||||||
.isEqualTo(TEST_INVALID_CONNECTION_STATUS);
|
.isEqualTo(TEST_INVALID_CONNECTION_STATUS);
|
||||||
timedOutCondition.open();
|
timedOutLatch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -596,29 +589,29 @@ public final class CronetDataSourceTest {
|
|||||||
startCondition.block();
|
startCondition.block();
|
||||||
|
|
||||||
// We should still be trying to open.
|
// 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.
|
// We should still be trying to open as we approach the timeout.
|
||||||
when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertThat(timedOutCondition.block(50)).isFalse();
|
assertNotCountedDown(timedOutLatch);
|
||||||
// Now we interrupt.
|
// Now we interrupt.
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
timedOutCondition.block();
|
timedOutLatch.await();
|
||||||
|
|
||||||
verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec);
|
verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectResponseBeforeTimeout() {
|
public void testConnectResponseBeforeTimeout() throws InterruptedException {
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(0L);
|
long startTimeMs = SystemClock.elapsedRealtime();
|
||||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||||
final ConditionVariable openCondition = new ConditionVariable();
|
final CountDownLatch openLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
dataSourceUnderTest.open(testDataSpec);
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
openCondition.open();
|
openLatch.countDown();
|
||||||
} catch (HttpDataSourceException e) {
|
} catch (HttpDataSourceException e) {
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
@ -627,20 +620,20 @@ public final class CronetDataSourceTest {
|
|||||||
startCondition.block();
|
startCondition.block();
|
||||||
|
|
||||||
// We should still be trying to open.
|
// 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.
|
// We should still be trying to open as we approach the timeout.
|
||||||
when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertThat(openCondition.block(50)).isFalse();
|
assertNotCountedDown(openLatch);
|
||||||
// The response arrives just in time.
|
// The response arrives just in time.
|
||||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||||
openCondition.block();
|
openLatch.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectIncreasesConnectionTimeout() throws InterruptedException {
|
public void testRedirectIncreasesConnectionTimeout() throws InterruptedException {
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(0L);
|
long startTimeMs = SystemClock.elapsedRealtime();
|
||||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||||
final ConditionVariable timedOutCondition = new ConditionVariable();
|
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
||||||
final AtomicInteger openExceptions = new AtomicInteger(0);
|
final AtomicInteger openExceptions = new AtomicInteger(0);
|
||||||
|
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@ -654,40 +647,36 @@ public final class CronetDataSourceTest {
|
|||||||
assertThat(e instanceof CronetDataSource.OpenException).isTrue();
|
assertThat(e instanceof CronetDataSource.OpenException).isTrue();
|
||||||
assertThat(e.getCause() instanceof SocketTimeoutException).isTrue();
|
assertThat(e.getCause() instanceof SocketTimeoutException).isTrue();
|
||||||
openExceptions.getAndIncrement();
|
openExceptions.getAndIncrement();
|
||||||
timedOutCondition.open();
|
timedOutLatch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
startCondition.block();
|
startCondition.block();
|
||||||
|
|
||||||
// We should still be trying to open.
|
// 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.
|
// We should still be trying to open as we approach the timeout.
|
||||||
when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertThat(timedOutCondition.block(50)).isFalse();
|
assertNotCountedDown(timedOutLatch);
|
||||||
// A redirect arrives just in time.
|
// A redirect arrives just in time.
|
||||||
dataSourceUnderTest.onRedirectReceived(mockUrlRequest, testUrlResponseInfo,
|
dataSourceUnderTest.onRedirectReceived(
|
||||||
"RandomRedirectedUrl1");
|
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1");
|
||||||
|
|
||||||
long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1;
|
long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1;
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs - 1);
|
||||||
// Give the thread some time to run.
|
|
||||||
assertThat(timedOutCondition.block(newTimeoutMs)).isFalse();
|
|
||||||
// We should still be trying to open as we approach the new timeout.
|
// 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.
|
// A redirect arrives just in time.
|
||||||
dataSourceUnderTest.onRedirectReceived(mockUrlRequest, testUrlResponseInfo,
|
dataSourceUnderTest.onRedirectReceived(
|
||||||
"RandomRedirectedUrl2");
|
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2");
|
||||||
|
|
||||||
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs - 1);
|
||||||
// Give the thread some time to run.
|
|
||||||
assertThat(timedOutCondition.block(newTimeoutMs)).isFalse();
|
|
||||||
// We should still be trying to open as we approach the new timeout.
|
// We should still be trying to open as we approach the new timeout.
|
||||||
assertThat(timedOutCondition.block(50)).isFalse();
|
assertNotCountedDown(timedOutLatch);
|
||||||
// Now we timeout.
|
// Now we timeout.
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs + 10);
|
||||||
timedOutCondition.block();
|
timedOutLatch.await();
|
||||||
|
|
||||||
verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec);
|
verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec);
|
||||||
assertThat(openExceptions.get()).isEqualTo(1);
|
assertThat(openExceptions.get()).isEqualTo(1);
|
||||||
@ -707,20 +696,22 @@ public final class CronetDataSourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders()
|
public void
|
||||||
throws HttpDataSourceException {
|
testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders()
|
||||||
dataSourceUnderTest = spy(
|
throws HttpDataSourceException {
|
||||||
new CronetDataSource(
|
dataSourceUnderTest =
|
||||||
mockCronetEngine,
|
spy(
|
||||||
mockExecutor,
|
new CronetDataSource(
|
||||||
mockContentTypePredicate,
|
mockCronetEngine,
|
||||||
mockTransferListener,
|
mockExecutor,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
mockContentTypePredicate,
|
||||||
TEST_READ_TIMEOUT_MS,
|
mockTransferListener,
|
||||||
true, // resetTimeoutOnRedirects
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
mockClock,
|
TEST_READ_TIMEOUT_MS,
|
||||||
null,
|
true, // resetTimeoutOnRedirects
|
||||||
true));
|
Clock.DEFAULT,
|
||||||
|
null,
|
||||||
|
true));
|
||||||
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||||
|
|
||||||
mockSingleRedirectSuccess();
|
mockSingleRedirectSuccess();
|
||||||
@ -736,21 +727,23 @@ public final class CronetDataSourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeadersIncludingByteRangeHeader()
|
public void
|
||||||
throws HttpDataSourceException {
|
testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeadersIncludingByteRangeHeader()
|
||||||
|
throws HttpDataSourceException {
|
||||||
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null);
|
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null);
|
||||||
dataSourceUnderTest = spy(
|
dataSourceUnderTest =
|
||||||
new CronetDataSource(
|
spy(
|
||||||
mockCronetEngine,
|
new CronetDataSource(
|
||||||
mockExecutor,
|
mockCronetEngine,
|
||||||
mockContentTypePredicate,
|
mockExecutor,
|
||||||
mockTransferListener,
|
mockContentTypePredicate,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
mockTransferListener,
|
||||||
TEST_READ_TIMEOUT_MS,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
true, // resetTimeoutOnRedirects
|
TEST_READ_TIMEOUT_MS,
|
||||||
mockClock,
|
true, // resetTimeoutOnRedirects
|
||||||
null,
|
Clock.DEFAULT,
|
||||||
true));
|
null,
|
||||||
|
true));
|
||||||
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||||
|
|
||||||
mockSingleRedirectSuccess();
|
mockSingleRedirectSuccess();
|
||||||
@ -778,18 +771,19 @@ public final class CronetDataSourceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie()
|
public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie()
|
||||||
throws HttpDataSourceException {
|
throws HttpDataSourceException {
|
||||||
dataSourceUnderTest = spy(
|
dataSourceUnderTest =
|
||||||
new CronetDataSource(
|
spy(
|
||||||
mockCronetEngine,
|
new CronetDataSource(
|
||||||
mockExecutor,
|
mockCronetEngine,
|
||||||
mockContentTypePredicate,
|
mockExecutor,
|
||||||
mockTransferListener,
|
mockContentTypePredicate,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
mockTransferListener,
|
||||||
TEST_READ_TIMEOUT_MS,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
true, // resetTimeoutOnRedirects
|
TEST_READ_TIMEOUT_MS,
|
||||||
mockClock,
|
true, // resetTimeoutOnRedirects
|
||||||
null,
|
Clock.DEFAULT,
|
||||||
true));
|
null,
|
||||||
|
true));
|
||||||
mockSingleRedirectSuccess();
|
mockSingleRedirectSuccess();
|
||||||
mockFollowRedirectSuccess();
|
mockFollowRedirectSuccess();
|
||||||
|
|
||||||
@ -804,8 +798,9 @@ public final class CronetDataSourceTest {
|
|||||||
|
|
||||||
// Make mockTransferListener throw an exception in CronetDataSource.close(). Ensure that
|
// Make mockTransferListener throw an exception in CronetDataSource.close(). Ensure that
|
||||||
// the subsequent open() call succeeds.
|
// the subsequent open() call succeeds.
|
||||||
doThrow(new NullPointerException()).when(mockTransferListener).onTransferEnd(
|
doThrow(new NullPointerException())
|
||||||
dataSourceUnderTest);
|
.when(mockTransferListener)
|
||||||
|
.onTransferEnd(dataSourceUnderTest);
|
||||||
dataSourceUnderTest.open(testDataSpec);
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
try {
|
try {
|
||||||
dataSourceUnderTest.close();
|
dataSourceUnderTest.close();
|
||||||
@ -833,13 +828,12 @@ public final class CronetDataSourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadInterrupted() throws HttpDataSourceException {
|
public void testReadInterrupted() throws HttpDataSourceException, InterruptedException {
|
||||||
when(mockClock.elapsedRealtime()).thenReturn(0L);
|
|
||||||
mockResponseStartSuccess();
|
mockResponseStartSuccess();
|
||||||
dataSourceUnderTest.open(testDataSpec);
|
dataSourceUnderTest.open(testDataSpec);
|
||||||
|
|
||||||
final ConditionVariable startCondition = buildReadStartedCondition();
|
final ConditionVariable startCondition = buildReadStartedCondition();
|
||||||
final ConditionVariable timedOutCondition = new ConditionVariable();
|
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
||||||
byte[] returnedBuffer = new byte[8];
|
byte[] returnedBuffer = new byte[8];
|
||||||
Thread thread =
|
Thread thread =
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@ -851,17 +845,17 @@ public final class CronetDataSourceTest {
|
|||||||
} catch (HttpDataSourceException e) {
|
} catch (HttpDataSourceException e) {
|
||||||
// Expected.
|
// Expected.
|
||||||
assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue();
|
assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue();
|
||||||
timedOutCondition.open();
|
timedOutLatch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
thread.start();
|
thread.start();
|
||||||
startCondition.block();
|
startCondition.block();
|
||||||
|
|
||||||
assertThat(timedOutCondition.block(50)).isFalse();
|
assertNotCountedDown(timedOutLatch);
|
||||||
// Now we interrupt.
|
// Now we interrupt.
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
timedOutCondition.block();
|
timedOutLatch.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -876,122 +870,135 @@ public final class CronetDataSourceTest {
|
|||||||
// Helper methods.
|
// Helper methods.
|
||||||
|
|
||||||
private void mockStatusResponse() {
|
private void mockStatusResponse() {
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
UrlRequest.StatusListener statusListener =
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
(UrlRequest.StatusListener) invocation.getArguments()[0];
|
UrlRequest.StatusListener statusListener =
|
||||||
statusListener.onStatus(TEST_CONNECTION_STATUS);
|
(UrlRequest.StatusListener) invocation.getArguments()[0];
|
||||||
return null;
|
statusListener.onStatus(TEST_CONNECTION_STATUS);
|
||||||
}
|
return null;
|
||||||
}).when(mockUrlRequest).getStatus(any(UrlRequest.StatusListener.class));
|
}
|
||||||
|
})
|
||||||
|
.when(mockUrlRequest)
|
||||||
|
.getStatus(any(UrlRequest.StatusListener.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockResponseStartSuccess() {
|
private void mockResponseStartSuccess() {
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
dataSourceUnderTest.onResponseStarted(
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||||
testUrlResponseInfo);
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
})
|
||||||
}).when(mockUrlRequest).start();
|
.when(mockUrlRequest)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockResponseStartRedirect() {
|
private void mockResponseStartRedirect() {
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
dataSourceUnderTest.onRedirectReceived(
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onRedirectReceived(
|
||||||
createUrlResponseInfo(307), // statusCode
|
mockUrlRequest,
|
||||||
"http://redirect.location.com");
|
createUrlResponseInfo(307), // statusCode
|
||||||
return null;
|
"http://redirect.location.com");
|
||||||
}
|
return null;
|
||||||
}).when(mockUrlRequest).start();
|
}
|
||||||
|
})
|
||||||
|
.when(mockUrlRequest)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockSingleRedirectSuccess() {
|
private void mockSingleRedirectSuccess() {
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
if (!redirectCalled) {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
redirectCalled = true;
|
if (!redirectCalled) {
|
||||||
dataSourceUnderTest.onRedirectReceived(
|
redirectCalled = true;
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onRedirectReceived(
|
||||||
createUrlResponseInfoWithUrl("http://example.com/video", 300),
|
mockUrlRequest,
|
||||||
"http://example.com/video/redirect");
|
createUrlResponseInfoWithUrl("http://example.com/video", 300),
|
||||||
} else {
|
"http://example.com/video/redirect");
|
||||||
dataSourceUnderTest.onResponseStarted(
|
} else {
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||||
testUrlResponseInfo);
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
})
|
||||||
}).when(mockUrlRequest).start();
|
.when(mockUrlRequest)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockFollowRedirectSuccess() {
|
private void mockFollowRedirectSuccess() {
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
dataSourceUnderTest.onResponseStarted(
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||||
testUrlResponseInfo);
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
})
|
||||||
}).when(mockUrlRequest).followRedirect();
|
.when(mockUrlRequest)
|
||||||
|
.followRedirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockResponseStartFailure() {
|
private void mockResponseStartFailure() {
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
dataSourceUnderTest.onFailed(
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onFailed(
|
||||||
createUrlResponseInfo(500), // statusCode
|
mockUrlRequest,
|
||||||
mockNetworkException);
|
createUrlResponseInfo(500), // statusCode
|
||||||
return null;
|
mockNetworkException);
|
||||||
}
|
return null;
|
||||||
}).when(mockUrlRequest).start();
|
}
|
||||||
|
})
|
||||||
|
.when(mockUrlRequest)
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockReadSuccess(int position, int length) {
|
private void mockReadSuccess(int position, int length) {
|
||||||
final int[] positionAndRemaining = new int[] {position, length};
|
final int[] positionAndRemaining = new int[] {position, length};
|
||||||
doAnswer(new Answer<Void>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Void>() {
|
||||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
if (positionAndRemaining[1] == 0) {
|
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo);
|
if (positionAndRemaining[1] == 0) {
|
||||||
} else {
|
dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo);
|
||||||
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
} else {
|
||||||
int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining());
|
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
||||||
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining());
|
||||||
positionAndRemaining[0] += readLength;
|
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
||||||
positionAndRemaining[1] -= readLength;
|
positionAndRemaining[0] += readLength;
|
||||||
dataSourceUnderTest.onReadCompleted(
|
positionAndRemaining[1] -= readLength;
|
||||||
mockUrlRequest,
|
dataSourceUnderTest.onReadCompleted(
|
||||||
testUrlResponseInfo,
|
mockUrlRequest, testUrlResponseInfo, inputBuffer);
|
||||||
inputBuffer);
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
})
|
||||||
}).when(mockUrlRequest).read(any(ByteBuffer.class));
|
.when(mockUrlRequest)
|
||||||
|
.read(any(ByteBuffer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockReadFailure() {
|
private void mockReadFailure() {
|
||||||
doAnswer(
|
doAnswer(
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onFailed(
|
dataSourceUnderTest.onFailed(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(500), // statusCode
|
createUrlResponseInfo(500), // statusCode
|
||||||
mockNetworkException);
|
mockNetworkException);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.when(mockUrlRequest)
|
.when(mockUrlRequest)
|
||||||
.read(any(ByteBuffer.class));
|
.read(any(ByteBuffer.class));
|
||||||
}
|
}
|
||||||
@ -999,13 +1006,13 @@ public final class CronetDataSourceTest {
|
|||||||
private ConditionVariable buildReadStartedCondition() {
|
private ConditionVariable buildReadStartedCondition() {
|
||||||
final ConditionVariable startedCondition = new ConditionVariable();
|
final ConditionVariable startedCondition = new ConditionVariable();
|
||||||
doAnswer(
|
doAnswer(
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
startedCondition.open();
|
startedCondition.open();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.when(mockUrlRequest)
|
.when(mockUrlRequest)
|
||||||
.read(any(ByteBuffer.class));
|
.read(any(ByteBuffer.class));
|
||||||
return startedCondition;
|
return startedCondition;
|
||||||
@ -1013,16 +1020,26 @@ public final class CronetDataSourceTest {
|
|||||||
|
|
||||||
private ConditionVariable buildUrlRequestStartedCondition() {
|
private ConditionVariable buildUrlRequestStartedCondition() {
|
||||||
final ConditionVariable startedCondition = new ConditionVariable();
|
final ConditionVariable startedCondition = new ConditionVariable();
|
||||||
doAnswer(new Answer<Object>() {
|
doAnswer(
|
||||||
@Override
|
new Answer<Object>() {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
@Override
|
||||||
startedCondition.open();
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
return null;
|
startedCondition.open();
|
||||||
}
|
return null;
|
||||||
}).when(mockUrlRequest).start();
|
}
|
||||||
|
})
|
||||||
|
.when(mockUrlRequest)
|
||||||
|
.start();
|
||||||
return startedCondition;
|
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) {
|
private static byte[] buildTestDataArray(int position, int length) {
|
||||||
return buildTestDataBuffer(position, length).array();
|
return buildTestDataBuffer(position, length).array();
|
||||||
}
|
}
|
||||||
@ -1045,5 +1062,4 @@ public final class CronetDataSourceTest {
|
|||||||
testBuffer.flip();
|
testBuffer.flip();
|
||||||
return testBuffer;
|
return testBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1 @@
|
|||||||
|
manifest=src/test/AndroidManifest.xml
|
@ -31,6 +31,7 @@ android {
|
|||||||
}
|
}
|
||||||
test {
|
test {
|
||||||
java.srcDirs += "../../testutils/src/main/java/"
|
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());
|
ContentDataSource dataSource = new ContentDataSource(instrumentation.getContext());
|
||||||
try {
|
try {
|
||||||
DataSpec dataSpec = new DataSpec(contentUri, offset, length, null);
|
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,
|
byte[] expectedData = Arrays.copyOfRange(completeData, offset,
|
||||||
length == C.LENGTH_UNSET ? completeData.length : offset + length);
|
length == C.LENGTH_UNSET ? completeData.length : offset + length);
|
||||||
TestUtil.assertDataSourceContent(dataSource, dataSpec, expectedData, !pipeMode);
|
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.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTrackSelection;
|
import com.google.android.exoplayer2.testutil.FakeTrackSelection;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
|
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -22,8 +22,8 @@ import static org.mockito.Mockito.when;
|
|||||||
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import com.google.android.exoplayer2.C;
|
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.drm.DrmInitData.SchemeData;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -21,11 +21,11 @@ import static org.junit.Assert.fail;
|
|||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
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.DownloadListener;
|
||||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
||||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State;
|
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State;
|
||||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
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.DummyDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.Cache;
|
import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
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.C;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.RobolectricUtil;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.Timeline.Period;
|
import com.google.android.exoplayer2.Timeline.Period;
|
||||||
import com.google.android.exoplayer2.Timeline.Window;
|
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;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.junit.Before;
|
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.C;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.RobolectricUtil;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
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;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify;
|
|||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.RobolectricUtil;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
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;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
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.C;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.RobolectricUtil;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -19,13 +19,13 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.RobolectricUtil;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource.IllegalMergeException;
|
import com.google.android.exoplayer2.source.MergingMediaSource.IllegalMergeException;
|
||||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||||
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
@ -35,15 +35,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(modulePrefix + 'library-core')
|
compile project(modulePrefix + 'library-core')
|
||||||
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||||
androidTestCompile project(modulePrefix + 'testutils')
|
testCompile project(modulePrefix + 'testutils-robolectric')
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -1113,7 +1114,9 @@ public final class DashMediaSource implements MediaSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long parse(Uri uri, InputStream inputStream) throws IOException {
|
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 {
|
try {
|
||||||
Matcher matcher = TIMESTAMP_WITH_TIMEZONE_PATTERN.matcher(firstLine);
|
Matcher matcher = TIMESTAMP_WITH_TIMEZONE_PATTERN.matcher(firstLine);
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
|
@ -18,16 +18,6 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.google.android.exoplayer2.source.dash.test">
|
package="com.google.android.exoplayer2.source.dash.test">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="25"/>
|
||||||
|
|
||||||
<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"/>
|
|
||||||
|
|
||||||
</manifest>
|
</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.upstream.DummyDataSource;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import java.util.Arrays;
|
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}. */
|
||||||
* Unit tests for {@link DashUtil}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public final class DashUtilTest {
|
||||||
public final class DashUtilTest extends TestCase {
|
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLoadDrmInitDataFromManifest() throws Exception {
|
public void testLoadDrmInitDataFromManifest() throws Exception {
|
||||||
Period period = newPeriod(newAdaptationSets(newRepresentations(newDrmInitData())));
|
Period period = newPeriod(newAdaptationSets(newRepresentations(newDrmInitData())));
|
||||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
||||||
assertThat(drmInitData).isEqualTo(newDrmInitData());
|
assertThat(drmInitData).isEqualTo(newDrmInitData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLoadDrmInitDataMissing() throws Exception {
|
public void testLoadDrmInitDataMissing() throws Exception {
|
||||||
Period period = newPeriod(newAdaptationSets(newRepresentations(null /* no init data */)));
|
Period period = newPeriod(newAdaptationSets(newRepresentations(null /* no init data */)));
|
||||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
||||||
assertThat(drmInitData).isNull();
|
assertThat(drmInitData).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLoadDrmInitDataNoRepresentations() throws Exception {
|
public void testLoadDrmInitDataNoRepresentations() throws Exception {
|
||||||
Period period = newPeriod(newAdaptationSets(/* no representation */));
|
Period period = newPeriod(newAdaptationSets(/* no representation */ ));
|
||||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
||||||
assertThat(drmInitData).isNull();
|
assertThat(drmInitData).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLoadDrmInitDataNoAdaptationSets() throws Exception {
|
public void testLoadDrmInitDataNoAdaptationSets() throws Exception {
|
||||||
Period period = newPeriod(/* no adaptation set */);
|
Period period = newPeriod(/* no adaptation set */ );
|
||||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
|
||||||
assertThat(drmInitData).isNull();
|
assertThat(drmInitData).isNull();
|
||||||
}
|
}
|
||||||
@ -68,8 +73,18 @@ public final class DashUtilTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Representation newRepresentations(DrmInitData drmInitData) {
|
private static Representation newRepresentations(DrmInitData drmInitData) {
|
||||||
Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4,
|
Format format =
|
||||||
MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0);
|
Format.createVideoContainerFormat(
|
||||||
|
"id",
|
||||||
|
MimeTypes.VIDEO_MP4,
|
||||||
|
MimeTypes.VIDEO_H264,
|
||||||
|
"",
|
||||||
|
Format.NO_VALUE,
|
||||||
|
1024,
|
||||||
|
768,
|
||||||
|
Format.NO_VALUE,
|
||||||
|
null,
|
||||||
|
0);
|
||||||
if (drmInitData != null) {
|
if (drmInitData != null) {
|
||||||
format = format.copyWithDrmInitData(drmInitData);
|
format = format.copyWithDrmInitData(drmInitData);
|
||||||
}
|
}
|
||||||
@ -77,8 +92,7 @@ public final class DashUtilTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static DrmInitData newDrmInitData() {
|
private static DrmInitData newDrmInitData() {
|
||||||
return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType",
|
return new DrmInitData(
|
||||||
new byte[] {1, 4, 7, 0, 3, 6}));
|
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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.net.Uri;
|
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.Format;
|
||||||
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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}. */
|
||||||
* Unit tests for {@link DashManifestParser}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class DashManifestParserTest {
|
||||||
public class DashManifestParserTest extends InstrumentationTestCase {
|
|
||||||
|
|
||||||
private static final String SAMPLE_MPD_1 = "sample_mpd_1";
|
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_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_3_SEGMENT_TEMPLATE = "sample_mpd_3_segment_template";
|
||||||
private static final String SAMPLE_MPD_4_EVENT_STREAM = "sample_mpd_4_event_stream";
|
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 {
|
public void testParseMediaPresentationDescription() throws IOException {
|
||||||
DashManifestParser parser = new DashManifestParser();
|
DashManifestParser parser = new DashManifestParser();
|
||||||
parser.parse(Uri.parse("https://example.com/test.mpd"),
|
parser.parse(
|
||||||
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_1));
|
Uri.parse("https://example.com/test.mpd"),
|
||||||
parser.parse(Uri.parse("https://example.com/test.mpd"),
|
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_1));
|
||||||
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_2_UNKNOWN_MIME_TYPE));
|
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 {
|
public void testParseMediaPresentationDescriptionWithSegmentTemplate() throws IOException {
|
||||||
DashManifestParser parser = new DashManifestParser();
|
DashManifestParser parser = new DashManifestParser();
|
||||||
DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"),
|
DashManifest mpd =
|
||||||
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_3_SEGMENT_TEMPLATE));
|
parser.parse(
|
||||||
|
Uri.parse("https://example.com/test.mpd"),
|
||||||
|
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_3_SEGMENT_TEMPLATE));
|
||||||
|
|
||||||
assertThat(mpd.getPeriodCount()).isEqualTo(1);
|
assertThat(mpd.getPeriodCount()).isEqualTo(1);
|
||||||
|
|
||||||
@ -75,11 +83,13 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseMediaPresentationDescriptionCanParseEventStream()
|
@Test
|
||||||
throws IOException {
|
public void testParseMediaPresentationDescriptionCanParseEventStream() throws IOException {
|
||||||
DashManifestParser parser = new DashManifestParser();
|
DashManifestParser parser = new DashManifestParser();
|
||||||
DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"),
|
DashManifest mpd =
|
||||||
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_4_EVENT_STREAM));
|
parser.parse(
|
||||||
|
Uri.parse("https://example.com/test.mpd"),
|
||||||
|
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_4_EVENT_STREAM));
|
||||||
|
|
||||||
Period period = mpd.getPeriod(0);
|
Period period = mpd.getPeriod(0);
|
||||||
assertThat(period.eventStreams).hasSize(3);
|
assertThat(period.eventStreams).hasSize(3);
|
||||||
@ -87,8 +97,14 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
|||||||
// assert text-only event stream
|
// assert text-only event stream
|
||||||
EventStream eventStream1 = period.eventStreams.get(0);
|
EventStream eventStream1 = period.eventStreams.get(0);
|
||||||
assertThat(eventStream1.events.length).isEqualTo(1);
|
assertThat(eventStream1.events.length).isEqualTo(1);
|
||||||
EventMessage expectedEvent1 = new EventMessage("urn:uuid:XYZY", "call", 10000, 0,
|
EventMessage expectedEvent1 =
|
||||||
"+ 1 800 10101010".getBytes(), 0);
|
new EventMessage(
|
||||||
|
"urn:uuid:XYZY",
|
||||||
|
"call",
|
||||||
|
10000,
|
||||||
|
0,
|
||||||
|
"+ 1 800 10101010".getBytes(Charset.forName(C.UTF8_NAME)),
|
||||||
|
0);
|
||||||
assertThat(eventStream1.events[0]).isEqualTo(expectedEvent1);
|
assertThat(eventStream1.events[0]).isEqualTo(expectedEvent1);
|
||||||
|
|
||||||
// assert CData-structured event stream
|
// assert CData-structured event stream
|
||||||
@ -135,6 +151,7 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
|||||||
1000000000));
|
1000000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParseCea608AccessibilityChannel() {
|
public void testParseCea608AccessibilityChannel() {
|
||||||
assertThat(
|
assertThat(
|
||||||
DashManifestParser.parseCea608AccessibilityChannel(
|
DashManifestParser.parseCea608AccessibilityChannel(
|
||||||
@ -175,6 +192,7 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
|||||||
.isEqualTo(Format.NO_VALUE);
|
.isEqualTo(Format.NO_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParseCea708AccessibilityChannel() {
|
public void testParseCea708AccessibilityChannel() {
|
||||||
assertThat(
|
assertThat(
|
||||||
DashManifestParser.parseCea708AccessibilityChannel(
|
DashManifestParser.parseCea708AccessibilityChannel(
|
||||||
@ -226,5 +244,4 @@ public class DashManifestParserTest extends InstrumentationTestCase {
|
|||||||
private static List<Descriptor> buildCea708AccessibilityDescriptors(String value) {
|
private static List<Descriptor> buildCea708AccessibilityDescriptors(String value) {
|
||||||
return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-708:2015", value, null));
|
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.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
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}. */
|
||||||
* Unit tests for {@link DashManifest}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class DashManifestTest {
|
||||||
public class DashManifestTest extends TestCase {
|
|
||||||
|
|
||||||
private static final UtcTimingElement DUMMY_UTC_TIMING = new UtcTimingElement("", "");
|
private static final UtcTimingElement DUMMY_UTC_TIMING = new UtcTimingElement("", "");
|
||||||
private static final SingleSegmentBase DUMMY_SEGMENT_BASE = new SingleSegmentBase();
|
private static final SingleSegmentBase DUMMY_SEGMENT_BASE = new SingleSegmentBase();
|
||||||
private static final Format DUMMY_FORMAT = Format.createSampleFormat("", "", 0);
|
private static final Format DUMMY_FORMAT = Format.createSampleFormat("", "", 0);
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCopy() throws Exception {
|
public void testCopy() throws Exception {
|
||||||
Representation[][][] representations = newRepresentations(3, 2, 3);
|
Representation[][][] representations = newRepresentations(3, 2, 3);
|
||||||
DashManifest sourceManifest = newDashManifest(10,
|
DashManifest sourceManifest =
|
||||||
newPeriod("1", 1,
|
newDashManifest(
|
||||||
newAdaptationSet(2, representations[0][0]),
|
10,
|
||||||
newAdaptationSet(3, representations[0][1])),
|
newPeriod(
|
||||||
newPeriod("4", 4,
|
"1",
|
||||||
newAdaptationSet(5, representations[1][0]),
|
1,
|
||||||
newAdaptationSet(6, representations[1][1])),
|
newAdaptationSet(2, representations[0][0]),
|
||||||
newPeriod("7", 7,
|
newAdaptationSet(3, representations[0][1])),
|
||||||
newAdaptationSet(8, representations[2][0]),
|
newPeriod(
|
||||||
newAdaptationSet(9, representations[2][1])));
|
"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(
|
List<RepresentationKey> keys =
|
||||||
new RepresentationKey(0, 0, 0),
|
Arrays.asList(
|
||||||
new RepresentationKey(0, 0, 1),
|
new RepresentationKey(0, 0, 0),
|
||||||
new RepresentationKey(0, 1, 2),
|
new RepresentationKey(0, 0, 1),
|
||||||
|
new RepresentationKey(0, 1, 2),
|
||||||
new RepresentationKey(1, 0, 1),
|
new RepresentationKey(1, 0, 1),
|
||||||
new RepresentationKey(1, 1, 0),
|
new RepresentationKey(1, 1, 0),
|
||||||
new RepresentationKey(1, 1, 2),
|
new RepresentationKey(1, 1, 2),
|
||||||
|
new RepresentationKey(2, 0, 1),
|
||||||
new RepresentationKey(2, 0, 1),
|
new RepresentationKey(2, 0, 2),
|
||||||
new RepresentationKey(2, 0, 2),
|
new RepresentationKey(2, 1, 0));
|
||||||
new RepresentationKey(2, 1, 0));
|
|
||||||
// Keys don't need to be in any particular order
|
// Keys don't need to be in any particular order
|
||||||
Collections.shuffle(keys, new Random(0));
|
Collections.shuffle(keys, new Random(0));
|
||||||
|
|
||||||
DashManifest copyManifest = sourceManifest.copy(keys);
|
DashManifest copyManifest = sourceManifest.copy(keys);
|
||||||
|
|
||||||
DashManifest expectedManifest = newDashManifest(10,
|
DashManifest expectedManifest =
|
||||||
newPeriod("1", 1,
|
newDashManifest(
|
||||||
newAdaptationSet(2, representations[0][0][0], representations[0][0][1]),
|
10,
|
||||||
newAdaptationSet(3, representations[0][1][2])),
|
newPeriod(
|
||||||
newPeriod("4", 4,
|
"1",
|
||||||
newAdaptationSet(5, representations[1][0][1]),
|
1,
|
||||||
newAdaptationSet(6, representations[1][1][0], representations[1][1][2])),
|
newAdaptationSet(2, representations[0][0][0], representations[0][0][1]),
|
||||||
newPeriod("7", 7,
|
newAdaptationSet(3, representations[0][1][2])),
|
||||||
newAdaptationSet(8, representations[2][0][1], representations[2][0][2]),
|
newPeriod(
|
||||||
newAdaptationSet(9, representations[2][1][0])));
|
"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);
|
assertManifestEquals(expectedManifest, copyManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCopySameAdaptationIndexButDifferentPeriod() throws Exception {
|
public void testCopySameAdaptationIndexButDifferentPeriod() throws Exception {
|
||||||
Representation[][][] representations = newRepresentations(2, 1, 1);
|
Representation[][][] representations = newRepresentations(2, 1, 1);
|
||||||
DashManifest sourceManifest = newDashManifest(10,
|
DashManifest sourceManifest =
|
||||||
newPeriod("1", 1,
|
newDashManifest(
|
||||||
newAdaptationSet(2, representations[0][0])),
|
10,
|
||||||
newPeriod("4", 4,
|
newPeriod("1", 1, newAdaptationSet(2, representations[0][0])),
|
||||||
newAdaptationSet(5, representations[1][0])));
|
newPeriod("4", 4, newAdaptationSet(5, representations[1][0])));
|
||||||
|
|
||||||
DashManifest copyManifest = sourceManifest.copy(Arrays.asList(
|
DashManifest copyManifest =
|
||||||
new RepresentationKey(0, 0, 0),
|
sourceManifest.copy(
|
||||||
new RepresentationKey(1, 0, 0)));
|
Arrays.asList(new RepresentationKey(0, 0, 0), new RepresentationKey(1, 0, 0)));
|
||||||
|
|
||||||
DashManifest expectedManifest = newDashManifest(10,
|
DashManifest expectedManifest =
|
||||||
newPeriod("1", 1,
|
newDashManifest(
|
||||||
newAdaptationSet(2, representations[0][0])),
|
10,
|
||||||
newPeriod("4", 4,
|
newPeriod("1", 1, newAdaptationSet(2, representations[0][0])),
|
||||||
newAdaptationSet(5, representations[1][0])));
|
newPeriod("4", 4, newAdaptationSet(5, representations[1][0])));
|
||||||
assertManifestEquals(expectedManifest, copyManifest);
|
assertManifestEquals(expectedManifest, copyManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCopySkipPeriod() throws Exception {
|
public void testCopySkipPeriod() throws Exception {
|
||||||
Representation[][][] representations = newRepresentations(3, 2, 3);
|
Representation[][][] representations = newRepresentations(3, 2, 3);
|
||||||
DashManifest sourceManifest = newDashManifest(10,
|
DashManifest sourceManifest =
|
||||||
newPeriod("1", 1,
|
newDashManifest(
|
||||||
newAdaptationSet(2, representations[0][0]),
|
10,
|
||||||
newAdaptationSet(3, representations[0][1])),
|
newPeriod(
|
||||||
newPeriod("4", 4,
|
"1",
|
||||||
newAdaptationSet(5, representations[1][0]),
|
1,
|
||||||
newAdaptationSet(6, representations[1][1])),
|
newAdaptationSet(2, representations[0][0]),
|
||||||
newPeriod("7", 7,
|
newAdaptationSet(3, representations[0][1])),
|
||||||
newAdaptationSet(8, representations[2][0]),
|
newPeriod(
|
||||||
newAdaptationSet(9, representations[2][1])));
|
"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(
|
DashManifest copyManifest =
|
||||||
new RepresentationKey(0, 0, 0),
|
sourceManifest.copy(
|
||||||
new RepresentationKey(0, 0, 1),
|
Arrays.asList(
|
||||||
new RepresentationKey(0, 1, 2),
|
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),
|
DashManifest expectedManifest =
|
||||||
new RepresentationKey(2, 0, 2),
|
newDashManifest(
|
||||||
new RepresentationKey(2, 1, 0)));
|
7,
|
||||||
|
newPeriod(
|
||||||
DashManifest expectedManifest = newDashManifest(7,
|
"1",
|
||||||
newPeriod("1", 1,
|
1,
|
||||||
newAdaptationSet(2, representations[0][0][0], representations[0][0][1]),
|
newAdaptationSet(2, representations[0][0][0], representations[0][0][1]),
|
||||||
newAdaptationSet(3, representations[0][1][2])),
|
newAdaptationSet(3, representations[0][1][2])),
|
||||||
newPeriod("7", 4,
|
newPeriod(
|
||||||
newAdaptationSet(8, representations[2][0][1], representations[2][0][2]),
|
"7",
|
||||||
newAdaptationSet(9, representations[2][1][0])));
|
4,
|
||||||
|
newAdaptationSet(8, representations[2][0][1], representations[2][0][2]),
|
||||||
|
newAdaptationSet(9, representations[2][1][0])));
|
||||||
assertManifestEquals(expectedManifest, copyManifest);
|
assertManifestEquals(expectedManifest, copyManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +198,8 @@ public class DashManifestTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Representation[][][] newRepresentations(int periodCount, int adaptationSetCounts,
|
private static Representation[][][] newRepresentations(
|
||||||
int representationCounts) {
|
int periodCount, int adaptationSetCounts, int representationCounts) {
|
||||||
Representation[][][] representations = new Representation[periodCount][][];
|
Representation[][][] representations = new Representation[periodCount][][];
|
||||||
for (int i = 0; i < periodCount; i++) {
|
for (int i = 0; i < periodCount; i++) {
|
||||||
representations[i] = new Representation[adaptationSetCounts][];
|
representations[i] = new Representation[adaptationSetCounts][];
|
||||||
@ -184,8 +218,8 @@ public class DashManifestTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static DashManifest newDashManifest(int duration, Period... periods) {
|
private static DashManifest newDashManifest(int duration, Period... periods) {
|
||||||
return new DashManifest(0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY,
|
return new DashManifest(
|
||||||
Arrays.asList(periods));
|
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) {
|
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) {
|
private static AdaptationSet newAdaptationSet(int seed, Representation... representations) {
|
||||||
return new AdaptationSet(++seed, ++seed, Arrays.asList(representations), null, null);
|
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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
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}. */
|
||||||
* Unit test for {@link RangedUri}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class RangedUriTest {
|
||||||
public class RangedUriTest extends TestCase {
|
|
||||||
|
|
||||||
private static final String BASE_URI = "http://www.test.com/";
|
private static final String BASE_URI = "http://www.test.com/";
|
||||||
private static final String PARTIAL_URI = "path/file.ext";
|
private static final String PARTIAL_URI = "path/file.ext";
|
||||||
private static final String FULL_URI = BASE_URI + PARTIAL_URI;
|
private static final String FULL_URI = BASE_URI + PARTIAL_URI;
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMerge() {
|
public void testMerge() {
|
||||||
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
|
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
|
||||||
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
|
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
|
||||||
@ -36,6 +38,7 @@ public class RangedUriTest extends TestCase {
|
|||||||
assertMerge(rangeA, rangeB, expected, null);
|
assertMerge(rangeA, rangeB, expected, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMergeUnbounded() {
|
public void testMergeUnbounded() {
|
||||||
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
|
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
|
||||||
RangedUri rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET);
|
RangedUri rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET);
|
||||||
@ -43,6 +46,7 @@ public class RangedUriTest extends TestCase {
|
|||||||
assertMerge(rangeA, rangeB, expected, null);
|
assertMerge(rangeA, rangeB, expected, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNonMerge() {
|
public void testNonMerge() {
|
||||||
// A and B do not overlap, so should not merge
|
// A and B do not overlap, so should not merge
|
||||||
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
|
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
|
||||||
@ -65,6 +69,7 @@ public class RangedUriTest extends TestCase {
|
|||||||
assertNonMerge(rangeA, rangeB, null);
|
assertNonMerge(rangeA, rangeB, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMergeWithBaseUri() {
|
public void testMergeWithBaseUri() {
|
||||||
RangedUri rangeA = new RangedUri(PARTIAL_URI, 0, 10);
|
RangedUri rangeA = new RangedUri(PARTIAL_URI, 0, 10);
|
||||||
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
|
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
|
||||||
@ -85,5 +90,4 @@ public class RangedUriTest extends TestCase {
|
|||||||
merged = rangeB.attemptMerge(rangeA, baseUrl);
|
merged = rangeB.attemptMerge(rangeA, baseUrl);
|
||||||
assertThat(merged).isNull();
|
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.Format;
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
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}. */
|
||||||
* Unit test for {@link Representation}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class RepresentationTest {
|
||||||
public class RepresentationTest extends TestCase {
|
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGetCacheKey() {
|
public void testGetCacheKey() {
|
||||||
String uri = "http://www.google.com";
|
String uri = "http://www.google.com";
|
||||||
SegmentBase base = new SingleSegmentBase(new RangedUri(null, 0, 1), 1, 0, 1, 1);
|
SegmentBase base = new SingleSegmentBase(new RangedUri(null, 0, 1), 1, 0, 1, 1);
|
||||||
Format format = Format.createVideoContainerFormat("0", MimeTypes.APPLICATION_MP4, null,
|
Format format =
|
||||||
MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0);
|
Format.createVideoContainerFormat(
|
||||||
Representation representation = Representation.newInstance("test_stream_1", 3, format, uri,
|
"0",
|
||||||
base);
|
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");
|
assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.0.3");
|
||||||
|
|
||||||
format = Format.createVideoContainerFormat("150", MimeTypes.APPLICATION_MP4, null,
|
format =
|
||||||
MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0);
|
Format.createVideoContainerFormat(
|
||||||
representation = Representation.newInstance("test_stream_1", Representation.REVISION_ID_DEFAULT,
|
"150",
|
||||||
format, uri, base);
|
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");
|
assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.150.-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -16,14 +16,17 @@
|
|||||||
package com.google.android.exoplayer2.source.dash.manifest;
|
package com.google.android.exoplayer2.source.dash.manifest;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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}. */
|
||||||
* Unit test for {@link UrlTemplate}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class UrlTemplateTest {
|
||||||
public class UrlTemplateTest extends TestCase {
|
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRealExamples() {
|
public void testRealExamples() {
|
||||||
String template = "QualityLevels($Bandwidth$)/Fragments(video=$Time$,format=mpd-time-csf)";
|
String template = "QualityLevels($Bandwidth$)/Fragments(video=$Time$,format=mpd-time-csf)";
|
||||||
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
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");
|
assertThat(url).isEqualTo("chunk_ctvideo_cfm4s_ridabc1_cn10_w2073857842_mpd.m4s");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFull() {
|
public void testFull() {
|
||||||
String template = "$Bandwidth$_a_$RepresentationID$_b_$Time$_c_$Number$";
|
String template = "$Bandwidth$_a_$RepresentationID$_b_$Time$_c_$Number$";
|
||||||
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
||||||
@ -48,6 +52,7 @@ public class UrlTemplateTest extends TestCase {
|
|||||||
assertThat(url).isEqualTo("650000_a_abc1_b_5000_c_10");
|
assertThat(url).isEqualTo("650000_a_abc1_b_5000_c_10");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFullWithDollarEscaping() {
|
public void testFullWithDollarEscaping() {
|
||||||
String template = "$$$Bandwidth$$$_a$$_$RepresentationID$_b_$Time$_c_$Number$$$";
|
String template = "$$$Bandwidth$$$_a$$_$RepresentationID$_b_$Time$_c_$Number$$$";
|
||||||
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
||||||
@ -55,6 +60,7 @@ public class UrlTemplateTest extends TestCase {
|
|||||||
assertThat(url).isEqualTo("$650000$_a$_abc1_b_5000_c_10$");
|
assertThat(url).isEqualTo("$650000$_a$_abc1_b_5000_c_10$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testInvalidSubstitution() {
|
public void testInvalidSubstitution() {
|
||||||
String template = "$IllegalId$";
|
String template = "$IllegalId$";
|
||||||
try {
|
try {
|
||||||
@ -64,5 +70,4 @@ public class UrlTemplateTest extends TestCase {
|
|||||||
// Expected.
|
// 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.assertCachedData;
|
||||||
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertDataCached;
|
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertDataCached;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.test.InstrumentationTestCase;
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.offline.DownloadException;
|
import com.google.android.exoplayer2.offline.DownloadException;
|
||||||
import com.google.android.exoplayer2.offline.Downloader.ProgressListener;
|
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.FakeDataSet;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSource.Factory;
|
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.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
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.InOrder;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/** Unit tests for {@link DashDownloader}. */
|
||||||
* Unit tests for {@link DashDownloader}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class DashDownloaderTest {
|
||||||
public class DashDownloaderTest extends InstrumentationTestCase {
|
|
||||||
|
|
||||||
private SimpleCache cache;
|
private SimpleCache cache;
|
||||||
private File tempFolder;
|
private File tempFolder;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
MockitoAnnotations.initMocks(this);
|
||||||
MockitoUtil.setUpMockito(this);
|
tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest");
|
||||||
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
|
|
||||||
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
Util.recursiveDelete(tempFolder);
|
Util.recursiveDelete(tempFolder);
|
||||||
super.tearDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGetManifest() throws Exception {
|
public void testGetManifest() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet = new FakeDataSet().setData(TEST_MPD_URI, TEST_MPD);
|
||||||
.setData(TEST_MPD_URI, TEST_MPD);
|
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
DashManifest manifest = dashDownloader.getManifest();
|
DashManifest manifest = dashDownloader.getManifest();
|
||||||
@ -80,15 +83,17 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadManifestFailure() throws Exception {
|
public void testDownloadManifestFailure() throws Exception {
|
||||||
byte[] testMpdFirstPart = Arrays.copyOf(TEST_MPD, 10);
|
byte[] testMpdFirstPart = Arrays.copyOf(TEST_MPD, 10);
|
||||||
byte[] testMpdSecondPart = Arrays.copyOfRange(TEST_MPD, 10, TEST_MPD.length);
|
byte[] testMpdSecondPart = Arrays.copyOfRange(TEST_MPD, 10, TEST_MPD.length);
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.newData(TEST_MPD_URI)
|
new FakeDataSet()
|
||||||
.appendReadData(testMpdFirstPart)
|
.newData(TEST_MPD_URI)
|
||||||
.appendReadError(new IOException())
|
.appendReadData(testMpdFirstPart)
|
||||||
.appendReadData(testMpdSecondPart)
|
.appendReadError(new IOException())
|
||||||
.endData();
|
.appendReadData(testMpdSecondPart)
|
||||||
|
.endData();
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
// fails on the first try
|
// fails on the first try
|
||||||
@ -108,13 +113,15 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadRepresentation() throws Exception {
|
public void testDownloadRepresentation() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6);
|
.setRandomData("audio_segment_2", 5)
|
||||||
|
.setRandomData("audio_segment_3", 6);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
||||||
@ -123,17 +130,19 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadRepresentationInSmallParts() throws Exception {
|
public void testDownloadRepresentationInSmallParts() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.newData("audio_segment_1")
|
.setRandomData("audio_init_data", 10)
|
||||||
.appendReadData(TestUtil.buildTestData(10))
|
.newData("audio_segment_1")
|
||||||
.appendReadData(TestUtil.buildTestData(10))
|
.appendReadData(TestUtil.buildTestData(10))
|
||||||
.appendReadData(TestUtil.buildTestData(10))
|
.appendReadData(TestUtil.buildTestData(10))
|
||||||
.endData()
|
.appendReadData(TestUtil.buildTestData(10))
|
||||||
.setRandomData("audio_segment_2", 5)
|
.endData()
|
||||||
.setRandomData("audio_segment_3", 6);
|
.setRandomData("audio_segment_2", 5)
|
||||||
|
.setRandomData("audio_segment_3", 6);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
||||||
@ -142,16 +151,18 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadRepresentations() throws Exception {
|
public void testDownloadRepresentations() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("text_segment_1", 1)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("text_segment_2", 2)
|
.setRandomData("text_segment_1", 1)
|
||||||
.setRandomData("text_segment_3", 3);
|
.setRandomData("text_segment_2", 2)
|
||||||
|
.setRandomData("text_segment_3", 3);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(
|
dashDownloader.selectRepresentations(
|
||||||
@ -161,19 +172,21 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadAllRepresentations() throws Exception {
|
public void testDownloadAllRepresentations() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("text_segment_1", 1)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("text_segment_2", 2)
|
.setRandomData("text_segment_1", 1)
|
||||||
.setRandomData("text_segment_3", 3)
|
.setRandomData("text_segment_2", 2)
|
||||||
.setRandomData("period_2_segment_1", 1)
|
.setRandomData("text_segment_3", 3)
|
||||||
.setRandomData("period_2_segment_2", 2)
|
.setRandomData("period_2_segment_1", 1)
|
||||||
.setRandomData("period_2_segment_3", 3);
|
.setRandomData("period_2_segment_2", 2)
|
||||||
|
.setRandomData("period_2_segment_3", 3);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
// dashDownloader.selectRepresentations() isn't called
|
// dashDownloader.selectRepresentations() isn't called
|
||||||
@ -190,21 +203,23 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
dashDownloader.remove();
|
dashDownloader.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testProgressiveDownload() throws Exception {
|
public void testProgressiveDownload() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("text_segment_1", 1)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("text_segment_2", 2)
|
.setRandomData("text_segment_1", 1)
|
||||||
.setRandomData("text_segment_3", 3);
|
.setRandomData("text_segment_2", 2)
|
||||||
|
.setRandomData("text_segment_3", 3);
|
||||||
FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet);
|
FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet);
|
||||||
Factory factory = mock(Factory.class);
|
Factory factory = mock(Factory.class);
|
||||||
when(factory.createDataSource()).thenReturn(fakeDataSource);
|
when(factory.createDataSource()).thenReturn(fakeDataSource);
|
||||||
DashDownloader dashDownloader = new DashDownloader(TEST_MPD_URI,
|
DashDownloader dashDownloader =
|
||||||
new DownloaderConstructorHelper(cache, factory));
|
new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory));
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(
|
dashDownloader.selectRepresentations(
|
||||||
new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)});
|
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");
|
assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("text_segment_3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testProgressiveDownloadSeparatePeriods() throws Exception {
|
public void testProgressiveDownloadSeparatePeriods() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("period_2_segment_1", 1)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("period_2_segment_2", 2)
|
.setRandomData("period_2_segment_1", 1)
|
||||||
.setRandomData("period_2_segment_3", 3);
|
.setRandomData("period_2_segment_2", 2)
|
||||||
|
.setRandomData("period_2_segment_3", 3);
|
||||||
FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet);
|
FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet);
|
||||||
Factory factory = mock(Factory.class);
|
Factory factory = mock(Factory.class);
|
||||||
when(factory.createDataSource()).thenReturn(fakeDataSource);
|
when(factory.createDataSource()).thenReturn(fakeDataSource);
|
||||||
DashDownloader dashDownloader = new DashDownloader(TEST_MPD_URI,
|
DashDownloader dashDownloader =
|
||||||
new DownloaderConstructorHelper(cache, factory));
|
new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory));
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(
|
dashDownloader.selectRepresentations(
|
||||||
new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(1, 0, 0)});
|
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");
|
assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("period_2_segment_3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadRepresentationFailure() throws Exception {
|
public void testDownloadRepresentationFailure() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.newData("audio_segment_2")
|
.setRandomData("audio_segment_1", 4)
|
||||||
.appendReadData(TestUtil.buildTestData(2))
|
.newData("audio_segment_2")
|
||||||
.appendReadError(new IOException())
|
.appendReadData(TestUtil.buildTestData(2))
|
||||||
.appendReadData(TestUtil.buildTestData(3))
|
.appendReadError(new IOException())
|
||||||
.endData()
|
.appendReadData(TestUtil.buildTestData(3))
|
||||||
.setRandomData("audio_segment_3", 6);
|
.endData()
|
||||||
|
.setRandomData("audio_segment_3", 6);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
||||||
@ -280,17 +299,19 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCounters() throws Exception {
|
public void testCounters() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.newData("audio_segment_2")
|
.setRandomData("audio_segment_1", 4)
|
||||||
.appendReadData(TestUtil.buildTestData(2))
|
.newData("audio_segment_2")
|
||||||
.appendReadError(new IOException())
|
.appendReadData(TestUtil.buildTestData(2))
|
||||||
.appendReadData(TestUtil.buildTestData(3))
|
.appendReadError(new IOException())
|
||||||
.endData()
|
.appendReadData(TestUtil.buildTestData(3))
|
||||||
.setRandomData("audio_segment_3", 6);
|
.endData()
|
||||||
|
.setRandomData("audio_segment_3", 6);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
assertCounters(dashDownloader, C.LENGTH_UNSET, C.LENGTH_UNSET, C.LENGTH_UNSET);
|
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);
|
assertCounters(dashDownloader, 4, 4, 10 + 4 + 5 + 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testListener() throws Exception {
|
public void testListener() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6);
|
.setRandomData("audio_segment_2", 5)
|
||||||
|
.setRandomData("audio_segment_3", 6);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
||||||
@ -335,16 +358,18 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
inOrder.verifyNoMoreInteractions();
|
inOrder.verifyNoMoreInteractions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRemoveAll() throws Exception {
|
public void testRemoveAll() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("text_segment_1", 1)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("text_segment_2", 2)
|
.setRandomData("text_segment_1", 1)
|
||||||
.setRandomData("text_segment_3", 3);
|
.setRandomData("text_segment_2", 2)
|
||||||
|
.setRandomData("text_segment_3", 3);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
dashDownloader.selectRepresentations(
|
dashDownloader.selectRepresentations(
|
||||||
new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)});
|
new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)});
|
||||||
@ -355,10 +380,12 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCacheEmpty(cache);
|
assertCacheEmpty(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRepresentationWithoutIndex() throws Exception {
|
public void testRepresentationWithoutIndex() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD_NO_INDEX)
|
new FakeDataSet()
|
||||||
.setRandomData("test_segment_1", 4);
|
.setData(TEST_MPD_URI, TEST_MPD_NO_INDEX)
|
||||||
|
.setRandomData("test_segment_1", 4);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
||||||
@ -374,13 +401,15 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCacheEmpty(cache);
|
assertCacheEmpty(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSelectRepresentationsClearsPreviousSelection() throws Exception {
|
public void testSelectRepresentationsClearsPreviousSelection() throws Exception {
|
||||||
FakeDataSet fakeDataSet = new FakeDataSet()
|
FakeDataSet fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6);
|
.setRandomData("audio_segment_2", 5)
|
||||||
|
.setRandomData("audio_segment_3", 6);
|
||||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||||
|
|
||||||
dashDownloader.selectRepresentations(
|
dashDownloader.selectRepresentations(
|
||||||
@ -396,11 +425,13 @@ public class DashDownloaderTest extends InstrumentationTestCase {
|
|||||||
return new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory));
|
return new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertCounters(DashDownloader dashDownloader, int totalSegments,
|
private static void assertCounters(
|
||||||
int downloadedSegments, int downloadedBytes) {
|
DashDownloader dashDownloader,
|
||||||
|
int totalSegments,
|
||||||
|
int downloadedSegments,
|
||||||
|
int downloadedBytes) {
|
||||||
assertThat(dashDownloader.getTotalSegments()).isEqualTo(totalSegments);
|
assertThat(dashDownloader.getTotalSegments()).isEqualTo(totalSegments);
|
||||||
assertThat(dashDownloader.getDownloadedSegments()).isEqualTo(downloadedSegments);
|
assertThat(dashDownloader.getDownloadedSegments()).isEqualTo(downloadedSegments);
|
||||||
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(downloadedBytes);
|
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(downloadedBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -23,26 +23,33 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.ConditionVariable;
|
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.DownloadManager;
|
||||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
||||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
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.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource.Factory;
|
import com.google.android.exoplayer2.upstream.DataSource.Factory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
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}. */
|
||||||
* Tests {@link DownloadManager}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||||
public class DownloadManagerDashTest extends InstrumentationTestCase {
|
public class DownloadManagerDashTest {
|
||||||
|
|
||||||
private static final int ASSERT_TRUE_TIMEOUT = 1000;
|
private static final int ASSERT_TRUE_TIMEOUT = 1000;
|
||||||
|
|
||||||
@ -56,26 +63,25 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
private File actionFile;
|
private File actionFile;
|
||||||
private DummyMainThread dummyMainThread;
|
private DummyMainThread dummyMainThread;
|
||||||
|
|
||||||
@UiThreadTest
|
@Before
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
|
||||||
dummyMainThread = new DummyMainThread();
|
dummyMainThread = new DummyMainThread();
|
||||||
Context context = getInstrumentation().getContext();
|
Context context = RuntimeEnvironment.application;
|
||||||
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
|
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
|
||||||
File cacheFolder = new File(tempFolder, "cache");
|
File cacheFolder = new File(tempFolder, "cache");
|
||||||
cacheFolder.mkdir();
|
cacheFolder.mkdir();
|
||||||
cache = new SimpleCache(cacheFolder, new NoOpCacheEvictor());
|
cache = new SimpleCache(cacheFolder, new NoOpCacheEvictor());
|
||||||
MockitoUtil.setUpMockito(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
fakeDataSet = new FakeDataSet()
|
fakeDataSet =
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
new FakeDataSet()
|
||||||
.setRandomData("audio_init_data", 10)
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_init_data", 10)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("text_segment_1", 1)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("text_segment_2", 2)
|
.setRandomData("text_segment_1", 1)
|
||||||
.setRandomData("text_segment_3", 3);
|
.setRandomData("text_segment_2", 2)
|
||||||
|
.setRandomData("text_segment_3", 3);
|
||||||
|
|
||||||
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
|
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
|
||||||
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
|
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
|
||||||
@ -83,33 +89,35 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
createDownloadManager();
|
createDownloadManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThreadTest
|
@After
|
||||||
@Override
|
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
downloadManager.release();
|
downloadManager.release();
|
||||||
Util.recursiveDelete(tempFolder);
|
Util.recursiveDelete(tempFolder);
|
||||||
dummyMainThread.release();
|
dummyMainThread.release();
|
||||||
super.tearDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disabled due to flakiness.
|
// 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.
|
// Configure fakeDataSet to block until interrupted when TEST_MPD is read.
|
||||||
fakeDataSet.newData(TEST_MPD_URI)
|
fakeDataSet
|
||||||
.appendReadAction(new Runnable() {
|
.newData(TEST_MPD_URI)
|
||||||
@SuppressWarnings("InfiniteLoopStatement")
|
.appendReadAction(
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@SuppressWarnings("InfiniteLoopStatement")
|
||||||
try {
|
@Override
|
||||||
// Wait until interrupted.
|
public void run() {
|
||||||
while (true) {
|
try {
|
||||||
Thread.sleep(100000);
|
// Wait until interrupted.
|
||||||
|
while (true) {
|
||||||
|
Thread.sleep(100000);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ignored) {
|
})
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.appendReadData(TEST_MPD)
|
.appendReadData(TEST_MPD)
|
||||||
.endData();
|
.endData();
|
||||||
|
|
||||||
@ -122,15 +130,20 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
// Setup an Action and immediately release the DM.
|
// Setup an Action and immediately release the DM.
|
||||||
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
|
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||||
downloadManager.release();
|
downloadManager.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
assertThat(actionFile.exists()).isTrue();
|
assertThat(actionFile.exists()).isTrue();
|
||||||
assertThat(actionFile.length()).isGreaterThan(0L);
|
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();
|
createDownloadManager();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -140,12 +153,14 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHandleDownloadAction() throws Throwable {
|
public void testHandleDownloadAction() throws Throwable {
|
||||||
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
|
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||||
blockUntilTasksCompleteAndThrowAnyDownloadError();
|
blockUntilTasksCompleteAndThrowAnyDownloadError();
|
||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHandleMultipleDownloadAction() throws Throwable {
|
public void testHandleMultipleDownloadAction() throws Throwable {
|
||||||
handleDownloadAction(fakeRepresentationKey1);
|
handleDownloadAction(fakeRepresentationKey1);
|
||||||
handleDownloadAction(fakeRepresentationKey2);
|
handleDownloadAction(fakeRepresentationKey2);
|
||||||
@ -153,6 +168,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHandleInterferingDownloadAction() throws Throwable {
|
public void testHandleInterferingDownloadAction() throws Throwable {
|
||||||
fakeDataSet
|
fakeDataSet
|
||||||
.newData("audio_segment_2")
|
.newData("audio_segment_2")
|
||||||
@ -172,6 +188,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHandleRemoveAction() throws Throwable {
|
public void testHandleRemoveAction() throws Throwable {
|
||||||
handleDownloadAction(fakeRepresentationKey1);
|
handleDownloadAction(fakeRepresentationKey1);
|
||||||
|
|
||||||
@ -185,7 +202,9 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disabled due to flakiness.
|
// Disabled due to flakiness.
|
||||||
public void disabledTestHandleRemoveActionBeforeDownloadFinish() throws Throwable {
|
@Ignore
|
||||||
|
@Test
|
||||||
|
public void testHandleRemoveActionBeforeDownloadFinish() throws Throwable {
|
||||||
handleDownloadAction(fakeRepresentationKey1);
|
handleDownloadAction(fakeRepresentationKey1);
|
||||||
handleRemoveAction();
|
handleRemoveAction();
|
||||||
|
|
||||||
@ -194,15 +213,18 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
|
|||||||
assertCacheEmpty(cache);
|
assertCacheEmpty(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHandleInterferingRemoveAction() throws Throwable {
|
public void testHandleInterferingRemoveAction() throws Throwable {
|
||||||
final ConditionVariable downloadInProgressCondition = new ConditionVariable();
|
final ConditionVariable downloadInProgressCondition = new ConditionVariable();
|
||||||
fakeDataSet.newData("audio_segment_2")
|
fakeDataSet
|
||||||
.appendReadAction(new Runnable() {
|
.newData("audio_segment_2")
|
||||||
@Override
|
.appendReadAction(
|
||||||
public void run() {
|
new Runnable() {
|
||||||
downloadInProgressCondition.open();
|
@Override
|
||||||
}
|
public void run() {
|
||||||
})
|
downloadInProgressCondition.open();
|
||||||
|
}
|
||||||
|
})
|
||||||
.appendReadData(TestUtil.buildTestData(5))
|
.appendReadData(TestUtil.buildTestData(5))
|
||||||
.endData();
|
.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.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.test.InstrumentationTestCase;
|
|
||||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||||
import com.google.android.exoplayer2.offline.DownloadService;
|
import com.google.android.exoplayer2.offline.DownloadService;
|
||||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
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.DummyMainThread;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
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.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
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 com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
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}. */
|
||||||
* Unit tests for {@link DownloadService}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||||
public class DownloadServiceDashTest extends InstrumentationTestCase {
|
public class DownloadServiceDashTest {
|
||||||
|
|
||||||
private SimpleCache cache;
|
private SimpleCache cache;
|
||||||
private File tempFolder;
|
private File tempFolder;
|
||||||
@ -57,44 +64,44 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
|||||||
private TestDownloadListener testDownloadListener;
|
private TestDownloadListener testDownloadListener;
|
||||||
private DummyMainThread dummyMainThread;
|
private DummyMainThread dummyMainThread;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
|
||||||
dummyMainThread = new DummyMainThread();
|
dummyMainThread = new DummyMainThread();
|
||||||
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
|
context = RuntimeEnvironment.application;
|
||||||
|
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
|
||||||
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
||||||
|
|
||||||
Runnable pauseAction = new Runnable() {
|
Runnable pauseAction =
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
if (pauseDownloadCondition != null) {
|
public void run() {
|
||||||
try {
|
if (pauseDownloadCondition != null) {
|
||||||
pauseDownloadCondition.block();
|
try {
|
||||||
} catch (InterruptedException e) {
|
pauseDownloadCondition.block();
|
||||||
Thread.currentThread().interrupt();
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
fakeDataSet =
|
||||||
};
|
new FakeDataSet()
|
||||||
fakeDataSet = new FakeDataSet()
|
.setData(TEST_MPD_URI, TEST_MPD)
|
||||||
.setData(TEST_MPD_URI, TEST_MPD)
|
.newData("audio_init_data")
|
||||||
.newData("audio_init_data")
|
.appendReadAction(pauseAction)
|
||||||
.appendReadAction(pauseAction)
|
.appendReadData(TestUtil.buildTestData(10))
|
||||||
.appendReadData(TestUtil.buildTestData(10))
|
.endData()
|
||||||
.endData()
|
.setRandomData("audio_segment_1", 4)
|
||||||
.setRandomData("audio_segment_1", 4)
|
.setRandomData("audio_segment_2", 5)
|
||||||
.setRandomData("audio_segment_2", 5)
|
.setRandomData("audio_segment_3", 6)
|
||||||
.setRandomData("audio_segment_3", 6)
|
.setRandomData("text_segment_1", 1)
|
||||||
.setRandomData("text_segment_1", 1)
|
.setRandomData("text_segment_2", 2)
|
||||||
.setRandomData("text_segment_2", 2)
|
.setRandomData("text_segment_3", 3);
|
||||||
.setRandomData("text_segment_3", 3);
|
|
||||||
final DataSource.Factory fakeDataSourceFactory =
|
final DataSource.Factory fakeDataSourceFactory =
|
||||||
new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet);
|
new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet);
|
||||||
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
|
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
|
||||||
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
|
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
|
||||||
|
|
||||||
context = getInstrumentation().getContext();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@ -128,7 +135,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getNotificationChannelId() {
|
protected String getNotificationChannelId() {
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -149,7 +156,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
try {
|
try {
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
@ -164,9 +171,9 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
Util.recursiveDelete(tempFolder);
|
Util.recursiveDelete(tempFolder);
|
||||||
dummyMainThread.release();
|
dummyMainThread.release();
|
||||||
super.tearDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMultipleDownloadAction() throws Throwable {
|
public void testMultipleDownloadAction() throws Throwable {
|
||||||
downloadKeys(fakeRepresentationKey1);
|
downloadKeys(fakeRepresentationKey1);
|
||||||
downloadKeys(fakeRepresentationKey2);
|
downloadKeys(fakeRepresentationKey2);
|
||||||
@ -176,6 +183,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRemoveAction() throws Throwable {
|
public void testRemoveAction() throws Throwable {
|
||||||
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
|
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
|
||||||
|
|
||||||
@ -188,6 +196,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
|
|||||||
assertCacheEmpty(cache);
|
assertCacheEmpty(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRemoveBeforeDownloadComplete() throws Throwable {
|
public void testRemoveBeforeDownloadComplete() throws Throwable {
|
||||||
pauseDownloadCondition = new ConditionVariable();
|
pauseDownloadCondition = new ConditionVariable();
|
||||||
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
|
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.DownloadListener;
|
||||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
||||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/** A {@link DownloadListener} for testing. */
|
/** A {@link DownloadListener} for testing. */
|
||||||
/*package*/ final class TestDownloadListener implements DownloadListener {
|
/*package*/ final class TestDownloadListener implements DownloadListener {
|
||||||
@ -29,13 +31,12 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
|
|||||||
|
|
||||||
private final DownloadManager downloadManager;
|
private final DownloadManager downloadManager;
|
||||||
private final DummyMainThread dummyMainThread;
|
private final DummyMainThread dummyMainThread;
|
||||||
private final android.os.ConditionVariable downloadFinishedCondition;
|
private CountDownLatch downloadFinishedCondition;
|
||||||
private Throwable downloadError;
|
private Throwable downloadError;
|
||||||
|
|
||||||
public TestDownloadListener(DownloadManager downloadManager, DummyMainThread dummyMainThread) {
|
public TestDownloadListener(DownloadManager downloadManager, DummyMainThread dummyMainThread) {
|
||||||
this.downloadManager = downloadManager;
|
this.downloadManager = downloadManager;
|
||||||
this.dummyMainThread = dummyMainThread;
|
this.dummyMainThread = dummyMainThread;
|
||||||
this.downloadFinishedCondition = new android.os.ConditionVariable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -46,8 +47,10 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIdle(DownloadManager downloadManager) {
|
public synchronized void onIdle(DownloadManager downloadManager) {
|
||||||
downloadFinishedCondition.open();
|
if (downloadFinishedCondition != null) {
|
||||||
|
downloadFinishedCondition.countDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,18 +58,19 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
|
|||||||
* error.
|
* error.
|
||||||
*/
|
*/
|
||||||
public void blockUntilTasksCompleteAndThrowAnyDownloadError() throws Throwable {
|
public void blockUntilTasksCompleteAndThrowAnyDownloadError() throws Throwable {
|
||||||
|
synchronized (this) {
|
||||||
|
downloadFinishedCondition = new CountDownLatch(1);
|
||||||
|
}
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (downloadManager.isIdle()) {
|
if (downloadManager.isIdle()) {
|
||||||
downloadFinishedCondition.open();
|
downloadFinishedCondition.countDown();
|
||||||
} else {
|
|
||||||
downloadFinishedCondition.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertThat(downloadFinishedCondition.block(TIMEOUT)).isTrue();
|
assertThat(downloadFinishedCondition.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
|
||||||
if (downloadError != null) {
|
if (downloadError != null) {
|
||||||
throw new Exception(downloadError);
|
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 {
|
dependencies {
|
||||||
compile project(modulePrefix + 'library-core')
|
compile project(modulePrefix + 'library-core')
|
||||||
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||||
androidTestCompile project(modulePrefix + 'testutils')
|
testCompile project(modulePrefix + 'testutils-robolectric')
|
||||||
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
|
|
||||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
|
|
||||||
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.google.android.exoplayer2.source.hls.test">
|
package="com.google.android.exoplayer2.source.hls.test">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||||
|
|
||||||
<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"/>
|
|
||||||
|
|
||||||
</manifest>
|
</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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.test.InstrumentationTestCase;
|
|
||||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSet;
|
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.upstream.cache.SimpleCache;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
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}. */
|
/** Unit tests for {@link HlsDownloader}. */
|
||||||
public class HlsDownloaderTest extends InstrumentationTestCase {
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class HlsDownloaderTest {
|
||||||
|
|
||||||
private SimpleCache cache;
|
private SimpleCache cache;
|
||||||
private File tempFolder;
|
private File tempFolder;
|
||||||
private FakeDataSet fakeDataSet;
|
private FakeDataSet fakeDataSet;
|
||||||
private HlsDownloader hlsDownloader;
|
private HlsDownloader hlsDownloader;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest");
|
||||||
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
|
|
||||||
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
|
||||||
|
|
||||||
fakeDataSet = new FakeDataSet()
|
fakeDataSet =
|
||||||
.setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA)
|
new FakeDataSet()
|
||||||
.setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA)
|
.setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA)
|
||||||
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10)
|
.setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA)
|
||||||
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11)
|
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10)
|
||||||
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12)
|
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11)
|
||||||
.setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA)
|
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12)
|
||||||
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13)
|
.setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA)
|
||||||
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14)
|
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13)
|
||||||
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15);
|
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14)
|
||||||
|
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15);
|
||||||
hlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI);
|
hlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
Util.recursiveDelete(tempFolder);
|
Util.recursiveDelete(tempFolder);
|
||||||
super.tearDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadManifest() throws Exception {
|
public void testDownloadManifest() throws Exception {
|
||||||
HlsMasterPlaylist manifest = hlsDownloader.getManifest();
|
HlsMasterPlaylist manifest = hlsDownloader.getManifest();
|
||||||
|
|
||||||
@ -83,17 +89,23 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI);
|
assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSelectRepresentationsClearsPreviousSelection() throws Exception {
|
public void testSelectRepresentationsClearsPreviousSelection() throws Exception {
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_2_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_2_URI});
|
||||||
hlsDownloader.download(null);
|
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 + "fileSequence0.ts",
|
||||||
MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts",
|
MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts",
|
||||||
MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts");
|
MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCounterMethods() throws Exception {
|
public void testCounterMethods() throws Exception {
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
||||||
hlsDownloader.download(null);
|
hlsDownloader.download(null);
|
||||||
@ -104,12 +116,12 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
|
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testInitStatus() throws Exception {
|
public void testInitStatus() throws Exception {
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
||||||
hlsDownloader.download(null);
|
hlsDownloader.download(null);
|
||||||
|
|
||||||
HlsDownloader newHlsDownloader =
|
HlsDownloader newHlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI);
|
||||||
getHlsDownloader(MASTER_PLAYLIST_URI);
|
|
||||||
newHlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
newHlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
||||||
newHlsDownloader.init();
|
newHlsDownloader.init();
|
||||||
|
|
||||||
@ -119,16 +131,22 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
|
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadRepresentation() throws Exception {
|
public void testDownloadRepresentation() throws Exception {
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
||||||
hlsDownloader.download(null);
|
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 + "fileSequence0.ts",
|
||||||
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
|
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
|
||||||
MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts");
|
MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadMultipleRepresentations() throws Exception {
|
public void testDownloadMultipleRepresentations() throws Exception {
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
|
||||||
hlsDownloader.download(null);
|
hlsDownloader.download(null);
|
||||||
@ -136,9 +154,11 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCachedData(cache, fakeDataSet);
|
assertCachedData(cache, fakeDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadAllRepresentations() throws Exception {
|
public void testDownloadAllRepresentations() throws Exception {
|
||||||
// Add data for the rest of the playlists
|
// 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 + "fileSequence0.ts", 10)
|
||||||
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence1.ts", 11)
|
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence1.ts", 11)
|
||||||
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence2.ts", 12)
|
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence2.ts", 12)
|
||||||
@ -162,6 +182,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
hlsDownloader.remove();
|
hlsDownloader.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRemoveAll() throws Exception {
|
public void testRemoveAll() throws Exception {
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
|
||||||
hlsDownloader.download(null);
|
hlsDownloader.download(null);
|
||||||
@ -170,27 +191,32 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
assertCacheEmpty(cache);
|
assertCacheEmpty(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadMediaPlaylist() throws Exception {
|
public void testDownloadMediaPlaylist() throws Exception {
|
||||||
hlsDownloader = getHlsDownloader(MEDIA_PLAYLIST_1_URI);
|
hlsDownloader = getHlsDownloader(MEDIA_PLAYLIST_1_URI);
|
||||||
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
|
||||||
hlsDownloader.download(null);
|
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 + "fileSequence0.ts",
|
||||||
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
|
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
|
||||||
MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts");
|
MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDownloadEncMediaPlaylist() throws Exception {
|
public void testDownloadEncMediaPlaylist() throws Exception {
|
||||||
fakeDataSet = new FakeDataSet()
|
fakeDataSet =
|
||||||
.setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA)
|
new FakeDataSet()
|
||||||
.setRandomData("enc.key", 8)
|
.setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA)
|
||||||
.setRandomData("enc2.key", 9)
|
.setRandomData("enc.key", 8)
|
||||||
.setRandomData("fileSequence0.ts", 10)
|
.setRandomData("enc2.key", 9)
|
||||||
.setRandomData("fileSequence1.ts", 11)
|
.setRandomData("fileSequence0.ts", 10)
|
||||||
.setRandomData("fileSequence2.ts", 12);
|
.setRandomData("fileSequence1.ts", 11)
|
||||||
hlsDownloader =
|
.setRandomData("fileSequence2.ts", 12);
|
||||||
getHlsDownloader(ENC_MEDIA_PLAYLIST_URI);
|
hlsDownloader = getHlsDownloader(ENC_MEDIA_PLAYLIST_URI);
|
||||||
hlsDownloader.selectRepresentations(new String[] {ENC_MEDIA_PLAYLIST_URI});
|
hlsDownloader.selectRepresentations(new String[] {ENC_MEDIA_PLAYLIST_URI});
|
||||||
hlsDownloader.download(null);
|
hlsDownloader.download(null);
|
||||||
|
|
||||||
@ -199,8 +225,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
private HlsDownloader getHlsDownloader(String mediaPlaylistUri) {
|
private HlsDownloader getHlsDownloader(String mediaPlaylistUri) {
|
||||||
Factory factory = new Factory(null).setFakeDataSet(fakeDataSet);
|
Factory factory = new Factory(null).setFakeDataSet(fakeDataSet);
|
||||||
return new HlsDownloader(Uri.parse(mediaPlaylistUri),
|
return new HlsDownloader(
|
||||||
new DownloaderConstructorHelper(cache, factory));
|
Uri.parse(mediaPlaylistUri), new DownloaderConstructorHelper(cache, factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
package com.google.android.exoplayer2.source.hls.playlist;
|
package com.google.android.exoplayer2.source.hls.playlist;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
@ -26,70 +27,85 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.List;
|
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}. */
|
||||||
* Test for {@link HlsMasterPlaylistParserTest}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class HlsMasterPlaylistParserTest {
|
||||||
public class HlsMasterPlaylistParserTest extends TestCase {
|
|
||||||
|
|
||||||
private static final String PLAYLIST_URI = "https://example.com/test.m3u8";
|
private static final String PLAYLIST_URI = "https://example.com/test.m3u8";
|
||||||
|
|
||||||
private static final String PLAYLIST_SIMPLE = " #EXTM3U \n"
|
private static final String PLAYLIST_SIMPLE =
|
||||||
+ "\n"
|
" #EXTM3U \n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
+ "\n"
|
||||||
+ "http://example.com/low.m3u8\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
|
||||||
+ "\n"
|
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
|
+ "http://example.com/low.m3u8\n"
|
||||||
+ "http://example.com/spaces_in_codecs.m3u8\n"
|
+ "\n"
|
||||||
+ "\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n"
|
+ "http://example.com/spaces_in_codecs.m3u8\n"
|
||||||
+ "http://example.com/mid.m3u8\n"
|
+ "\n"
|
||||||
+ "\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n"
|
+ "http://example.com/mid.m3u8\n"
|
||||||
+ "http://example.com/hi.m3u8\n"
|
+ "\n"
|
||||||
+ "\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
|
+ "http://example.com/hi.m3u8\n"
|
||||||
+ "http://example.com/audio-only.m3u8";
|
+ "\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"
|
private static final String PLAYLIST_WITH_AVG_BANDWIDTH =
|
||||||
+ "\n"
|
" #EXTM3U \n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
+ "\n"
|
||||||
+ "http://example.com/low.m3u8\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
|
||||||
+ "\n"
|
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000,"
|
+ "http://example.com/low.m3u8\n"
|
||||||
+ "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
|
+ "\n"
|
||||||
+ "http://example.com/spaces_in_codecs.m3u8\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"
|
private static final String PLAYLIST_WITH_INVALID_HEADER =
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
"#EXTMU3\n"
|
||||||
+ "http://example.com/low.m3u8\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"
|
private static final String PLAYLIST_WITH_CC =
|
||||||
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
|
" #EXTM3U \n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
|
||||||
+ "http://example.com/low.m3u8\n";
|
+ "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"
|
private static final String PLAYLIST_WITHOUT_CC =
|
||||||
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
|
" #EXTM3U \n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
|
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
|
||||||
+ "CLOSED-CAPTIONS=NONE\n"
|
+ "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
|
||||||
+ "http://example.com/low.m3u8\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"
|
private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG =
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n"
|
"#EXTM3U\n"
|
||||||
+ "uri1.m3u8\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n"
|
+ "uri1.m3u8\n"
|
||||||
+ "uri2.m3u8\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n"
|
+ "uri2.m3u8\n"
|
||||||
+ "uri1.m3u8\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n"
|
||||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n"
|
+ "uri1.m3u8\n"
|
||||||
+ "uri2.m3u8\n"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n"
|
||||||
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\","
|
+ "uri2.m3u8\n"
|
||||||
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n"
|
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\","
|
||||||
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\","
|
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n"
|
||||||
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/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 {
|
public void testParseMasterPlaylist() throws IOException {
|
||||||
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);
|
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");
|
assertThat(variants.get(4).url).isEqualTo("http://example.com/audio-only.m3u8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMasterPlaylistWithBandwdithAverage() throws IOException {
|
public void testMasterPlaylistWithBandwdithAverage() throws IOException {
|
||||||
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI,
|
HlsMasterPlaylist masterPlaylist =
|
||||||
PLAYLIST_WITH_AVG_BANDWIDTH);
|
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH);
|
||||||
|
|
||||||
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
|
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
|
||||||
|
|
||||||
@ -139,6 +156,7 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
|||||||
assertThat(variants.get(1).format.bitrate).isEqualTo(1270000);
|
assertThat(variants.get(1).format.bitrate).isEqualTo(1270000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testPlaylistWithInvalidHeader() throws IOException {
|
public void testPlaylistWithInvalidHeader() throws IOException {
|
||||||
try {
|
try {
|
||||||
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER);
|
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER);
|
||||||
@ -148,6 +166,7 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testPlaylistWithClosedCaption() throws IOException {
|
public void testPlaylistWithClosedCaption() throws IOException {
|
||||||
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC);
|
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC);
|
||||||
assertThat(playlist.muxedCaptionFormats).hasSize(1);
|
assertThat(playlist.muxedCaptionFormats).hasSize(1);
|
||||||
@ -157,11 +176,13 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
|||||||
assertThat(closedCaptionFormat.language).isEqualTo("es");
|
assertThat(closedCaptionFormat.language).isEqualTo("es");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testPlaylistWithoutClosedCaptions() throws IOException {
|
public void testPlaylistWithoutClosedCaptions() throws IOException {
|
||||||
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITHOUT_CC);
|
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITHOUT_CC);
|
||||||
assertThat(playlist.muxedCaptionFormats).isEmpty();
|
assertThat(playlist.muxedCaptionFormats).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCodecPropagation() throws IOException {
|
public void testCodecPropagation() throws IOException {
|
||||||
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AUDIO_MEDIA_TAG);
|
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)
|
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Uri playlistUri = Uri.parse(uri);
|
Uri playlistUri = Uri.parse(uri);
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(
|
ByteArrayInputStream inputStream =
|
||||||
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
||||||
return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
|
return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -26,49 +26,53 @@ import java.io.InputStream;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
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}. */
|
||||||
* Test for {@link HlsMediaPlaylistParserTest}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class HlsMediaPlaylistParserTest {
|
||||||
public class HlsMediaPlaylistParserTest extends TestCase {
|
|
||||||
|
|
||||||
public void testParseMediaPlaylist() throws IOException {
|
@Test
|
||||||
|
public void testParseMediaPlaylist() throws Exception {
|
||||||
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
|
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
|
||||||
String playlistString = "#EXTM3U\n"
|
String playlistString =
|
||||||
+ "#EXT-X-VERSION:3\n"
|
"#EXTM3U\n"
|
||||||
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
|
+ "#EXT-X-VERSION:3\n"
|
||||||
+ "#EXT-X-START:TIME-OFFSET=-25"
|
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||||
+ "#EXT-X-TARGETDURATION:8\n"
|
+ "#EXT-X-START:TIME-OFFSET=-25"
|
||||||
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
|
+ "#EXT-X-TARGETDURATION:8\n"
|
||||||
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
|
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
|
||||||
+ "#EXT-X-ALLOW-CACHE:YES\n"
|
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
|
||||||
+ "\n"
|
+ "#EXT-X-ALLOW-CACHE:YES\n"
|
||||||
+ "#EXTINF:7.975,\n"
|
+ "\n"
|
||||||
+ "#EXT-X-BYTERANGE:51370@0\n"
|
+ "#EXTINF:7.975,\n"
|
||||||
+ "https://priv.example.com/fileSequence2679.ts\n"
|
+ "#EXT-X-BYTERANGE:51370@0\n"
|
||||||
+ "\n"
|
+ "https://priv.example.com/fileSequence2679.ts\n"
|
||||||
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
|
+ "\n"
|
||||||
+ "#EXTINF:7.975,\n"
|
+ "#EXT-X-KEY:METHOD=AES-128,"
|
||||||
+ "#EXT-X-BYTERANGE:51501@2147483648\n"
|
+ "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
|
||||||
+ "https://priv.example.com/fileSequence2680.ts\n"
|
+ "#EXTINF:7.975,\n"
|
||||||
+ "\n"
|
+ "#EXT-X-BYTERANGE:51501@2147483648\n"
|
||||||
+ "#EXT-X-KEY:METHOD=NONE\n"
|
+ "https://priv.example.com/fileSequence2680.ts\n"
|
||||||
+ "#EXTINF:7.941,\n"
|
+ "\n"
|
||||||
+ "#EXT-X-BYTERANGE:51501\n" // @2147535149
|
+ "#EXT-X-KEY:METHOD=NONE\n"
|
||||||
+ "https://priv.example.com/fileSequence2681.ts\n"
|
+ "#EXTINF:7.941,\n"
|
||||||
+ "\n"
|
+ "#EXT-X-BYTERANGE:51501\n" // @2147535149
|
||||||
+ "#EXT-X-DISCONTINUITY\n"
|
+ "https://priv.example.com/fileSequence2681.ts\n"
|
||||||
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n"
|
+ "\n"
|
||||||
+ "#EXTINF:7.975,\n"
|
+ "#EXT-X-DISCONTINUITY\n"
|
||||||
+ "#EXT-X-BYTERANGE:51740\n" // @2147586650
|
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n"
|
||||||
+ "https://priv.example.com/fileSequence2682.ts\n"
|
+ "#EXTINF:7.975,\n"
|
||||||
+ "\n"
|
+ "#EXT-X-BYTERANGE:51740\n" // @2147586650
|
||||||
+ "#EXTINF:7.975,\n"
|
+ "https://priv.example.com/fileSequence2682.ts\n"
|
||||||
+ "https://priv.example.com/fileSequence2683.ts\n"
|
+ "\n"
|
||||||
+ "#EXT-X-ENDLIST";
|
+ "#EXTINF:7.975,\n"
|
||||||
InputStream inputStream = new ByteArrayInputStream(
|
+ "https://priv.example.com/fileSequence2683.ts\n"
|
||||||
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
+ "#EXT-X-ENDLIST";
|
||||||
|
InputStream inputStream =
|
||||||
|
new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
||||||
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
|
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||||
|
|
||||||
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
||||||
@ -136,6 +140,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
|||||||
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
|
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGapTag() throws IOException {
|
public void testGapTag() throws IOException {
|
||||||
Uri playlistUri = Uri.parse("https://example.com/test2.m3u8");
|
Uri playlistUri = Uri.parse("https://example.com/test2.m3u8");
|
||||||
String playlistString =
|
String playlistString =
|
||||||
@ -170,5 +175,4 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
|||||||
assertThat(playlist.segments.get(2).hasGapTag).isTrue();
|
assertThat(playlist.segments.get(2).hasGapTag).isTrue();
|
||||||
assertThat(playlist.segments.get(3).hasGapTag).isFalse();
|
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 {
|
dependencies {
|
||||||
compile project(modulePrefix + 'library-core')
|
compile project(modulePrefix + 'library-core')
|
||||||
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||||
androidTestCompile project(modulePrefix + 'testutils')
|
testCompile project(modulePrefix + 'testutils-robolectric')
|
||||||
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
|
|
||||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
|
|
||||||
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -18,16 +18,6 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.google.android.exoplayer2.source.smoothstreaming.test">
|
package="com.google.android.exoplayer2.source.smoothstreaming.test">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
|
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
|
||||||
|
|
||||||
<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"/>
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -16,27 +16,29 @@
|
|||||||
package com.google.android.exoplayer2.source.smoothstreaming.manifest;
|
package com.google.android.exoplayer2.source.smoothstreaming.manifest;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.test.InstrumentationTestCase;
|
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
import java.io.IOException;
|
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}. */
|
||||||
* Unit tests for {@link SsManifestParser}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public final class SsManifestParserTest {
|
||||||
public final class SsManifestParserTest extends InstrumentationTestCase {
|
|
||||||
|
|
||||||
private static final String SAMPLE_ISMC_1 = "sample_ismc_1";
|
private static final String SAMPLE_ISMC_1 = "sample_ismc_1";
|
||||||
private static final String SAMPLE_ISMC_2 = "sample_ismc_2";
|
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 {
|
public void testParseSmoothStreamingManifest() throws IOException {
|
||||||
SsManifestParser parser = new SsManifestParser();
|
SsManifestParser parser = new SsManifestParser();
|
||||||
parser.parse(Uri.parse("https://example.com/test.ismc"),
|
parser.parse(
|
||||||
TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_1));
|
Uri.parse("https://example.com/test.ismc"),
|
||||||
parser.parse(Uri.parse("https://example.com/test.ismc"),
|
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_1));
|
||||||
TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_2));
|
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.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
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}. */
|
||||||
* Unit tests for {@link SsManifest}.
|
@RunWith(RobolectricTestRunner.class)
|
||||||
*/
|
public class SsManifestTest {
|
||||||
public class SsManifestTest extends TestCase {
|
|
||||||
|
|
||||||
private static final ProtectionElement DUMMY_PROTECTION_ELEMENT =
|
private static final ProtectionElement DUMMY_PROTECTION_ELEMENT =
|
||||||
new ProtectionElement(C.WIDEVINE_UUID, new byte[] {0, 1, 2});
|
new ProtectionElement(C.WIDEVINE_UUID, new byte[] {0, 1, 2});
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCopy() throws Exception {
|
public void testCopy() throws Exception {
|
||||||
Format[][] formats = newFormats(2, 3);
|
Format[][] formats = newFormats(2, 3);
|
||||||
SsManifest sourceManifest = newSsManifest(
|
SsManifest sourceManifest =
|
||||||
newStreamElement("1",formats[0]),
|
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
|
||||||
newStreamElement("2", formats[1]));
|
|
||||||
|
|
||||||
List<TrackKey> keys = Arrays.asList(
|
List<TrackKey> keys = Arrays.asList(new TrackKey(0, 0), new TrackKey(0, 2), new TrackKey(1, 0));
|
||||||
new TrackKey(0, 0),
|
|
||||||
new TrackKey(0, 2),
|
|
||||||
new TrackKey(1, 0));
|
|
||||||
// Keys don't need to be in any particular order
|
// Keys don't need to be in any particular order
|
||||||
Collections.shuffle(keys, new Random(0));
|
Collections.shuffle(keys, new Random(0));
|
||||||
|
|
||||||
SsManifest copyManifest = sourceManifest.copy(keys);
|
SsManifest copyManifest = sourceManifest.copy(keys);
|
||||||
|
|
||||||
SsManifest expectedManifest = newSsManifest(
|
SsManifest expectedManifest =
|
||||||
newStreamElement("1", formats[0][0], formats[0][2]),
|
newSsManifest(
|
||||||
newStreamElement("2", formats[1][0]));
|
newStreamElement("1", formats[0][0], formats[0][2]),
|
||||||
|
newStreamElement("2", formats[1][0]));
|
||||||
assertManifestEquals(expectedManifest, copyManifest);
|
assertManifestEquals(expectedManifest, copyManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCopyRemoveStreamElement() throws Exception {
|
public void testCopyRemoveStreamElement() throws Exception {
|
||||||
Format[][] formats = newFormats(2, 3);
|
Format[][] formats = newFormats(2, 3);
|
||||||
SsManifest sourceManifest = newSsManifest(
|
SsManifest sourceManifest =
|
||||||
newStreamElement("1", formats[0]),
|
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
|
||||||
newStreamElement("2", formats[1]));
|
|
||||||
|
|
||||||
List<TrackKey> keys = Arrays.asList(
|
List<TrackKey> keys = Arrays.asList(new TrackKey(1, 0));
|
||||||
new TrackKey(1, 0));
|
|
||||||
// Keys don't need to be in any particular order
|
// Keys don't need to be in any particular order
|
||||||
Collections.shuffle(keys, new Random(0));
|
Collections.shuffle(keys, new Random(0));
|
||||||
|
|
||||||
SsManifest copyManifest = sourceManifest.copy(keys);
|
SsManifest copyManifest = sourceManifest.copy(keys);
|
||||||
|
|
||||||
SsManifest expectedManifest = newSsManifest(
|
SsManifest expectedManifest = newSsManifest(newStreamElement("2", formats[1][0]));
|
||||||
newStreamElement("2", formats[1][0]));
|
|
||||||
assertManifestEquals(expectedManifest, copyManifest);
|
assertManifestEquals(expectedManifest, copyManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,13 +114,25 @@ public class SsManifestTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static StreamElement newStreamElement(String name, Format... formats) {
|
private static StreamElement newStreamElement(String name, Format... formats) {
|
||||||
return new StreamElement("baseUri", "chunkTemplate", C.TRACK_TYPE_VIDEO, "subType",
|
return new StreamElement(
|
||||||
1000, name, 1024, 768, 1024, 768, null, formats, Collections.<Long>emptyList(), 0);
|
"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) {
|
private static Format newFormat(String id) {
|
||||||
return Format.createContainerFormat(id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null,
|
return Format.createContainerFormat(
|
||||||
Format.NO_VALUE, 0, null);
|
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 project(modulePrefix + 'library-core')
|
||||||
compile 'org.mockito:mockito-core:' + mockitoVersion
|
compile 'org.mockito:mockito-core:' + mockitoVersion
|
||||||
compile 'com.google.truth:truth:' + truthVersion
|
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 com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.extractor.Extractor;
|
import com.google.android.exoplayer2.extractor.Extractor;
|
||||||
@ -132,20 +131,10 @@ public class TestUtil {
|
|||||||
return joined;
|
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 {
|
public static byte[] getByteArray(Context context, String fileName) throws IOException {
|
||||||
return Util.toByteArray(getInputStream(context, fileName));
|
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 {
|
public static InputStream getInputStream(Context context, String fileName) throws IOException {
|
||||||
return context.getResources().getAssets().open(fileName);
|
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.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/** Assertion methods for {@link Cache}. */
|
||||||
* Assertion methods for {@link Cache}.
|
|
||||||
*/
|
|
||||||
public final class CacheAsserts {
|
public final class CacheAsserts {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,5 +133,4 @@ public final class CacheAsserts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CacheAsserts() {}
|
private CacheAsserts() {}
|
||||||
|
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
/** Helper class to simulate main/UI thread in tests. */
|
/** Helper class to simulate main/UI thread in tests. */
|
||||||
public final class DummyMainThread {
|
public final class DummyMainThread {
|
||||||
@ -54,16 +55,20 @@ public final class DummyMainThread {
|
|||||||
* @param runnable The {@link Runnable} to run.
|
* @param runnable The {@link Runnable} to run.
|
||||||
*/
|
*/
|
||||||
public void runOnMainThread(int timeoutMs, final Runnable runnable) {
|
public void runOnMainThread(int timeoutMs, final Runnable runnable) {
|
||||||
final ConditionVariable finishedCondition = new ConditionVariable();
|
if (Looper.myLooper() == handler.getLooper()) {
|
||||||
handler.post(
|
runnable.run();
|
||||||
new Runnable() {
|
} else {
|
||||||
@Override
|
final ConditionVariable finishedCondition = new ConditionVariable();
|
||||||
public void run() {
|
handler.post(
|
||||||
runnable.run();
|
new Runnable() {
|
||||||
finishedCondition.open();
|
@Override
|
||||||
}
|
public void run() {
|
||||||
});
|
runnable.run();
|
||||||
assertThat(finishedCondition.block(timeoutMs)).isTrue();
|
finishedCondition.open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertThat(finishedCondition.block(timeoutMs)).isTrue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
@ -19,9 +19,7 @@ import com.google.android.exoplayer2.Format;
|
|||||||
import com.google.android.exoplayer2.Renderer;
|
import com.google.android.exoplayer2.Renderer;
|
||||||
import com.google.android.exoplayer2.util.MediaClock;
|
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 abstract class FakeMediaClockRenderer extends FakeRenderer implements MediaClock {
|
||||||
|
|
||||||
public FakeMediaClockRenderer(Format... expectedFormats) {
|
public FakeMediaClockRenderer(Format... expectedFormats) {
|
||||||
@ -32,5 +30,4 @@ public abstract class FakeMediaClockRenderer extends FakeRenderer implements Med
|
|||||||
public MediaClock getMediaClock() {
|
public MediaClock getMediaClock() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -25,8 +25,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number
|
* A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number of
|
||||||
* of calls to its methods.
|
* calls to its methods.
|
||||||
*/
|
*/
|
||||||
public final class FakeTrackSelection implements TrackSelection {
|
public final class FakeTrackSelection implements TrackSelection {
|
||||||
|
|
||||||
@ -118,8 +118,8 @@ public final class FakeTrackSelection implements TrackSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs,
|
public void updateSelectedTrack(
|
||||||
long availableDurationUs) {
|
long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
|
||||||
assertThat(isEnabled).isTrue();
|
assertThat(isEnabled).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,5 +134,4 @@ public final class FakeTrackSelection implements TrackSelection {
|
|||||||
assertThat(isEnabled).isTrue();
|
assertThat(isEnabled).isTrue();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -25,9 +25,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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 {
|
public class FakeTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
private final List<FakeTrackSelection> selectedTrackSelections = new ArrayList<>();
|
private final List<FakeTrackSelection> selectedTrackSelections = new ArrayList<>();
|
||||||
@ -38,17 +36,19 @@ public class FakeTrackSelector extends MappingTrackSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse
|
* @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse {@link
|
||||||
* {@link TrackSelection}s during track selection, when it finds previously-selected track
|
* TrackSelection}s during track selection, when it finds previously-selected track selection
|
||||||
* selection using the same {@link TrackGroup}.
|
* using the same {@link TrackGroup}.
|
||||||
*/
|
*/
|
||||||
public FakeTrackSelector(boolean mayReuseTrackSelection) {
|
public FakeTrackSelector(boolean mayReuseTrackSelection) {
|
||||||
this.mayReuseTrackSelection = mayReuseTrackSelection;
|
this.mayReuseTrackSelection = mayReuseTrackSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities,
|
protected TrackSelection[] selectTracks(
|
||||||
TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports)
|
RendererCapabilities[] rendererCapabilities,
|
||||||
|
TrackGroupArray[] rendererTrackGroupArrays,
|
||||||
|
int[][][] rendererFormatSupports)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
List<FakeTrackSelection> resultList = new ArrayList<>();
|
List<FakeTrackSelection> resultList = new ArrayList<>();
|
||||||
for (TrackGroupArray trackGroupArray : rendererTrackGroupArrays) {
|
for (TrackGroupArray trackGroupArray : rendererTrackGroupArrays) {
|
||||||
@ -76,11 +76,8 @@ public class FakeTrackSelector extends MappingTrackSelector {
|
|||||||
return trackSelectionForRenderer;
|
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() {
|
public List<FakeTrackSelection> getSelectedTrackSelections() {
|
||||||
return selectedTrackSelections;
|
return selectedTrackSelections;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -37,9 +37,7 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/** A runner for {@link MediaSource} tests. */
|
||||||
* A runner for {@link MediaSource} tests.
|
|
||||||
*/
|
|
||||||
public class MediaSourceTestRunner {
|
public class MediaSourceTestRunner {
|
||||||
|
|
||||||
public static final int TIMEOUT_MS = 10000;
|
public static final int TIMEOUT_MS = 10000;
|
||||||
@ -78,18 +76,19 @@ public class MediaSourceTestRunner {
|
|||||||
public void runOnPlaybackThread(final Runnable runnable) {
|
public void runOnPlaybackThread(final Runnable runnable) {
|
||||||
final Throwable[] throwable = new Throwable[1];
|
final Throwable[] throwable = new Throwable[1];
|
||||||
final ConditionVariable finishedCondition = new ConditionVariable();
|
final ConditionVariable finishedCondition = new ConditionVariable();
|
||||||
playbackHandler.post(new Runnable() {
|
playbackHandler.post(
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
try {
|
public void run() {
|
||||||
runnable.run();
|
try {
|
||||||
} catch (Throwable e) {
|
runnable.run();
|
||||||
throwable[0] = e;
|
} catch (Throwable e) {
|
||||||
} finally {
|
throwable[0] = e;
|
||||||
finishedCondition.open();
|
} finally {
|
||||||
}
|
finishedCondition.open();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
assertThat(finishedCondition.block(TIMEOUT_MS)).isTrue();
|
assertThat(finishedCondition.block(TIMEOUT_MS)).isTrue();
|
||||||
if (throwable[0] != null) {
|
if (throwable[0] != null) {
|
||||||
Util.sneakyThrow(throwable[0]);
|
Util.sneakyThrow(throwable[0]);
|
||||||
@ -103,20 +102,21 @@ public class MediaSourceTestRunner {
|
|||||||
*/
|
*/
|
||||||
public Timeline prepareSource() throws IOException {
|
public Timeline prepareSource() throws IOException {
|
||||||
final IOException[] prepareError = new IOException[1];
|
final IOException[] prepareError = new IOException[1];
|
||||||
runOnPlaybackThread(new Runnable() {
|
runOnPlaybackThread(
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
mediaSource.prepareSource(player, true, mediaSourceListener);
|
public void run() {
|
||||||
try {
|
mediaSource.prepareSource(player, true, mediaSourceListener);
|
||||||
// TODO: This only catches errors that are set synchronously in prepareSource. To capture
|
try {
|
||||||
// async errors we'll need to poll maybeThrowSourceInfoRefreshError until the first call
|
// TODO: This only catches errors that are set synchronously in prepareSource. To
|
||||||
// to onSourceInfoRefreshed.
|
// capture async errors we'll need to poll maybeThrowSourceInfoRefreshError until the
|
||||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
// first call to onSourceInfoRefreshed.
|
||||||
} catch (IOException e) {
|
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||||
prepareError[0] = e;
|
} catch (IOException e) {
|
||||||
}
|
prepareError[0] = e;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
if (prepareError[0] != null) {
|
if (prepareError[0] != null) {
|
||||||
throw prepareError[0];
|
throw prepareError[0];
|
||||||
}
|
}
|
||||||
@ -132,12 +132,13 @@ public class MediaSourceTestRunner {
|
|||||||
*/
|
*/
|
||||||
public MediaPeriod createPeriod(final MediaPeriodId periodId) {
|
public MediaPeriod createPeriod(final MediaPeriodId periodId) {
|
||||||
final MediaPeriod[] holder = new MediaPeriod[1];
|
final MediaPeriod[] holder = new MediaPeriod[1];
|
||||||
runOnPlaybackThread(new Runnable() {
|
runOnPlaybackThread(
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
holder[0] = mediaSource.createPeriod(periodId, allocator);
|
public void run() {
|
||||||
}
|
holder[0] = mediaSource.createPeriod(periodId, allocator);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
assertThat(holder[0]).isNotNull();
|
assertThat(holder[0]).isNotNull();
|
||||||
return holder[0];
|
return holder[0];
|
||||||
}
|
}
|
||||||
@ -183,24 +184,24 @@ public class MediaSourceTestRunner {
|
|||||||
* @param mediaPeriod The {@link MediaPeriod} to release.
|
* @param mediaPeriod The {@link MediaPeriod} to release.
|
||||||
*/
|
*/
|
||||||
public void releasePeriod(final MediaPeriod mediaPeriod) {
|
public void releasePeriod(final MediaPeriod mediaPeriod) {
|
||||||
runOnPlaybackThread(new Runnable() {
|
runOnPlaybackThread(
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
mediaSource.releasePeriod(mediaPeriod);
|
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() {
|
public void releaseSource() {
|
||||||
runOnPlaybackThread(new Runnable() {
|
runOnPlaybackThread(
|
||||||
@Override
|
new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
mediaSource.releaseSource();
|
public void run() {
|
||||||
}
|
mediaSource.releaseSource();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -276,9 +277,7 @@ public class MediaSourceTestRunner {
|
|||||||
releasePeriod(secondMediaPeriod);
|
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() {
|
public void release() {
|
||||||
playbackThread.quit();
|
playbackThread.quit();
|
||||||
}
|
}
|
||||||
@ -290,7 +289,6 @@ public class MediaSourceTestRunner {
|
|||||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||||
timelines.addLast(timeline);
|
timelines.addLast(timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EventHandlingExoPlayer extends StubExoPlayer
|
private static class EventHandlingExoPlayer extends StubExoPlayer
|
||||||
@ -326,5 +324,4 @@ public class MediaSourceTestRunner {
|
|||||||
return true;
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.Shadows.shadowOf;
|
||||||
import static org.robolectric.util.ReflectionHelpers.callInstanceMethod;
|
import static org.robolectric.util.ReflectionHelpers.callInstanceMethod;
|
@ -271,5 +271,4 @@ public abstract class StubExoPlayer implements ExoPlayer {
|
|||||||
public long getContentPosition() {
|
public long getContentPosition() {
|
||||||
throw new UnsupportedOperationException();
|
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.Period;
|
||||||
import com.google.android.exoplayer2.Timeline.Window;
|
import com.google.android.exoplayer2.Timeline.Window;
|
||||||
|
|
||||||
/**
|
/** Unit test for {@link Timeline}. */
|
||||||
* Unit test for {@link Timeline}.
|
|
||||||
*/
|
|
||||||
public final class TimelineAsserts {
|
public final class TimelineAsserts {
|
||||||
|
|
||||||
private static final int[] REPEAT_MODES = {
|
private static final int[] REPEAT_MODES = {
|
||||||
@ -34,9 +32,7 @@ public final class TimelineAsserts {
|
|||||||
|
|
||||||
private 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) {
|
public static void assertEmpty(Timeline timeline) {
|
||||||
assertWindowIds(timeline);
|
assertWindowIds(timeline);
|
||||||
assertPeriodCounts(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) {
|
public static void assertWindowIsDynamic(Timeline timeline, boolean... windowIsDynamic) {
|
||||||
Window window = new Window();
|
Window window = new Window();
|
||||||
for (int i = 0; i < timeline.getWindowCount(); i++) {
|
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
|
* Asserts that previous window indices for each window depending on the repeat mode and the
|
||||||
* shuffle mode are equal to the given sequence.
|
* shuffle mode are equal to the given sequence.
|
||||||
*/
|
*/
|
||||||
public static void assertPreviousWindowIndices(Timeline timeline,
|
public static void assertPreviousWindowIndices(
|
||||||
@Player.RepeatMode int repeatMode, boolean shuffleModeEnabled,
|
Timeline timeline,
|
||||||
|
@Player.RepeatMode int repeatMode,
|
||||||
|
boolean shuffleModeEnabled,
|
||||||
int... expectedPreviousWindowIndices) {
|
int... expectedPreviousWindowIndices) {
|
||||||
for (int i = 0; i < timeline.getWindowCount(); i++) {
|
for (int i = 0; i < timeline.getWindowCount(); i++) {
|
||||||
assertThat(timeline.getPreviousWindowIndex(i, repeatMode, shuffleModeEnabled))
|
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
|
* Asserts that next window indices for each window depending on the repeat mode and the shuffle
|
||||||
* shuffle mode are equal to the given sequence.
|
* mode are equal to the given sequence.
|
||||||
*/
|
*/
|
||||||
public static void assertNextWindowIndices(Timeline timeline, @Player.RepeatMode int repeatMode,
|
public static void assertNextWindowIndices(
|
||||||
boolean shuffleModeEnabled, int... expectedNextWindowIndices) {
|
Timeline timeline,
|
||||||
|
@Player.RepeatMode int repeatMode,
|
||||||
|
boolean shuffleModeEnabled,
|
||||||
|
int... expectedNextWindowIndices) {
|
||||||
for (int i = 0; i < timeline.getWindowCount(); i++) {
|
for (int i = 0; i < timeline.getWindowCount(); i++) {
|
||||||
assertThat(timeline.getNextWindowIndex(i, repeatMode, shuffleModeEnabled))
|
assertThat(timeline.getNextWindowIndex(i, repeatMode, shuffleModeEnabled))
|
||||||
.isEqualTo(expectedNextWindowIndices[i]);
|
.isEqualTo(expectedNextWindowIndices[i]);
|
||||||
@ -113,9 +112,9 @@ public final class TimelineAsserts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that period counts for each window are set correctly. Also asserts that
|
* Asserts that period counts for each window are set correctly. Also asserts that {@link
|
||||||
* {@link Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it
|
* Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it asserts
|
||||||
* asserts the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}.
|
* the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}.
|
||||||
*/
|
*/
|
||||||
public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCounts) {
|
public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCounts) {
|
||||||
int windowCount = timeline.getWindowCount();
|
int windowCount = timeline.getWindowCount();
|
||||||
@ -147,8 +146,8 @@ public final class TimelineAsserts {
|
|||||||
.isEqualTo(i + 1);
|
.isEqualTo(i + 1);
|
||||||
} else {
|
} else {
|
||||||
int nextWindow = timeline.getNextWindowIndex(expectedWindowIndex, repeatMode, false);
|
int nextWindow = timeline.getNextWindowIndex(expectedWindowIndex, repeatMode, false);
|
||||||
int nextPeriod = nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET
|
int nextPeriod =
|
||||||
: accumulatedPeriodCounts[nextWindow];
|
nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET : accumulatedPeriodCounts[nextWindow];
|
||||||
assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false))
|
assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false))
|
||||||
.isEqualTo(nextPeriod);
|
.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) {
|
public static void assertAdGroupCounts(Timeline timeline, int... expectedAdGroupCounts) {
|
||||||
Period period = new Period();
|
Period period = new Period();
|
||||||
for (int i = 0; i < timeline.getPeriodCount(); i++) {
|
for (int i = 0; i < timeline.getPeriodCount(); i++) {
|
||||||
@ -166,5 +163,4 @@ public final class TimelineAsserts {
|
|||||||
assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]);
|
assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user