Commit b47fb282 by tonihei Committed by Oliver Woodman

Move extension tests to Robolectric.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=187021822
parent d400efd0
Showing with 1942 additions and 818 deletions
......@@ -24,6 +24,7 @@ include modulePrefix + 'library-hls'
include modulePrefix + 'library-smoothstreaming'
include modulePrefix + 'library-ui'
include modulePrefix + 'testutils'
include modulePrefix + 'testutils-robolectric'
include modulePrefix + 'extension-ffmpeg'
include modulePrefix + 'extension-flac'
include modulePrefix + 'extension-gvr'
......@@ -44,6 +45,7 @@ project(modulePrefix + 'library-hls').projectDir = new File(rootDir, 'library/hl
project(modulePrefix + 'library-smoothstreaming').projectDir = new File(rootDir, 'library/smoothstreaming')
project(modulePrefix + 'library-ui').projectDir = new File(rootDir, 'library/ui')
project(modulePrefix + 'testutils').projectDir = new File(rootDir, 'testutils')
project(modulePrefix + 'testutils-robolectric').projectDir = new File(rootDir, 'testutils_robolectric')
project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'extensions/ffmpeg')
project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac')
project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr')
......
......@@ -38,10 +38,7 @@ dependencies {
compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion
compile project(modulePrefix + 'library-core')
compile project(modulePrefix + 'library-ui')
testCompile project(modulePrefix + 'testutils')
testCompile 'junit:junit:' + junitVersion
testCompile 'org.mockito:mockito-core:' + mockitoVersion
testCompile 'org.robolectric:robolectric:' + robolectricVersion
testCompile project(modulePrefix + 'testutils-robolectric')
}
ext {
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.exoplayer2.ext.cast.test">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
</manifest>
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.cast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaQueueItem;
import com.google.android.gms.cast.MediaStatus;
......@@ -25,11 +26,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
/** Tests for {@link CastTimelineTracker}. */
@RunWith(RobolectricTestRunner.class)
@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE)
public class CastTimelineTrackerTest {
private static final long DURATION_1_MS = 1000;
......@@ -49,12 +48,12 @@ public class CastTimelineTrackerTest {
new long[] {DURATION_1_MS, MediaInfo.UNKNOWN_DURATION, MediaInfo.UNKNOWN_DURATION});
CastTimelineTracker tracker = new CastTimelineTracker();
mediaInfo = mockMediaInfo("contentId1", DURATION_1_MS);
mediaInfo = getMediaInfo("contentId1", DURATION_1_MS);
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
TimelineAsserts.assertPeriodDurations(
tracker.getCastTimeline(status), C.msToUs(DURATION_1_MS), C.TIME_UNSET, C.TIME_UNSET);
mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS);
mediaInfo = getMediaInfo("contentId3", DURATION_3_MS);
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
TimelineAsserts.assertPeriodDurations(
tracker.getCastTimeline(status),
......@@ -62,7 +61,7 @@ public class CastTimelineTrackerTest {
C.TIME_UNSET,
C.msToUs(DURATION_3_MS));
mediaInfo = mockMediaInfo("contentId2", DURATION_2_MS);
mediaInfo = getMediaInfo("contentId2", DURATION_2_MS);
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
TimelineAsserts.assertPeriodDurations(
tracker.getCastTimeline(status),
......@@ -80,7 +79,7 @@ public class CastTimelineTrackerTest {
DURATION_5_MS,
MediaInfo.UNKNOWN_DURATION
});
mediaInfo = mockMediaInfo("contentId5", DURATION_5_MS);
mediaInfo = getMediaInfo("contentId5", DURATION_5_MS);
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
TimelineAsserts.assertPeriodDurations(
tracker.getCastTimeline(newStatus),
......@@ -89,7 +88,7 @@ public class CastTimelineTrackerTest {
C.msToUs(DURATION_5_MS),
C.msToUs(DURATION_3_MS));
mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS);
mediaInfo = getMediaInfo("contentId3", DURATION_3_MS);
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
TimelineAsserts.assertPeriodDurations(
tracker.getCastTimeline(newStatus),
......@@ -98,7 +97,7 @@ public class CastTimelineTrackerTest {
C.msToUs(DURATION_5_MS),
C.msToUs(DURATION_3_MS));
mediaInfo = mockMediaInfo("contentId4", DURATION_4_MS);
mediaInfo = getMediaInfo("contentId4", DURATION_4_MS);
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
TimelineAsserts.assertPeriodDurations(
tracker.getCastTimeline(newStatus),
......@@ -112,7 +111,7 @@ public class CastTimelineTrackerTest {
int[] itemIds, String[] contentIds, long[] durationsMs) {
ArrayList<MediaQueueItem> items = new ArrayList<>();
for (int i = 0; i < contentIds.length; i++) {
MediaInfo mediaInfo = mockMediaInfo(contentIds[i], durationsMs[i]);
MediaInfo mediaInfo = getMediaInfo(contentIds[i], durationsMs[i]);
MediaQueueItem item = Mockito.mock(MediaQueueItem.class);
Mockito.when(item.getMedia()).thenReturn(mediaInfo);
Mockito.when(item.getItemId()).thenReturn(itemIds[i]);
......@@ -123,10 +122,11 @@ public class CastTimelineTrackerTest {
return status;
}
private static MediaInfo mockMediaInfo(String contentId, long durationMs) {
MediaInfo mediaInfo = Mockito.mock(MediaInfo.class);
Mockito.when(mediaInfo.getContentId()).thenReturn(contentId);
Mockito.when(mediaInfo.getStreamDuration()).thenReturn(durationMs);
return mediaInfo;
private static MediaInfo getMediaInfo(String contentId, long durationMs) {
return new MediaInfo.Builder(contentId)
.setStreamDuration(durationMs)
.setContentType(MimeTypes.APPLICATION_MP4)
.setStreamType(MediaInfo.STREAM_TYPE_NONE)
.build();
}
}
......@@ -39,12 +39,8 @@ dependencies {
compile files('libs/cronet_api.jar')
compile files('libs/cronet_impl_common_java.jar')
compile files('libs/cronet_impl_native_java.jar')
androidTestCompile project(modulePrefix + 'library')
androidTestCompile project(modulePrefix + 'testutils')
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
androidTestCompile 'com.android.support.test:runner:' + testSupportLibraryVersion
testCompile project(modulePrefix + 'library')
testCompile project(modulePrefix + 'testutils-robolectric')
}
ext {
......
......@@ -18,16 +18,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.exoplayer2.ext.cronet">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
<application android:debuggable="true"
android:allowBackup="false"
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.google.android.exoplayer2.ext.cronet"/>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
</manifest>
......@@ -19,9 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.google.android.exoplayer2.testutil.MockitoUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
......@@ -30,11 +27,11 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
/**
* Tests for {@link ByteArrayUploadDataProvider}.
*/
@RunWith(AndroidJUnit4.class)
/** Tests for {@link ByteArrayUploadDataProvider}. */
@RunWith(RobolectricTestRunner.class)
public final class ByteArrayUploadDataProviderTest {
private static final byte[] TEST_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
......@@ -45,7 +42,7 @@ public final class ByteArrayUploadDataProviderTest {
@Before
public void setUp() {
MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this);
MockitoAnnotations.initMocks(this);
byteBuffer = ByteBuffer.allocate(TEST_DATA.length);
byteArrayUploadDataProvider = new ByteArrayUploadDataProvider(TEST_DATA);
}
......@@ -90,5 +87,4 @@ public final class ByteArrayUploadDataProviderTest {
assertThat(byteBuffer.array()).isEqualTo(TEST_DATA);
verify(mockUploadDataSink).onRewindSucceeded();
}
}
......@@ -31,6 +31,7 @@ android {
}
test {
java.srcDirs += "../../testutils/src/main/java/"
java.srcDirs += "../../testutils_robolectric/src/main/java/"
}
}
......
......@@ -89,7 +89,7 @@ public final class ContentDataSourceTest extends InstrumentationTestCase {
ContentDataSource dataSource = new ContentDataSource(instrumentation.getContext());
try {
DataSpec dataSpec = new DataSpec(contentUri, offset, length, null);
byte[] completeData = TestUtil.getByteArray(instrumentation, DATA_PATH);
byte[] completeData = TestUtil.getByteArray(instrumentation.getContext(), DATA_PATH);
byte[] expectedData = Arrays.copyOfRange(completeData, offset,
length == C.LENGTH_UNSET ? completeData.length : offset + length);
TestUtil.assertDataSourceContent(dataSource, dataSpec, expectedData, !pipeMode);
......
......@@ -42,6 +42,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.FakeTrackSelection;
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.upstream.Allocator;
import java.util.ArrayList;
import java.util.Arrays;
......
......@@ -22,8 +22,8 @@ import static org.mockito.Mockito.when;
import android.util.Pair;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import java.util.HashMap;
import org.junit.After;
import org.junit.Before;
......
......@@ -21,11 +21,11 @@ import static org.junit.Assert.fail;
import android.os.ConditionVariable;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.util.Util;
......
......@@ -20,7 +20,6 @@ import static org.junit.Assert.fail;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window;
......@@ -29,6 +28,7 @@ import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import org.junit.Before;
......
......@@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.testutil.FakeMediaSource;
......@@ -27,6 +26,7 @@ import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import org.junit.Test;
......
......@@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify;
import android.os.ConditionVariable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.testutil.DummyMainThread;
......@@ -31,6 +30,7 @@ import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import java.util.Arrays;
......
......@@ -17,12 +17,12 @@ package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import java.io.IOException;
import org.junit.Before;
......
......@@ -19,13 +19,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MergingMediaSource.IllegalMergeException;
import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
......
......@@ -35,15 +35,7 @@ android {
dependencies {
compile project(modulePrefix + 'library-core')
compile 'com.android.support:support-annotations:' + supportLibraryVersion
androidTestCompile project(modulePrefix + 'testutils')
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
testCompile project(modulePrefix + 'testutils')
testCompile 'com.google.truth:truth:' + truthVersion
testCompile 'junit:junit:' + junitVersion
testCompile 'org.mockito:mockito-core:' + mockitoVersion
testCompile 'org.robolectric:robolectric:' + robolectricVersion
testCompile project(modulePrefix + 'testutils-robolectric')
}
ext {
......
......@@ -50,6 +50,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
......@@ -1113,7 +1114,9 @@ public final class DashMediaSource implements MediaSource {
@Override
public Long parse(Uri uri, InputStream inputStream) throws IOException {
String firstLine = new BufferedReader(new InputStreamReader(inputStream)).readLine();
String firstLine =
new BufferedReader(new InputStreamReader(inputStream, Charset.forName(C.UTF8_NAME)))
.readLine();
try {
Matcher matcher = TIMESTAMP_WITH_TIMEZONE_PATTERN.matcher(firstLine);
if (!matcher.matches()) {
......
......@@ -18,16 +18,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.exoplayer2.source.dash.test">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
<application android:debuggable="true"
android:allowBackup="false"
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
<uses-library android:name="android.test.runner"/>
</application>
<instrumentation
android:targetPackage="com.google.android.exoplayer2.source.dash.test"
android:name="android.test.InstrumentationTestRunner"/>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="25"/>
</manifest>
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source.dash;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Unit test for {@link DashMediaSource}. */
@RunWith(RobolectricTestRunner.class)
public final class DashMediaSourceTest {
@Test
public void testIso8601ParserParse() throws IOException {
DashMediaSource.Iso8601Parser parser = new DashMediaSource.Iso8601Parser();
// UTC.
assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37Z");
assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+00:00");
assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+0000");
assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+00");
// Positive timezone offsets.
assertParseStringToLong(1512381697000L - 4980000L, parser, "2017-12-04T10:01:37+01:23");
assertParseStringToLong(1512381697000L - 4980000L, parser, "2017-12-04T10:01:37+0123");
assertParseStringToLong(1512381697000L - 3600000L, parser, "2017-12-04T10:01:37+01");
// Negative timezone offsets with minus character.
assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37-01:23");
assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37-0123");
assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-01:00");
assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-0100");
assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-01");
// Negative timezone offsets with hyphen character.
assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37−01:23");
assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37−0123");
assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−01:00");
assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−0100");
assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−01");
}
@Test
public void testIso8601ParserParseMissingTimezone() throws IOException {
DashMediaSource.Iso8601Parser parser = new DashMediaSource.Iso8601Parser();
try {
assertParseStringToLong(0, parser, "2017-12-04T10:01:37");
fail();
} catch (ParserException e) {
// Expected.
}
}
private static void assertParseStringToLong(
long expected, ParsingLoadable.Parser<Long> parser, String data) throws IOException {
long actual = parser.parse(null, new ByteArrayInputStream(Util.getUtf8Bytes(data)));
assertThat(actual).isEqualTo(expected);
}
}
......@@ -28,33 +28,38 @@ import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegm
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Arrays;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Unit tests for {@link DashUtil}.
*/
public final class DashUtilTest extends TestCase {
/** Unit tests for {@link DashUtil}. */
@RunWith(RobolectricTestRunner.class)
public final class DashUtilTest {
@Test
public void testLoadDrmInitDataFromManifest() throws Exception {
Period period = newPeriod(newAdaptationSets(newRepresentations(newDrmInitData())));
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
assertThat(drmInitData).isEqualTo(newDrmInitData());
}
@Test
public void testLoadDrmInitDataMissing() throws Exception {
Period period = newPeriod(newAdaptationSets(newRepresentations(null /* no init data */)));
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
assertThat(drmInitData).isNull();
}
@Test
public void testLoadDrmInitDataNoRepresentations() throws Exception {
Period period = newPeriod(newAdaptationSets(/* no representation */));
Period period = newPeriod(newAdaptationSets(/* no representation */ ));
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
assertThat(drmInitData).isNull();
}
@Test
public void testLoadDrmInitDataNoAdaptationSets() throws Exception {
Period period = newPeriod(/* no adaptation set */);
Period period = newPeriod(/* no adaptation set */ );
DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period);
assertThat(drmInitData).isNull();
}
......@@ -68,8 +73,18 @@ public final class DashUtilTest extends TestCase {
}
private static Representation newRepresentations(DrmInitData drmInitData) {
Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4,
MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0);
Format format =
Format.createVideoContainerFormat(
"id",
MimeTypes.VIDEO_MP4,
MimeTypes.VIDEO_H264,
"",
Format.NO_VALUE,
1024,
768,
Format.NO_VALUE,
null,
0);
if (drmInitData != null) {
format = format.copyWithDrmInitData(drmInitData);
}
......@@ -77,8 +92,7 @@ public final class DashUtilTest extends TestCase {
}
private static DrmInitData newDrmInitData() {
return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType",
new byte[] {1, 4, 7, 0, 3, 6}));
return new DrmInitData(
new SchemeData(C.WIDEVINE_UUID, "mimeType", new byte[] {1, 4, 7, 0, 3, 6}));
}
}
......@@ -18,39 +18,47 @@ package com.google.android.exoplayer2.source.dash.manifest;
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Unit tests for {@link DashManifestParser}.
*/
public class DashManifestParserTest extends InstrumentationTestCase {
/** Unit tests for {@link DashManifestParser}. */
@RunWith(RobolectricTestRunner.class)
public class DashManifestParserTest {
private static final String SAMPLE_MPD_1 = "sample_mpd_1";
private static final String SAMPLE_MPD_2_UNKNOWN_MIME_TYPE = "sample_mpd_2_unknown_mime_type";
private static final String SAMPLE_MPD_3_SEGMENT_TEMPLATE = "sample_mpd_3_segment_template";
private static final String SAMPLE_MPD_4_EVENT_STREAM = "sample_mpd_4_event_stream";
/**
* Simple test to ensure the sample manifests parse without any exceptions being thrown.
*/
/** Simple test to ensure the sample manifests parse without any exceptions being thrown. */
@Test
public void testParseMediaPresentationDescription() throws IOException {
DashManifestParser parser = new DashManifestParser();
parser.parse(Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_1));
parser.parse(Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_2_UNKNOWN_MIME_TYPE));
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_1));
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_2_UNKNOWN_MIME_TYPE));
}
@Test
public void testParseMediaPresentationDescriptionWithSegmentTemplate() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_3_SEGMENT_TEMPLATE));
DashManifest mpd =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_3_SEGMENT_TEMPLATE));
assertThat(mpd.getPeriodCount()).isEqualTo(1);
......@@ -75,11 +83,13 @@ public class DashManifestParserTest extends InstrumentationTestCase {
}
}
public void testParseMediaPresentationDescriptionCanParseEventStream()
throws IOException {
@Test
public void testParseMediaPresentationDescriptionCanParseEventStream() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_4_EVENT_STREAM));
DashManifest mpd =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_4_EVENT_STREAM));
Period period = mpd.getPeriod(0);
assertThat(period.eventStreams).hasSize(3);
......@@ -87,8 +97,14 @@ public class DashManifestParserTest extends InstrumentationTestCase {
// assert text-only event stream
EventStream eventStream1 = period.eventStreams.get(0);
assertThat(eventStream1.events.length).isEqualTo(1);
EventMessage expectedEvent1 = new EventMessage("urn:uuid:XYZY", "call", 10000, 0,
"+ 1 800 10101010".getBytes(), 0);
EventMessage expectedEvent1 =
new EventMessage(
"urn:uuid:XYZY",
"call",
10000,
0,
"+ 1 800 10101010".getBytes(Charset.forName(C.UTF8_NAME)),
0);
assertThat(eventStream1.events[0]).isEqualTo(expectedEvent1);
// assert CData-structured event stream
......@@ -135,6 +151,7 @@ public class DashManifestParserTest extends InstrumentationTestCase {
1000000000));
}
@Test
public void testParseCea608AccessibilityChannel() {
assertThat(
DashManifestParser.parseCea608AccessibilityChannel(
......@@ -175,6 +192,7 @@ public class DashManifestParserTest extends InstrumentationTestCase {
.isEqualTo(Format.NO_VALUE);
}
@Test
public void testParseCea708AccessibilityChannel() {
assertThat(
DashManifestParser.parseCea708AccessibilityChannel(
......@@ -226,5 +244,4 @@ public class DashManifestParserTest extends InstrumentationTestCase {
private static List<Descriptor> buildCea708AccessibilityDescriptors(String value) {
return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-708:2015", value, null));
}
}
......@@ -18,17 +18,19 @@ package com.google.android.exoplayer2.source.dash.manifest;
import static com.google.common.truth.Truth.assertThat;
import com.google.android.exoplayer2.C;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Unit test for {@link RangedUri}.
*/
public class RangedUriTest extends TestCase {
/** Unit test for {@link RangedUri}. */
@RunWith(RobolectricTestRunner.class)
public class RangedUriTest {
private static final String BASE_URI = "http://www.test.com/";
private static final String PARTIAL_URI = "path/file.ext";
private static final String FULL_URI = BASE_URI + PARTIAL_URI;
@Test
public void testMerge() {
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
......@@ -36,6 +38,7 @@ public class RangedUriTest extends TestCase {
assertMerge(rangeA, rangeB, expected, null);
}
@Test
public void testMergeUnbounded() {
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
RangedUri rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET);
......@@ -43,6 +46,7 @@ public class RangedUriTest extends TestCase {
assertMerge(rangeA, rangeB, expected, null);
}
@Test
public void testNonMerge() {
// A and B do not overlap, so should not merge
RangedUri rangeA = new RangedUri(FULL_URI, 0, 10);
......@@ -65,6 +69,7 @@ public class RangedUriTest extends TestCase {
assertNonMerge(rangeA, rangeB, null);
}
@Test
public void testMergeWithBaseUri() {
RangedUri rangeA = new RangedUri(PARTIAL_URI, 0, 10);
RangedUri rangeB = new RangedUri(FULL_URI, 10, 10);
......@@ -85,5 +90,4 @@ public class RangedUriTest extends TestCase {
merged = rangeB.attemptMerge(rangeA, baseUrl);
assertThat(merged).isNull();
}
}
......@@ -20,27 +20,49 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
import com.google.android.exoplayer2.util.MimeTypes;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Unit test for {@link Representation}.
*/
public class RepresentationTest extends TestCase {
/** Unit test for {@link Representation}. */
@RunWith(RobolectricTestRunner.class)
public class RepresentationTest {
@Test
public void testGetCacheKey() {
String uri = "http://www.google.com";
SegmentBase base = new SingleSegmentBase(new RangedUri(null, 0, 1), 1, 0, 1, 1);
Format format = Format.createVideoContainerFormat("0", MimeTypes.APPLICATION_MP4, null,
MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0);
Representation representation = Representation.newInstance("test_stream_1", 3, format, uri,
base);
Format format =
Format.createVideoContainerFormat(
"0",
MimeTypes.APPLICATION_MP4,
null,
MimeTypes.VIDEO_H264,
2500000,
1920,
1080,
Format.NO_VALUE,
null,
0);
Representation representation =
Representation.newInstance("test_stream_1", 3, format, uri, base);
assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.0.3");
format = Format.createVideoContainerFormat("150", MimeTypes.APPLICATION_MP4, null,
MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0);
representation = Representation.newInstance("test_stream_1", Representation.REVISION_ID_DEFAULT,
format, uri, base);
format =
Format.createVideoContainerFormat(
"150",
MimeTypes.APPLICATION_MP4,
null,
MimeTypes.VIDEO_H264,
2500000,
1920,
1080,
Format.NO_VALUE,
null,
0);
representation =
Representation.newInstance(
"test_stream_1", Representation.REVISION_ID_DEFAULT, format, uri, base);
assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.150.-1");
}
}
......@@ -16,14 +16,17 @@
package com.google.android.exoplayer2.source.dash.manifest;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Unit test for {@link UrlTemplate}.
*/
public class UrlTemplateTest extends TestCase {
/** Unit test for {@link UrlTemplate}. */
@RunWith(RobolectricTestRunner.class)
public class UrlTemplateTest {
@Test
public void testRealExamples() {
String template = "QualityLevels($Bandwidth$)/Fragments(video=$Time$,format=mpd-time-csf)";
UrlTemplate urlTemplate = UrlTemplate.compile(template);
......@@ -41,6 +44,7 @@ public class UrlTemplateTest extends TestCase {
assertThat(url).isEqualTo("chunk_ctvideo_cfm4s_ridabc1_cn10_w2073857842_mpd.m4s");
}
@Test
public void testFull() {
String template = "$Bandwidth$_a_$RepresentationID$_b_$Time$_c_$Number$";
UrlTemplate urlTemplate = UrlTemplate.compile(template);
......@@ -48,6 +52,7 @@ public class UrlTemplateTest extends TestCase {
assertThat(url).isEqualTo("650000_a_abc1_b_5000_c_10");
}
@Test
public void testFullWithDollarEscaping() {
String template = "$$$Bandwidth$$$_a$$_$RepresentationID$_b_$Time$_c_$Number$$$";
UrlTemplate urlTemplate = UrlTemplate.compile(template);
......@@ -55,6 +60,7 @@ public class UrlTemplateTest extends TestCase {
assertThat(url).isEqualTo("$650000$_a$_abc1_b_5000_c_10$");
}
@Test
public void testInvalidSubstitution() {
String template = "$IllegalId$";
try {
......@@ -64,5 +70,4 @@ public class UrlTemplateTest extends TestCase {
// Expected.
}
}
}
/*
* 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);
}
}
......@@ -16,85 +16,88 @@
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.
*/
/** 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();
+ "<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();
+ "<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));
}
......@@ -23,26 +23,33 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.ConditionVariable;
import android.test.InstrumentationTestCase;
import android.test.UiThreadTest;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.MockitoUtil;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.DataSource.Factory;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Util;
import java.io.File;
/**
* Tests {@link DownloadManager}.
*/
public class DownloadManagerDashTest extends InstrumentationTestCase {
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}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
public class DownloadManagerDashTest {
private static final int ASSERT_TRUE_TIMEOUT = 1000;
......@@ -56,26 +63,25 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
private File actionFile;
private DummyMainThread dummyMainThread;
@UiThreadTest
@Override
@Before
public void setUp() throws Exception {
super.setUp();
dummyMainThread = new DummyMainThread();
Context context = getInstrumentation().getContext();
Context context = RuntimeEnvironment.application;
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
File cacheFolder = new File(tempFolder, "cache");
cacheFolder.mkdir();
cache = new SimpleCache(cacheFolder, new NoOpCacheEvictor());
MockitoUtil.setUpMockito(this);
fakeDataSet = new FakeDataSet()
.setData(TEST_MPD_URI, TEST_MPD)
.setRandomData("audio_init_data", 10)
.setRandomData("audio_segment_1", 4)
.setRandomData("audio_segment_2", 5)
.setRandomData("audio_segment_3", 6)
.setRandomData("text_segment_1", 1)
.setRandomData("text_segment_2", 2)
.setRandomData("text_segment_3", 3);
MockitoAnnotations.initMocks(this);
fakeDataSet =
new FakeDataSet()
.setData(TEST_MPD_URI, TEST_MPD)
.setRandomData("audio_init_data", 10)
.setRandomData("audio_segment_1", 4)
.setRandomData("audio_segment_2", 5)
.setRandomData("audio_segment_3", 6)
.setRandomData("text_segment_1", 1)
.setRandomData("text_segment_2", 2)
.setRandomData("text_segment_3", 3);
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
......@@ -83,33 +89,35 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
createDownloadManager();
}
@UiThreadTest
@Override
@After
public void tearDown() throws Exception {
downloadManager.release();
Util.recursiveDelete(tempFolder);
dummyMainThread.release();
super.tearDown();
}
// Disabled due to flakiness.
public void disabledTestSaveAndLoadActionFile() throws Throwable {
@Ignore
@Test
public void testSaveAndLoadActionFile() throws Throwable {
// Configure fakeDataSet to block until interrupted when TEST_MPD is read.
fakeDataSet.newData(TEST_MPD_URI)
.appendReadAction(new Runnable() {
@SuppressWarnings("InfiniteLoopStatement")
@Override
public void run() {
try {
// Wait until interrupted.
while (true) {
Thread.sleep(100000);
fakeDataSet
.newData(TEST_MPD_URI)
.appendReadAction(
new Runnable() {
@SuppressWarnings("InfiniteLoopStatement")
@Override
public void run() {
try {
// Wait until interrupted.
while (true) {
Thread.sleep(100000);
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
})
})
.appendReadData(TEST_MPD)
.endData();
......@@ -122,15 +130,20 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
// Setup an Action and immediately release the DM.
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
downloadManager.release();
}
});
assertThat(actionFile.exists()).isTrue();
assertThat(actionFile.length()).isGreaterThan(0L);
assertCacheEmpty(cache);
assertThat(actionFile.exists()).isTrue();
assertThat(actionFile.length()).isGreaterThan(0L);
assertCacheEmpty(cache);
// Revert fakeDataSet to normal.
fakeDataSet.setData(TEST_MPD_URI, TEST_MPD);
// Revert fakeDataSet to normal.
fakeDataSet.setData(TEST_MPD_URI, TEST_MPD);
dummyMainThread.runOnMainThread(
new Runnable() {
@Override
public void run() {
createDownloadManager();
}
});
......@@ -140,12 +153,14 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
assertCachedData(cache, fakeDataSet);
}
@Test
public void testHandleDownloadAction() throws Throwable {
handleDownloadAction(fakeRepresentationKey1, fakeRepresentationKey2);
blockUntilTasksCompleteAndThrowAnyDownloadError();
assertCachedData(cache, fakeDataSet);
}
@Test
public void testHandleMultipleDownloadAction() throws Throwable {
handleDownloadAction(fakeRepresentationKey1);
handleDownloadAction(fakeRepresentationKey2);
......@@ -153,6 +168,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
assertCachedData(cache, fakeDataSet);
}
@Test
public void testHandleInterferingDownloadAction() throws Throwable {
fakeDataSet
.newData("audio_segment_2")
......@@ -172,6 +188,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
assertCachedData(cache, fakeDataSet);
}
@Test
public void testHandleRemoveAction() throws Throwable {
handleDownloadAction(fakeRepresentationKey1);
......@@ -185,7 +202,9 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
}
// Disabled due to flakiness.
public void disabledTestHandleRemoveActionBeforeDownloadFinish() throws Throwable {
@Ignore
@Test
public void testHandleRemoveActionBeforeDownloadFinish() throws Throwable {
handleDownloadAction(fakeRepresentationKey1);
handleRemoveAction();
......@@ -194,15 +213,18 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
assertCacheEmpty(cache);
}
@Test
public void testHandleInterferingRemoveAction() throws Throwable {
final ConditionVariable downloadInProgressCondition = new ConditionVariable();
fakeDataSet.newData("audio_segment_2")
.appendReadAction(new Runnable() {
@Override
public void run() {
downloadInProgressCondition.open();
}
})
fakeDataSet
.newData("audio_segment_2")
.appendReadAction(
new Runnable() {
@Override
public void run() {
downloadInProgressCondition.open();
}
})
.appendReadData(TestUtil.buildTestData(5))
.endData();
......@@ -250,5 +272,4 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
}
});
}
}
......@@ -22,7 +22,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa
import android.content.Context;
import android.content.Intent;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
......@@ -32,6 +31,7 @@ import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
......@@ -40,11 +40,18 @@ import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.Util;
import java.io.File;
import java.io.IOException;
/**
* Unit tests for {@link DownloadService}.
*/
public class DownloadServiceDashTest extends InstrumentationTestCase {
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}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
public class DownloadServiceDashTest {
private SimpleCache cache;
private File tempFolder;
......@@ -57,44 +64,44 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
private TestDownloadListener testDownloadListener;
private DummyMainThread dummyMainThread;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
dummyMainThread = new DummyMainThread();
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
context = RuntimeEnvironment.application;
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
Runnable pauseAction = new Runnable() {
@Override
public void run() {
if (pauseDownloadCondition != null) {
try {
pauseDownloadCondition.block();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Runnable pauseAction =
new Runnable() {
@Override
public void run() {
if (pauseDownloadCondition != null) {
try {
pauseDownloadCondition.block();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}
};
fakeDataSet = new FakeDataSet()
.setData(TEST_MPD_URI, TEST_MPD)
.newData("audio_init_data")
.appendReadAction(pauseAction)
.appendReadData(TestUtil.buildTestData(10))
.endData()
.setRandomData("audio_segment_1", 4)
.setRandomData("audio_segment_2", 5)
.setRandomData("audio_segment_3", 6)
.setRandomData("text_segment_1", 1)
.setRandomData("text_segment_2", 2)
.setRandomData("text_segment_3", 3);
};
fakeDataSet =
new FakeDataSet()
.setData(TEST_MPD_URI, TEST_MPD)
.newData("audio_init_data")
.appendReadAction(pauseAction)
.appendReadData(TestUtil.buildTestData(10))
.endData()
.setRandomData("audio_segment_1", 4)
.setRandomData("audio_segment_2", 5)
.setRandomData("audio_segment_3", 6)
.setRandomData("text_segment_1", 1)
.setRandomData("text_segment_2", 2)
.setRandomData("text_segment_3", 3);
final DataSource.Factory fakeDataSourceFactory =
new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet);
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
context = getInstrumentation().getContext();
try {
dummyMainThread.runOnMainThread(
new Runnable() {
......@@ -128,7 +135,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
@Override
protected String getNotificationChannelId() {
return null;
return "";
}
@Override
......@@ -149,7 +156,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
}
}
@Override
@After
public void tearDown() throws Exception {
try {
dummyMainThread.runOnMainThread(
......@@ -164,9 +171,9 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
}
Util.recursiveDelete(tempFolder);
dummyMainThread.release();
super.tearDown();
}
@Test
public void testMultipleDownloadAction() throws Throwable {
downloadKeys(fakeRepresentationKey1);
downloadKeys(fakeRepresentationKey2);
......@@ -176,6 +183,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
assertCachedData(cache, fakeDataSet);
}
@Test
public void testRemoveAction() throws Throwable {
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
......@@ -188,6 +196,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
assertCacheEmpty(cache);
}
@Test
public void testRemoveBeforeDownloadComplete() throws Throwable {
pauseDownloadCondition = new ConditionVariable();
downloadKeys(fakeRepresentationKey1, fakeRepresentationKey2);
......@@ -219,5 +228,4 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
}
});
}
}
......@@ -21,6 +21,8 @@ import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** A {@link DownloadListener} for testing. */
/*package*/ final class TestDownloadListener implements DownloadListener {
......@@ -29,13 +31,12 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
private final DownloadManager downloadManager;
private final DummyMainThread dummyMainThread;
private final android.os.ConditionVariable downloadFinishedCondition;
private CountDownLatch downloadFinishedCondition;
private Throwable downloadError;
public TestDownloadListener(DownloadManager downloadManager, DummyMainThread dummyMainThread) {
this.downloadManager = downloadManager;
this.dummyMainThread = dummyMainThread;
this.downloadFinishedCondition = new android.os.ConditionVariable();
}
@Override
......@@ -46,8 +47,10 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
}
@Override
public void onIdle(DownloadManager downloadManager) {
downloadFinishedCondition.open();
public synchronized void onIdle(DownloadManager downloadManager) {
if (downloadFinishedCondition != null) {
downloadFinishedCondition.countDown();
}
}
/**
......@@ -55,18 +58,19 @@ import com.google.android.exoplayer2.testutil.DummyMainThread;
* error.
*/
public void blockUntilTasksCompleteAndThrowAnyDownloadError() throws Throwable {
synchronized (this) {
downloadFinishedCondition = new CountDownLatch(1);
}
dummyMainThread.runOnMainThread(
new Runnable() {
@Override
public void run() {
if (downloadManager.isIdle()) {
downloadFinishedCondition.open();
} else {
downloadFinishedCondition.close();
downloadFinishedCondition.countDown();
}
}
});
assertThat(downloadFinishedCondition.block(TIMEOUT)).isTrue();
assertThat(downloadFinishedCondition.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
if (downloadError != null) {
throw new Exception(downloadError);
}
......
......@@ -35,10 +35,7 @@ android {
dependencies {
compile project(modulePrefix + 'library-core')
compile 'com.android.support:support-annotations:' + supportLibraryVersion
androidTestCompile project(modulePrefix + 'testutils')
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
testCompile project(modulePrefix + 'testutils-robolectric')
}
ext {
......
......@@ -18,16 +18,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.exoplayer2.source.hls.test">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
<application android:debuggable="true"
android:allowBackup="false"
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
<uses-library android:name="android.test.runner"/>
</application>
<instrumentation
android:targetPackage="com.google.android.exoplayer2.source.hls.test"
android:name="android.test.InstrumentationTestRunner"/>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
</manifest>
......@@ -15,9 +15,10 @@
*/
package com.google.android.exoplayer2.source.hls.offline;
/**
* Data for HLS downloading tests.
*/
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";
......@@ -33,45 +34,50 @@ package com.google.android.exoplayer2.source.hls.offline;
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();
+ "#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();
+ "#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();
+ "#EXT-X-TARGETDURATION:10\n"
+ "#EXT-X-VERSION:3\n"
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"enc.key\"\n"
+ "#EXTINF:9.97667,\n"
+ "fileSequence0.ts\n"
+ "#EXTINF:9.97667,\n"
+ "fileSequence1.ts\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"enc2.key\"\n"
+ "#EXTINF:9.97667,\n"
+ "fileSequence2.ts\n"
+ "#EXT-X-ENDLIST")
.getBytes(Charset.forName(C.UTF8_NAME));
}
......@@ -33,7 +33,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.testutil.FakeDataSet;
......@@ -42,40 +41,47 @@ import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Util;
import java.io.File;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/** Unit tests for {@link HlsDownloader}. */
public class HlsDownloaderTest extends InstrumentationTestCase {
@RunWith(RobolectricTestRunner.class)
public class HlsDownloaderTest {
private SimpleCache cache;
private File tempFolder;
private FakeDataSet fakeDataSet;
private HlsDownloader hlsDownloader;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest");
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
fakeDataSet = new FakeDataSet()
.setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA)
.setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12)
.setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA)
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13)
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14)
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15);
fakeDataSet =
new FakeDataSet()
.setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA)
.setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12)
.setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA)
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13)
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14)
.setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15);
hlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI);
}
@Override
@After
public void tearDown() throws Exception {
Util.recursiveDelete(tempFolder);
super.tearDown();
}
@Test
public void testDownloadManifest() throws Exception {
HlsMasterPlaylist manifest = hlsDownloader.getManifest();
......@@ -83,17 +89,23 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI);
}
@Test
public void testSelectRepresentationsClearsPreviousSelection() throws Exception {
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_2_URI});
hlsDownloader.download(null);
assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI, MEDIA_PLAYLIST_2_URI,
assertCachedData(
cache,
fakeDataSet,
MASTER_PLAYLIST_URI,
MEDIA_PLAYLIST_2_URI,
MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts",
MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts",
MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts");
}
@Test
public void testCounterMethods() throws Exception {
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
hlsDownloader.download(null);
......@@ -104,12 +116,12 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
}
@Test
public void testInitStatus() throws Exception {
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
hlsDownloader.download(null);
HlsDownloader newHlsDownloader =
getHlsDownloader(MASTER_PLAYLIST_URI);
HlsDownloader newHlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI);
newHlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
newHlsDownloader.init();
......@@ -119,16 +131,22 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
}
@Test
public void testDownloadRepresentation() throws Exception {
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
hlsDownloader.download(null);
assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI, MEDIA_PLAYLIST_1_URI,
assertCachedData(
cache,
fakeDataSet,
MASTER_PLAYLIST_URI,
MEDIA_PLAYLIST_1_URI,
MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts",
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts");
}
@Test
public void testDownloadMultipleRepresentations() throws Exception {
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
hlsDownloader.download(null);
......@@ -136,9 +154,11 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
assertCachedData(cache, fakeDataSet);
}
@Test
public void testDownloadAllRepresentations() throws Exception {
// Add data for the rest of the playlists
fakeDataSet.setData(MEDIA_PLAYLIST_0_URI, MEDIA_PLAYLIST_DATA)
fakeDataSet
.setData(MEDIA_PLAYLIST_0_URI, MEDIA_PLAYLIST_DATA)
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence0.ts", 10)
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence1.ts", 11)
.setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence2.ts", 12)
......@@ -162,6 +182,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
hlsDownloader.remove();
}
@Test
public void testRemoveAll() throws Exception {
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI});
hlsDownloader.download(null);
......@@ -170,27 +191,32 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
assertCacheEmpty(cache);
}
@Test
public void testDownloadMediaPlaylist() throws Exception {
hlsDownloader = getHlsDownloader(MEDIA_PLAYLIST_1_URI);
hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI});
hlsDownloader.download(null);
assertCachedData(cache, fakeDataSet, MEDIA_PLAYLIST_1_URI,
assertCachedData(
cache,
fakeDataSet,
MEDIA_PLAYLIST_1_URI,
MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts",
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts");
}
@Test
public void testDownloadEncMediaPlaylist() throws Exception {
fakeDataSet = new FakeDataSet()
.setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA)
.setRandomData("enc.key", 8)
.setRandomData("enc2.key", 9)
.setRandomData("fileSequence0.ts", 10)
.setRandomData("fileSequence1.ts", 11)
.setRandomData("fileSequence2.ts", 12);
hlsDownloader =
getHlsDownloader(ENC_MEDIA_PLAYLIST_URI);
fakeDataSet =
new FakeDataSet()
.setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA)
.setRandomData("enc.key", 8)
.setRandomData("enc2.key", 9)
.setRandomData("fileSequence0.ts", 10)
.setRandomData("fileSequence1.ts", 11)
.setRandomData("fileSequence2.ts", 12);
hlsDownloader = getHlsDownloader(ENC_MEDIA_PLAYLIST_URI);
hlsDownloader.selectRepresentations(new String[] {ENC_MEDIA_PLAYLIST_URI});
hlsDownloader.download(null);
......@@ -199,8 +225,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase {
private HlsDownloader getHlsDownloader(String mediaPlaylistUri) {
Factory factory = new Factory(null).setFakeDataSet(fakeDataSet);
return new HlsDownloader(Uri.parse(mediaPlaylistUri),
new DownloaderConstructorHelper(cache, factory));
return new HlsDownloader(
Uri.parse(mediaPlaylistUri), new DownloaderConstructorHelper(cache, factory));
}
}
......@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.hls.playlist;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.net.Uri;
import com.google.android.exoplayer2.C;
......@@ -26,70 +27,85 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Test for {@link HlsMasterPlaylistParserTest}.
*/
public class HlsMasterPlaylistParserTest extends TestCase {
/** Test for {@link HlsMasterPlaylistParserTest}. */
@RunWith(RobolectricTestRunner.class)
public class HlsMasterPlaylistParserTest {
private static final String PLAYLIST_URI = "https://example.com/test.m3u8";
private static final String PLAYLIST_SIMPLE = " #EXTM3U \n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n"
+ "http://example.com/mid.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n"
+ "http://example.com/hi.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
+ "http://example.com/audio-only.m3u8";
private static final String PLAYLIST_WITH_AVG_BANDWIDTH = " #EXTM3U \n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000,"
+ "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n";
private static final String PLAYLIST_WITH_INVALID_HEADER = "#EXTMU3\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_CC = " #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITHOUT_CC = " #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
+ "CLOSED-CAPTIONS=NONE\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG = "#EXTM3U\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n"
+ "uri1.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n"
+ "uri2.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n"
+ "uri1.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n"
+ "uri2.m3u8\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\","
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\","
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n";
private static final String PLAYLIST_SIMPLE =
" #EXTM3U \n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n"
+ "http://example.com/mid.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n"
+ "http://example.com/hi.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
+ "http://example.com/audio-only.m3u8";
private static final String PLAYLIST_WITH_AVG_BANDWIDTH =
" #EXTM3U \n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000,"
+ "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n";
private static final String PLAYLIST_WITH_INVALID_HEADER =
"#EXTMU3\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_CC =
" #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
+ "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITHOUT_CC =
" #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
+ "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
+ "CLOSED-CAPTIONS=NONE\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG =
"#EXTM3U\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n"
+ "uri1.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n"
+ "uri2.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n"
+ "uri1.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n"
+ "uri2.m3u8\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\","
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\","
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n";
@Test
public void testParseMasterPlaylist() throws IOException {
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);
......@@ -129,9 +145,10 @@ public class HlsMasterPlaylistParserTest extends TestCase {
assertThat(variants.get(4).url).isEqualTo("http://example.com/audio-only.m3u8");
}
@Test
public void testMasterPlaylistWithBandwdithAverage() throws IOException {
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI,
PLAYLIST_WITH_AVG_BANDWIDTH);
HlsMasterPlaylist masterPlaylist =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH);
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
......@@ -139,6 +156,7 @@ public class HlsMasterPlaylistParserTest extends TestCase {
assertThat(variants.get(1).format.bitrate).isEqualTo(1270000);
}
@Test
public void testPlaylistWithInvalidHeader() throws IOException {
try {
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER);
......@@ -148,6 +166,7 @@ public class HlsMasterPlaylistParserTest extends TestCase {
}
}
@Test
public void testPlaylistWithClosedCaption() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC);
assertThat(playlist.muxedCaptionFormats).hasSize(1);
......@@ -157,11 +176,13 @@ public class HlsMasterPlaylistParserTest extends TestCase {
assertThat(closedCaptionFormat.language).isEqualTo("es");
}
@Test
public void testPlaylistWithoutClosedCaptions() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITHOUT_CC);
assertThat(playlist.muxedCaptionFormats).isEmpty();
}
@Test
public void testCodecPropagation() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AUDIO_MEDIA_TAG);
......@@ -177,9 +198,8 @@ public class HlsMasterPlaylistParserTest extends TestCase {
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
throws IOException {
Uri playlistUri = Uri.parse(uri);
ByteArrayInputStream inputStream = new ByteArrayInputStream(
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
ByteArrayInputStream inputStream =
new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
}
}
......@@ -26,49 +26,53 @@ import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Locale;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Test for {@link HlsMediaPlaylistParserTest}.
*/
public class HlsMediaPlaylistParserTest extends TestCase {
/** Test for {@link HlsMediaPlaylistParserTest}. */
@RunWith(RobolectricTestRunner.class)
public class HlsMediaPlaylistParserTest {
public void testParseMediaPlaylist() throws IOException {
@Test
public void testParseMediaPlaylist() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString = "#EXTM3U\n"
+ "#EXT-X-VERSION:3\n"
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
+ "#EXT-X-START:TIME-OFFSET=-25"
+ "#EXT-X-TARGETDURATION:8\n"
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
+ "#EXT-X-ALLOW-CACHE:YES\n"
+ "\n"
+ "#EXTINF:7.975,\n"
+ "#EXT-X-BYTERANGE:51370@0\n"
+ "https://priv.example.com/fileSequence2679.ts\n"
+ "\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
+ "#EXTINF:7.975,\n"
+ "#EXT-X-BYTERANGE:51501@2147483648\n"
+ "https://priv.example.com/fileSequence2680.ts\n"
+ "\n"
+ "#EXT-X-KEY:METHOD=NONE\n"
+ "#EXTINF:7.941,\n"
+ "#EXT-X-BYTERANGE:51501\n" // @2147535149
+ "https://priv.example.com/fileSequence2681.ts\n"
+ "\n"
+ "#EXT-X-DISCONTINUITY\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n"
+ "#EXTINF:7.975,\n"
+ "#EXT-X-BYTERANGE:51740\n" // @2147586650
+ "https://priv.example.com/fileSequence2682.ts\n"
+ "\n"
+ "#EXTINF:7.975,\n"
+ "https://priv.example.com/fileSequence2683.ts\n"
+ "#EXT-X-ENDLIST";
InputStream inputStream = new ByteArrayInputStream(
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-VERSION:3\n"
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
+ "#EXT-X-START:TIME-OFFSET=-25"
+ "#EXT-X-TARGETDURATION:8\n"
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
+ "#EXT-X-ALLOW-CACHE:YES\n"
+ "\n"
+ "#EXTINF:7.975,\n"
+ "#EXT-X-BYTERANGE:51370@0\n"
+ "https://priv.example.com/fileSequence2679.ts\n"
+ "\n"
+ "#EXT-X-KEY:METHOD=AES-128,"
+ "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
+ "#EXTINF:7.975,\n"
+ "#EXT-X-BYTERANGE:51501@2147483648\n"
+ "https://priv.example.com/fileSequence2680.ts\n"
+ "\n"
+ "#EXT-X-KEY:METHOD=NONE\n"
+ "#EXTINF:7.941,\n"
+ "#EXT-X-BYTERANGE:51501\n" // @2147535149
+ "https://priv.example.com/fileSequence2681.ts\n"
+ "\n"
+ "#EXT-X-DISCONTINUITY\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n"
+ "#EXTINF:7.975,\n"
+ "#EXT-X-BYTERANGE:51740\n" // @2147586650
+ "https://priv.example.com/fileSequence2682.ts\n"
+ "\n"
+ "#EXTINF:7.975,\n"
+ "https://priv.example.com/fileSequence2683.ts\n"
+ "#EXT-X-ENDLIST";
InputStream inputStream =
new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
......@@ -136,6 +140,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
}
@Test
public void testGapTag() throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test2.m3u8");
String playlistString =
......@@ -170,5 +175,4 @@ public class HlsMediaPlaylistParserTest extends TestCase {
assertThat(playlist.segments.get(2).hasGapTag).isTrue();
assertThat(playlist.segments.get(3).hasGapTag).isFalse();
}
}
manifest=src/test/AndroidManifest.xml
......@@ -35,10 +35,7 @@ android {
dependencies {
compile project(modulePrefix + 'library-core')
compile 'com.android.support:support-annotations:' + supportLibraryVersion
androidTestCompile project(modulePrefix + 'testutils')
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
testCompile project(modulePrefix + 'testutils-robolectric')
}
ext {
......
......@@ -18,16 +18,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.exoplayer2.source.smoothstreaming.test">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="27"/>
<application android:debuggable="true"
android:allowBackup="false"
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
<uses-library android:name="android.test.runner"/>
</application>
<instrumentation
android:targetPackage="com.google.android.exoplayer2.source.smoothstreaming.test"
android:name="android.test.InstrumentationTestRunner"/>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
</manifest>
......@@ -16,27 +16,29 @@
package com.google.android.exoplayer2.source.smoothstreaming.manifest;
import android.net.Uri;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Unit tests for {@link SsManifestParser}.
*/
public final class SsManifestParserTest extends InstrumentationTestCase {
/** Unit tests for {@link SsManifestParser}. */
@RunWith(RobolectricTestRunner.class)
public final class SsManifestParserTest {
private static final String SAMPLE_ISMC_1 = "sample_ismc_1";
private static final String SAMPLE_ISMC_2 = "sample_ismc_2";
/**
* Simple test to ensure the sample manifests parse without any exceptions being thrown.
*/
/** Simple test to ensure the sample manifests parse without any exceptions being thrown. */
@Test
public void testParseSmoothStreamingManifest() throws IOException {
SsManifestParser parser = new SsManifestParser();
parser.parse(Uri.parse("https://example.com/test.ismc"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_1));
parser.parse(Uri.parse("https://example.com/test.ismc"),
TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_2));
parser.parse(
Uri.parse("https://example.com/test.ismc"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_1));
parser.parse(
Uri.parse("https://example.com/test.ismc"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_2));
}
}
......@@ -26,52 +26,49 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Unit tests for {@link SsManifest}.
*/
public class SsManifestTest extends TestCase {
/** Unit tests for {@link SsManifest}. */
@RunWith(RobolectricTestRunner.class)
public class SsManifestTest {
private static final ProtectionElement DUMMY_PROTECTION_ELEMENT =
new ProtectionElement(C.WIDEVINE_UUID, new byte[] {0, 1, 2});
@Test
public void testCopy() throws Exception {
Format[][] formats = newFormats(2, 3);
SsManifest sourceManifest = newSsManifest(
newStreamElement("1",formats[0]),
newStreamElement("2", formats[1]));
List<TrackKey> keys = Arrays.asList(
new TrackKey(0, 0),
new TrackKey(0, 2),
new TrackKey(1, 0));
SsManifest sourceManifest =
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
List<TrackKey> keys = Arrays.asList(new TrackKey(0, 0), new TrackKey(0, 2), new TrackKey(1, 0));
// Keys don't need to be in any particular order
Collections.shuffle(keys, new Random(0));
SsManifest copyManifest = sourceManifest.copy(keys);
SsManifest expectedManifest = newSsManifest(
newStreamElement("1", formats[0][0], formats[0][2]),
newStreamElement("2", formats[1][0]));
SsManifest expectedManifest =
newSsManifest(
newStreamElement("1", formats[0][0], formats[0][2]),
newStreamElement("2", formats[1][0]));
assertManifestEquals(expectedManifest, copyManifest);
}
@Test
public void testCopyRemoveStreamElement() throws Exception {
Format[][] formats = newFormats(2, 3);
SsManifest sourceManifest = newSsManifest(
newStreamElement("1", formats[0]),
newStreamElement("2", formats[1]));
SsManifest sourceManifest =
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
List<TrackKey> keys = Arrays.asList(
new TrackKey(1, 0));
List<TrackKey> keys = Arrays.asList(new TrackKey(1, 0));
// Keys don't need to be in any particular order
Collections.shuffle(keys, new Random(0));
SsManifest copyManifest = sourceManifest.copy(keys);
SsManifest expectedManifest = newSsManifest(
newStreamElement("2", formats[1][0]));
SsManifest expectedManifest = newSsManifest(newStreamElement("2", formats[1][0]));
assertManifestEquals(expectedManifest, copyManifest);
}
......@@ -117,13 +114,25 @@ public class SsManifestTest extends TestCase {
}
private static StreamElement newStreamElement(String name, Format... formats) {
return new StreamElement("baseUri", "chunkTemplate", C.TRACK_TYPE_VIDEO, "subType",
1000, name, 1024, 768, 1024, 768, null, formats, Collections.<Long>emptyList(), 0);
return new StreamElement(
"baseUri",
"chunkTemplate",
C.TRACK_TYPE_VIDEO,
"subType",
1000,
name,
1024,
768,
1024,
768,
null,
formats,
Collections.<Long>emptyList(),
0);
}
private static Format newFormat(String id) {
return Format.createContainerFormat(id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null,
Format.NO_VALUE, 0, null);
return Format.createContainerFormat(
id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, Format.NO_VALUE, 0, null);
}
}
......@@ -35,4 +35,5 @@ dependencies {
compile project(modulePrefix + 'library-core')
compile 'org.mockito:mockito-core:' + mockitoVersion
compile 'com.google.truth:truth:' + truthVersion
testCompile project(modulePrefix + 'testutils-robolectric')
}
/*
* 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() {}
}
......@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.app.Instrumentation;
import android.content.Context;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.Extractor;
......@@ -132,20 +131,10 @@ public class TestUtil {
return joined;
}
public static byte[] getByteArray(Instrumentation instrumentation, String fileName)
throws IOException {
return getByteArray(instrumentation.getContext(), fileName);
}
public static byte[] getByteArray(Context context, String fileName) throws IOException {
return Util.toByteArray(getInputStream(context, fileName));
}
public static InputStream getInputStream(Instrumentation instrumentation, String fileName)
throws IOException {
return getInputStream(instrumentation.getContext(), fileName);
}
public static InputStream getInputStream(Context context, String fileName) throws IOException {
return context.getResources().getAssets().open(fileName);
}
......
<?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>
/*
* 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);
}
}
/*
* 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;
}
}
}
/*
* 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);
}
}
/*
* 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;
}
}
}
manifest=src/test/AndroidManifest.xml
// 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
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest package="com.google.android.exoplayer2.testutil"/>
......@@ -29,9 +29,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheUtil;
import java.io.IOException;
import java.util.ArrayList;
/**
* Assertion methods for {@link Cache}.
*/
/** Assertion methods for {@link Cache}. */
public final class CacheAsserts {
/**
......@@ -135,5 +133,4 @@ public final class CacheAsserts {
}
private CacheAsserts() {}
}
......@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
/** Helper class to simulate main/UI thread in tests. */
public final class DummyMainThread {
......@@ -54,16 +55,20 @@ public final class DummyMainThread {
* @param runnable The {@link Runnable} to run.
*/
public void runOnMainThread(int timeoutMs, final Runnable runnable) {
final ConditionVariable finishedCondition = new ConditionVariable();
handler.post(
new Runnable() {
@Override
public void run() {
runnable.run();
finishedCondition.open();
}
});
assertThat(finishedCondition.block(timeoutMs)).isTrue();
if (Looper.myLooper() == handler.getLooper()) {
runnable.run();
} else {
final ConditionVariable finishedCondition = new ConditionVariable();
handler.post(
new Runnable() {
@Override
public void run() {
runnable.run();
finishedCondition.open();
}
});
assertThat(finishedCondition.block(timeoutMs)).isTrue();
}
}
public void release() {
......
......@@ -19,9 +19,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.util.MediaClock;
/**
* Fake abstract {@link Renderer} which is also a {@link MediaClock}.
*/
/** Fake abstract {@link Renderer} which is also a {@link MediaClock}. */
public abstract class FakeMediaClockRenderer extends FakeRenderer implements MediaClock {
public FakeMediaClockRenderer(Format... expectedFormats) {
......@@ -32,5 +30,4 @@ public abstract class FakeMediaClockRenderer extends FakeRenderer implements Med
public MediaClock getMediaClock() {
return this;
}
}
......@@ -25,8 +25,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import java.util.List;
/**
* A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number
* of calls to its methods.
* A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number of
* calls to its methods.
*/
public final class FakeTrackSelection implements TrackSelection {
......@@ -118,8 +118,8 @@ public final class FakeTrackSelection implements TrackSelection {
}
@Override
public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs,
long availableDurationUs) {
public void updateSelectedTrack(
long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
assertThat(isEnabled).isTrue();
}
......@@ -134,5 +134,4 @@ public final class FakeTrackSelection implements TrackSelection {
assertThat(isEnabled).isTrue();
return false;
}
}
......@@ -25,9 +25,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import java.util.ArrayList;
import java.util.List;
/**
* A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s.
*/
/** A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. */
public class FakeTrackSelector extends MappingTrackSelector {
private final List<FakeTrackSelection> selectedTrackSelections = new ArrayList<>();
......@@ -38,17 +36,19 @@ public class FakeTrackSelector extends MappingTrackSelector {
}
/**
* @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse
* {@link TrackSelection}s during track selection, when it finds previously-selected track
* selection using the same {@link TrackGroup}.
* @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse {@link
* TrackSelection}s during track selection, when it finds previously-selected track selection
* using the same {@link TrackGroup}.
*/
public FakeTrackSelector(boolean mayReuseTrackSelection) {
this.mayReuseTrackSelection = mayReuseTrackSelection;
}
@Override
protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities,
TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports)
protected TrackSelection[] selectTracks(
RendererCapabilities[] rendererCapabilities,
TrackGroupArray[] rendererTrackGroupArrays,
int[][][] rendererFormatSupports)
throws ExoPlaybackException {
List<FakeTrackSelection> resultList = new ArrayList<>();
for (TrackGroupArray trackGroupArray : rendererTrackGroupArrays) {
......@@ -76,11 +76,8 @@ public class FakeTrackSelector extends MappingTrackSelector {
return trackSelectionForRenderer;
}
/**
* Returns list of all {@link FakeTrackSelection}s that this track selector has made so far.
*/
/** Returns list of all {@link FakeTrackSelection}s that this track selector has made so far. */
public List<FakeTrackSelection> getSelectedTrackSelections() {
return selectedTrackSelections;
}
}
......@@ -37,9 +37,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
/**
* A runner for {@link MediaSource} tests.
*/
/** A runner for {@link MediaSource} tests. */
public class MediaSourceTestRunner {
public static final int TIMEOUT_MS = 10000;
......@@ -78,18 +76,19 @@ public class MediaSourceTestRunner {
public void runOnPlaybackThread(final Runnable runnable) {
final Throwable[] throwable = new Throwable[1];
final ConditionVariable finishedCondition = new ConditionVariable();
playbackHandler.post(new Runnable() {
@Override
public void run() {
try {
runnable.run();
} catch (Throwable e) {
throwable[0] = e;
} finally {
finishedCondition.open();
}
}
});
playbackHandler.post(
new Runnable() {
@Override
public void run() {
try {
runnable.run();
} catch (Throwable e) {
throwable[0] = e;
} finally {
finishedCondition.open();
}
}
});
assertThat(finishedCondition.block(TIMEOUT_MS)).isTrue();
if (throwable[0] != null) {
Util.sneakyThrow(throwable[0]);
......@@ -103,20 +102,21 @@ public class MediaSourceTestRunner {
*/
public Timeline prepareSource() throws IOException {
final IOException[] prepareError = new IOException[1];
runOnPlaybackThread(new Runnable() {
@Override
public void run() {
mediaSource.prepareSource(player, true, mediaSourceListener);
try {
// TODO: This only catches errors that are set synchronously in prepareSource. To capture
// async errors we'll need to poll maybeThrowSourceInfoRefreshError until the first call
// to onSourceInfoRefreshed.
mediaSource.maybeThrowSourceInfoRefreshError();
} catch (IOException e) {
prepareError[0] = e;
}
}
});
runOnPlaybackThread(
new Runnable() {
@Override
public void run() {
mediaSource.prepareSource(player, true, mediaSourceListener);
try {
// TODO: This only catches errors that are set synchronously in prepareSource. To
// capture async errors we'll need to poll maybeThrowSourceInfoRefreshError until the
// first call to onSourceInfoRefreshed.
mediaSource.maybeThrowSourceInfoRefreshError();
} catch (IOException e) {
prepareError[0] = e;
}
}
});
if (prepareError[0] != null) {
throw prepareError[0];
}
......@@ -132,12 +132,13 @@ public class MediaSourceTestRunner {
*/
public MediaPeriod createPeriod(final MediaPeriodId periodId) {
final MediaPeriod[] holder = new MediaPeriod[1];
runOnPlaybackThread(new Runnable() {
@Override
public void run() {
holder[0] = mediaSource.createPeriod(periodId, allocator);
}
});
runOnPlaybackThread(
new Runnable() {
@Override
public void run() {
holder[0] = mediaSource.createPeriod(periodId, allocator);
}
});
assertThat(holder[0]).isNotNull();
return holder[0];
}
......@@ -183,24 +184,24 @@ public class MediaSourceTestRunner {
* @param mediaPeriod The {@link MediaPeriod} to release.
*/
public void releasePeriod(final MediaPeriod mediaPeriod) {
runOnPlaybackThread(new Runnable() {
@Override
public void run() {
mediaSource.releasePeriod(mediaPeriod);
}
});
runOnPlaybackThread(
new Runnable() {
@Override
public void run() {
mediaSource.releasePeriod(mediaPeriod);
}
});
}
/**
* Calls {@link MediaSource#releaseSource()} on the playback thread.
*/
/** Calls {@link MediaSource#releaseSource()} on the playback thread. */
public void releaseSource() {
runOnPlaybackThread(new Runnable() {
@Override
public void run() {
mediaSource.releaseSource();
}
});
runOnPlaybackThread(
new Runnable() {
@Override
public void run() {
mediaSource.releaseSource();
}
});
}
/**
......@@ -276,9 +277,7 @@ public class MediaSourceTestRunner {
releasePeriod(secondMediaPeriod);
}
/**
* Releases the runner. Should be called when the runner is no longer required.
*/
/** Releases the runner. Should be called when the runner is no longer required. */
public void release() {
playbackThread.quit();
}
......@@ -290,7 +289,6 @@ public class MediaSourceTestRunner {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
timelines.addLast(timeline);
}
}
private static class EventHandlingExoPlayer extends StubExoPlayer
......@@ -326,5 +324,4 @@ public class MediaSourceTestRunner {
return true;
}
}
}
......@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
package com.google.android.exoplayer2.testutil;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.util.ReflectionHelpers.callInstanceMethod;
......
......@@ -271,5 +271,4 @@ public abstract class StubExoPlayer implements ExoPlayer {
public long getContentPosition() {
throw new UnsupportedOperationException();
}
}
......@@ -23,9 +23,7 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window;
/**
* Unit test for {@link Timeline}.
*/
/** Unit test for {@link Timeline}. */
public final class TimelineAsserts {
private static final int[] REPEAT_MODES = {
......@@ -34,9 +32,7 @@ public final class TimelineAsserts {
private TimelineAsserts() {}
/**
* Assert that timeline is empty (i.e. has no windows or periods).
*/
/** Assert that timeline is empty (i.e. has no windows or periods). */
public static void assertEmpty(Timeline timeline) {
assertWindowIds(timeline);
assertPeriodCounts(timeline);
......@@ -63,9 +59,7 @@ public final class TimelineAsserts {
}
}
/**
* Asserts that window properties {@link Window}.isDynamic are set correctly.
*/
/** Asserts that window properties {@link Window}.isDynamic are set correctly. */
public static void assertWindowIsDynamic(Timeline timeline, boolean... windowIsDynamic) {
Window window = new Window();
for (int i = 0; i < timeline.getWindowCount(); i++) {
......@@ -78,8 +72,10 @@ public final class TimelineAsserts {
* Asserts that previous window indices for each window depending on the repeat mode and the
* shuffle mode are equal to the given sequence.
*/
public static void assertPreviousWindowIndices(Timeline timeline,
@Player.RepeatMode int repeatMode, boolean shuffleModeEnabled,
public static void assertPreviousWindowIndices(
Timeline timeline,
@Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled,
int... expectedPreviousWindowIndices) {
for (int i = 0; i < timeline.getWindowCount(); i++) {
assertThat(timeline.getPreviousWindowIndex(i, repeatMode, shuffleModeEnabled))
......@@ -88,11 +84,14 @@ public final class TimelineAsserts {
}
/**
* Asserts that next window indices for each window depending on the repeat mode and the
* shuffle mode are equal to the given sequence.
* Asserts that next window indices for each window depending on the repeat mode and the shuffle
* mode are equal to the given sequence.
*/
public static void assertNextWindowIndices(Timeline timeline, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled, int... expectedNextWindowIndices) {
public static void assertNextWindowIndices(
Timeline timeline,
@Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled,
int... expectedNextWindowIndices) {
for (int i = 0; i < timeline.getWindowCount(); i++) {
assertThat(timeline.getNextWindowIndex(i, repeatMode, shuffleModeEnabled))
.isEqualTo(expectedNextWindowIndices[i]);
......@@ -113,9 +112,9 @@ public final class TimelineAsserts {
}
/**
* Asserts that period counts for each window are set correctly. Also asserts that
* {@link Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it
* asserts the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}.
* Asserts that period counts for each window are set correctly. Also asserts that {@link
* Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it asserts
* the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}.
*/
public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCounts) {
int windowCount = timeline.getWindowCount();
......@@ -147,8 +146,8 @@ public final class TimelineAsserts {
.isEqualTo(i + 1);
} else {
int nextWindow = timeline.getNextWindowIndex(expectedWindowIndex, repeatMode, false);
int nextPeriod = nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET
: accumulatedPeriodCounts[nextWindow];
int nextPeriod =
nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET : accumulatedPeriodCounts[nextWindow];
assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false))
.isEqualTo(nextPeriod);
}
......@@ -156,9 +155,7 @@ public final class TimelineAsserts {
}
}
/**
* Asserts that periods' {@link Period#getAdGroupCount()} are set correctly.
*/
/** Asserts that periods' {@link Period#getAdGroupCount()} are set correctly. */
public static void assertAdGroupCounts(Timeline timeline, int... expectedAdGroupCounts) {
Period period = new Period();
for (int i = 0; i < timeline.getPeriodCount(); i++) {
......@@ -166,5 +163,4 @@ public final class TimelineAsserts {
assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment