Merge pull request #3477 from orionlee/test_download_service_event_posting

Test download service event posting
This commit is contained in:
H. Lehmann 2019-10-02 15:25:11 +02:00 committed by GitHub
commit 8f5ff42f6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 260 additions and 37 deletions

View File

@ -0,0 +1,52 @@
package de.danoeh.antennapod.core.service.download;
import android.support.annotation.NonNull;
import de.danoeh.antennapod.core.util.Consumer;
public class StubDownloader extends Downloader {
private final long downloadTime;
@NonNull
private final Consumer<DownloadStatus> onDownloadComplete;
public StubDownloader(@NonNull DownloadRequest request, long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
super(request);
this.downloadTime = downloadTime;
this.onDownloadComplete = onDownloadComplete;
}
@Override
protected void download() {
try {
Thread.sleep(downloadTime);
} catch (Throwable t) {
t.printStackTrace();
}
onDownloadComplete.accept(result);
}
@NonNull
@Override
public DownloadRequest getDownloadRequest() {
return super.getDownloadRequest();
}
@NonNull
@Override
public DownloadStatus getResult() {
return super.getResult();
}
@Override
public boolean isFinished() {
return super.isFinished();
}
@Override
public void cancel() {
super.cancel();
result.setCancelled();
}
}

View File

@ -0,0 +1,127 @@
package de.test.antennapod.service.download;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.StubDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Consumer;
import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @see HttpDownloaderTest for the test of actual download (and saving the file)
*/
@RunWith(AndroidJUnit4.class)
public class DownloadServiceTest {
private CountDownLatch latch = null;
private Feed testFeed = null;
private FeedMedia testMedia11 = null;
private DownloadService.DownloaderFactory origFactory = null;
@Before
public void setUp() throws Exception {
origFactory = DownloadService.getDownloaderFactory();
testFeed = setUpTestFeeds();
testMedia11 = testFeed.getItemAtIndex(0).getMedia();
}
private Feed setUpTestFeeds() throws Exception {
Feed feed = new Feed("url", null, "Test Feed title 1");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
items.add(item1);
FeedMedia media1 = new FeedMedia(0, item1, 123, 1, 1, "audio/mp3", null, "http://example.com/episode.mp3", false, null, 0, 0);
item1.setMedia(media1);
DBWriter.setFeedItem(item1).get();
return feed;
}
@After
public void tearDown() throws Exception {
DownloadService.setDownloaderFactory(origFactory);
}
@Test
public void testEventsGeneratedCaseMediaDownloadSuccess() throws Exception {
// create a stub download that returns successful
//
// OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
// are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
// to do so
DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, downloadStatus -> {
downloadStatus.setSuccessful();
}));
withFeedItemEventListener(feedItemEventListener -> {
try {
assertEquals(0, feedItemEventListener.getEvents().size());
assertFalse("The media in test should not yet been downloaded",
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
DownloadRequester.getInstance().downloadMedia(InstrumentationRegistry.getTargetContext(),
testMedia11);
Awaitility.await()
.atMost(1000, TimeUnit.MILLISECONDS)
.until(() -> feedItemEventListener.getEvents().size() > 0);
assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
} catch (ConditionTimeoutException cte) {
fail("The expected FeedItemEvent (for media download complete) has not been posted. "
+ cte.getMessage());
}
});
}
private static class StubDownloaderFactory implements DownloadService.DownloaderFactory {
private final long downloadTime;
@NonNull
private final Consumer<DownloadStatus> onDownloadComplete;
StubDownloaderFactory(long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
this.downloadTime = downloadTime;
this.onDownloadComplete = onDownloadComplete;
}
@Nullable
@Override
public Downloader create(@NonNull DownloadRequest request) {
return new StubDownloader(request, downloadTime, onDownloadComplete);
}
}
}

View File

@ -7,7 +7,6 @@ import android.support.test.filters.LargeTest;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -18,7 +17,6 @@ import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent; import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
@ -29,8 +27,8 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;
import io.reactivex.functions.Consumer;
import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -168,35 +166,6 @@ public class PlaybackServiceTaskManagerTest {
pstm.shutdown(); pstm.shutdown();
} }
/**
* Provides an listener subscribing to {@link FeedItemEvent} that the callers can use
*
* Note: it uses RxJava's version of {@link Consumer} because it allows exceptions to be thrown.
*/
private static void withFeedItemEventListener(Consumer<FeedItemEventListener> consumer) throws Exception {
FeedItemEventListener feedItemEventListener = new FeedItemEventListener();
try {
EventBus.getDefault().register(feedItemEventListener);
consumer.accept(feedItemEventListener);
} finally {
EventBus.getDefault().unregister(feedItemEventListener);
}
}
private static class FeedItemEventListener {
private final List<FeedItemEvent> events = new ArrayList<>();
@Subscribe
public void onEvent(FeedItemEvent event) {
events.add(event);
}
List<? extends FeedItemEvent> getEvents() {
return events;
}
}
@Test @Test
public void testStartPositionSaver() throws InterruptedException { public void testStartPositionSaver() throws InterruptedException {
final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext(); final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();

View File

@ -0,0 +1,46 @@
package de.test.antennapod.util.event;
import android.support.annotation.NonNull;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import io.reactivex.functions.Consumer;
/**
* Test helpers to listen {@link FeedItemEvent} and handle them accordingly
*
*/
public class FeedItemEventListener {
private final List<FeedItemEvent> events = new ArrayList<>();
/**
* Provides an listener subscribing to {@link FeedItemEvent} that the callers can use
*
* Note: it uses RxJava's version of {@link Consumer} because it allows exceptions to be thrown.
*/
public static void withFeedItemEventListener(@NonNull Consumer<FeedItemEventListener> consumer)
throws Exception {
FeedItemEventListener feedItemEventListener = new FeedItemEventListener();
try {
EventBus.getDefault().register(feedItemEventListener);
consumer.accept(feedItemEventListener);
} finally {
EventBus.getDefault().unregister(feedItemEventListener);
}
}
@Subscribe
public void onEvent(FeedItemEvent event) {
events.add(event);
}
@NonNull
public List<? extends FeedItemEvent> getEvents() {
return events;
}
}

View File

@ -14,6 +14,7 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -434,13 +435,41 @@ public class DownloadService extends Service {
queryDownloads(); queryDownloads();
} }
private Downloader getDownloader(DownloadRequest request) { @VisibleForTesting
public interface DownloaderFactory {
@Nullable
Downloader create(@NonNull DownloadRequest request);
}
private static class DefaultDownloaderFactory implements DownloaderFactory {
@Nullable
@Override
public Downloader create(@NonNull DownloadRequest request) {
if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) { if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
Log.e(TAG, "Could not find appropriate downloader for " + request.getSource()); Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
return null; return null;
} }
return new HttpDownloader(request); return new HttpDownloader(request);
} }
}
private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
@VisibleForTesting
public static DownloaderFactory getDownloaderFactory() {
return downloaderFactory;
}
// public scope rather than package private,
// because androidTest put classes in the non-standard de.test.antennapod hierarchy
@VisibleForTesting
public static void setDownloaderFactory(DownloaderFactory downloaderFactory) {
DownloadService.downloaderFactory = downloaderFactory;
}
private Downloader getDownloader(@NonNull DownloadRequest request) {
return downloaderFactory.create(request);
}
/** /**
* Remove download from the DownloadRequester list and from the * Remove download from the DownloadRequester list and from the