Run DownloadManager on a custom thread while testing

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185674707
This commit is contained in:
eguven 2018-02-14 05:43:47 -08:00 committed by Oliver Woodman
parent 55f2b09340
commit c8e950537d
7 changed files with 156 additions and 85 deletions

View File

@ -24,6 +24,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener; import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.testutil.MockitoUtil;
import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.Cache;
@ -53,12 +54,13 @@ public class DownloadManagerTest extends InstrumentationTestCase {
private DownloadManager downloadManager; private DownloadManager downloadManager;
private File actionFile; private File actionFile;
private TestDownloadListener testDownloadListener; private TestDownloadListener testDownloadListener;
private DummyMainThread dummyMainThread;
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
MockitoUtil.setUpMockito(this); MockitoUtil.setUpMockito(this);
dummyMainThread = new DummyMainThread();
actionFile = Util.createTempFile(getInstrumentation().getContext(), "ExoPlayerTest"); actionFile = Util.createTempFile(getInstrumentation().getContext(), "ExoPlayerTest");
testDownloadListener = new TestDownloadListener(); testDownloadListener = new TestDownloadListener();
setUpDownloadManager(100); setUpDownloadManager(100);
@ -68,6 +70,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
public void tearDown() throws Exception { public void tearDown() throws Exception {
releaseDownloadManager(); releaseDownloadManager();
actionFile.delete(); actionFile.delete();
dummyMainThread.release();
super.tearDown(); super.tearDown();
} }
@ -76,7 +79,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
releaseDownloadManager(); releaseDownloadManager();
} }
try { try {
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -98,7 +101,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
private void releaseDownloadManager() throws Exception { private void releaseDownloadManager() throws Exception {
try { try {
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -345,7 +348,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
remove2Action.post().assertStarted(); remove2Action.post().assertStarted();
download2Action.post().assertDoesNotStart(); download2Action.post().assertDoesNotStart();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -368,7 +371,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// New download actions can be added but they don't start. // New download actions can be added but they don't start.
download3Action.post().assertDoesNotStart(); download3Action.post().assertDoesNotStart();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -393,7 +396,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// download3Action doesn't start as DM was configured to run two downloads in parallel. // download3Action doesn't start as DM was configured to run two downloads in parallel.
download3Action.post().assertDoesNotStart(); download3Action.post().assertDoesNotStart();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -404,7 +407,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// download1Action doesn't stop yet as it ignores interrupts. // download1Action doesn't stop yet as it ignores interrupts.
download2Action.assertStopped(); download2Action.assertStopped();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -462,6 +465,10 @@ public class DownloadManagerTest extends InstrumentationTestCase {
return new FakeDownloadAction(mediaId, true); return new FakeDownloadAction(mediaId, true);
} }
private void runOnMainThread(final Runnable r) throws Throwable {
dummyMainThread.runOnMainThread(r);
}
private static final class TestDownloadListener implements DownloadListener { private static final class TestDownloadListener implements DownloadListener {
private ConditionVariable downloadFinishedCondition; private ConditionVariable downloadFinishedCondition;
@ -544,7 +551,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
} }
private FakeDownloadAction post() throws Throwable { private FakeDownloadAction post() throws Throwable {
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -115,7 +115,12 @@ public final class DownloadManager {
tasks = new ArrayList<>(); tasks = new ArrayList<>();
activeDownloadTasks = new ArrayList<>(); activeDownloadTasks = new ArrayList<>();
handler = new Handler(Looper.getMainLooper());
Looper looper = Looper.myLooper();
if (looper == null) {
looper = Looper.getMainLooper();
}
handler = new Handler(looper);
fileIOThread = new HandlerThread("DownloadManager file i/o"); fileIOThread = new HandlerThread("DownloadManager file i/o");
fileIOThread.start(); fileIOThread.start();

View File

@ -20,13 +20,12 @@ import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.os.ConditionVariable; import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.RobolectricUtil;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
@ -779,40 +778,6 @@ public final class DynamicConcatenatingMediaSourceTest {
return new FakeTimeline(new TimelineWindowDefinition(index + 1, (index + 1) * 111)); return new FakeTimeline(new TimelineWindowDefinition(index + 1, (index + 1) * 111));
} }
private static final class DummyMainThread {
private final HandlerThread thread;
private final Handler handler;
private DummyMainThread() {
thread = new HandlerThread("DummyMainThread");
thread.start();
handler = new Handler(thread.getLooper());
}
/**
* Runs the provided {@link Runnable} on the main thread, blocking until execution completes.
*
* @param runnable The {@link Runnable} to run.
*/
public void runOnMainThread(final Runnable runnable) {
final ConditionVariable finishedCondition = new ConditionVariable();
handler.post(
new Runnable() {
@Override
public void run() {
runnable.run();
finishedCondition.open();
}
});
assertThat(finishedCondition.block(MediaSourceTestRunner.TIMEOUT_MS)).isTrue();
}
public void release() {
thread.quit();
}
}
private static final class TimelineGrabber implements Runnable { private static final class TimelineGrabber implements Runnable {
private final MediaSourceTestRunner testRunner; private final MediaSourceTestRunner testRunner;

View File

@ -28,6 +28,7 @@ import android.test.UiThreadTest;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet; import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.testutil.MockitoUtil;
@ -53,11 +54,13 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
private RepresentationKey fakeRepresentationKey2; private RepresentationKey fakeRepresentationKey2;
private TestDownloadListener downloadListener; private TestDownloadListener downloadListener;
private File actionFile; private File actionFile;
private DummyMainThread dummyMainThread;
@UiThreadTest @UiThreadTest
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
dummyMainThread = new DummyMainThread();
Context context = getInstrumentation().getContext(); Context context = getInstrumentation().getContext();
tempFolder = Util.createTempDirectory(context, "ExoPlayerTest"); tempFolder = Util.createTempDirectory(context, "ExoPlayerTest");
File cacheFolder = new File(tempFolder, "cache"); File cacheFolder = new File(tempFolder, "cache");
@ -85,6 +88,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
public void tearDown() throws Exception { public void tearDown() throws Exception {
downloadManager.release(); downloadManager.release();
Util.recursiveDelete(tempFolder); Util.recursiveDelete(tempFolder);
dummyMainThread.release();
super.tearDown(); super.tearDown();
} }
@ -111,7 +115,7 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
// Run DM accessing code on UI/main thread as it should be. Also not to block handling of loaded // Run DM accessing code on UI/main thread as it should be. Also not to block handling of loaded
// actions. // actions.
runTestOnUiThread( dummyMainThread.runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
@ -226,7 +230,12 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
} }
private void createDownloadManager() { private void createDownloadManager() {
Factory fakeDataSourceFactory = new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet); dummyMainThread.runOnMainThread(
new Runnable() {
@Override
public void run() {
Factory fakeDataSourceFactory =
new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet);
downloadManager = downloadManager =
new DownloadManager( new DownloadManager(
new DownloaderConstructorHelper(cache, fakeDataSourceFactory), new DownloaderConstructorHelper(cache, fakeDataSourceFactory),
@ -235,9 +244,11 @@ public class DownloadManagerDashTest extends InstrumentationTestCase {
actionFile.getAbsolutePath(), actionFile.getAbsolutePath(),
DashDownloadAction.DESERIALIZER); DashDownloadAction.DESERIALIZER);
downloadListener = new TestDownloadListener(downloadManager, this); downloadListener = new TestDownloadListener(downloadManager, dummyMainThread);
downloadManager.addListener(downloadListener); downloadManager.addListener(downloadListener);
downloadManager.startDownloads(); downloadManager.startDownloads();
} }
});
}
} }

View File

@ -27,6 +27,7 @@ import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet; import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
@ -38,6 +39,7 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.util.scheduler.Requirements; import com.google.android.exoplayer2.util.scheduler.Requirements;
import com.google.android.exoplayer2.util.scheduler.Scheduler; import com.google.android.exoplayer2.util.scheduler.Scheduler;
import java.io.File; import java.io.File;
import java.io.IOException;
/** /**
* Unit tests for {@link DownloadService}. * Unit tests for {@link DownloadService}.
@ -53,10 +55,12 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
private DownloadService dashDownloadService; private DownloadService dashDownloadService;
private ConditionVariable pauseDownloadCondition; private ConditionVariable pauseDownloadCondition;
private TestDownloadListener testDownloadListener; private TestDownloadListener testDownloadListener;
private DummyMainThread dummyMainThread;
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
dummyMainThread = new DummyMainThread();
tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest"); tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest");
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
@ -84,14 +88,24 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
.setRandomData("text_segment_1", 1) .setRandomData("text_segment_1", 1)
.setRandomData("text_segment_2", 2) .setRandomData("text_segment_2", 2)
.setRandomData("text_segment_3", 3); .setRandomData("text_segment_3", 3);
DataSource.Factory fakeDataSourceFactory = new FakeDataSource.Factory(null) final DataSource.Factory fakeDataSourceFactory =
.setFakeDataSet(fakeDataSet); new FakeDataSource.Factory(null).setFakeDataSet(fakeDataSet);
fakeRepresentationKey1 = new RepresentationKey(0, 0, 0); fakeRepresentationKey1 = new RepresentationKey(0, 0, 0);
fakeRepresentationKey2 = new RepresentationKey(0, 1, 0); fakeRepresentationKey2 = new RepresentationKey(0, 1, 0);
context = getInstrumentation().getContext(); context = getInstrumentation().getContext();
File actionFile = Util.createTempFile(context, "ExoPlayerTest"); try {
dummyMainThread.runOnMainThread(
new Runnable() {
@Override
public void run() {
File actionFile = null;
try {
actionFile = Util.createTempFile(context, "ExoPlayerTest");
} catch (IOException e) {
throw new RuntimeException(e);
}
actionFile.delete(); actionFile.delete();
final DownloadManager dashDownloadManager = final DownloadManager dashDownloadManager =
new DownloadManager( new DownloadManager(
@ -100,15 +114,10 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
3, 3,
actionFile.getAbsolutePath(), actionFile.getAbsolutePath(),
DashDownloadAction.DESERIALIZER); DashDownloadAction.DESERIALIZER);
testDownloadListener = new TestDownloadListener(dashDownloadManager, this); testDownloadListener = new TestDownloadListener(dashDownloadManager, dummyMainThread);
dashDownloadManager.addListener(testDownloadListener); dashDownloadManager.addListener(testDownloadListener);
dashDownloadManager.startDownloads(); dashDownloadManager.startDownloads();
try {
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
dashDownloadService = dashDownloadService =
new DownloadService(101010) { new DownloadService(101010) {
@ -143,7 +152,8 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
@Override @Override
public void tearDown() throws Exception { public void tearDown() throws Exception {
try { try {
runTestOnUiThread(new Runnable() { dummyMainThread.runOnMainThread(
new Runnable() {
@Override @Override
public void run() { public void run() {
dashDownloadService.onDestroy(); dashDownloadService.onDestroy();
@ -153,6 +163,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
throw new Exception(throwable); throw new Exception(throwable);
} }
Util.recursiveDelete(tempFolder); Util.recursiveDelete(tempFolder);
dummyMainThread.release();
super.tearDown(); super.tearDown();
} }
@ -197,7 +208,7 @@ public class DownloadServiceDashTest extends InstrumentationTestCase {
} }
private void callDownloadServiceOnStart(final DashDownloadAction action) throws Throwable { private void callDownloadServiceOnStart(final DashDownloadAction action) throws Throwable {
runTestOnUiThread( dummyMainThread.runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -17,10 +17,10 @@ package com.google.android.exoplayer2.source.dash.offline;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener; import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
import com.google.android.exoplayer2.testutil.DummyMainThread;
/** A {@link DownloadListener} for testing. */ /** A {@link DownloadListener} for testing. */
/*package*/ final class TestDownloadListener implements DownloadListener { /*package*/ final class TestDownloadListener implements DownloadListener {
@ -28,13 +28,13 @@ import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
private static final int TIMEOUT = 1000; private static final int TIMEOUT = 1000;
private final DownloadManager downloadManager; private final DownloadManager downloadManager;
private final InstrumentationTestCase testCase; private final DummyMainThread dummyMainThread;
private final android.os.ConditionVariable downloadFinishedCondition; private final android.os.ConditionVariable downloadFinishedCondition;
private Throwable downloadError; private Throwable downloadError;
public TestDownloadListener(DownloadManager downloadManager, InstrumentationTestCase testCase) { public TestDownloadListener(DownloadManager downloadManager, DummyMainThread dummyMainThread) {
this.downloadManager = downloadManager; this.downloadManager = downloadManager;
this.testCase = testCase; this.dummyMainThread = dummyMainThread;
this.downloadFinishedCondition = new android.os.ConditionVariable(); this.downloadFinishedCondition = new android.os.ConditionVariable();
} }
@ -55,7 +55,7 @@ import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
* error. * error.
*/ */
public void blockUntilTasksCompleteAndThrowAnyDownloadError() throws Throwable { public void blockUntilTasksCompleteAndThrowAnyDownloadError() throws Throwable {
testCase.runTestOnUiThread( dummyMainThread.runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -0,0 +1,72 @@
/*
* 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.
*/
package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
/** Helper class to simulate main/UI thread in tests. */
public final class DummyMainThread {
/** Default timeout value used for {@link #runOnMainThread(Runnable)}. */
public static final int TIMEOUT_MS = 10000;
private final HandlerThread thread;
private final Handler handler;
public DummyMainThread() {
thread = new HandlerThread("DummyMainThread");
thread.start();
handler = new Handler(thread.getLooper());
}
/**
* Runs the provided {@link Runnable} on the main thread, blocking until execution completes or
* until {@link #TIMEOUT_MS} milliseconds have passed.
*
* @param runnable The {@link Runnable} to run.
*/
public void runOnMainThread(final Runnable runnable) {
runOnMainThread(TIMEOUT_MS, runnable);
}
/**
* Runs the provided {@link Runnable} on the main thread, blocking until execution completes or
* until timeout milliseconds have passed.
*
* @param timeoutMs the maximum time to wait in milliseconds.
* @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();
}
public void release() {
thread.quit();
}
}