summaryrefslogtreecommitdiff
path: root/net/download/service/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'net/download/service/src/test')
-rw-r--r--net/download/service/src/test/assets/local-feed1/track1.mp3bin0 -> 43341 bytes
-rw-r--r--net/download/service/src/test/assets/local-feed2/folder.pngbin0 -> 1589 bytes
-rw-r--r--net/download/service/src/test/assets/local-feed2/track1.mp3bin0 -> 43341 bytes
-rw-r--r--net/download/service/src/test/assets/local-feed2/track2.mp3bin0 -> 43497 bytes
-rw-r--r--net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java306
5 files changed, 306 insertions, 0 deletions
diff --git a/net/download/service/src/test/assets/local-feed1/track1.mp3 b/net/download/service/src/test/assets/local-feed1/track1.mp3
new file mode 100644
index 000000000..b1f993c3f
--- /dev/null
+++ b/net/download/service/src/test/assets/local-feed1/track1.mp3
Binary files differ
diff --git a/net/download/service/src/test/assets/local-feed2/folder.png b/net/download/service/src/test/assets/local-feed2/folder.png
new file mode 100644
index 000000000..9e522a986
--- /dev/null
+++ b/net/download/service/src/test/assets/local-feed2/folder.png
Binary files differ
diff --git a/net/download/service/src/test/assets/local-feed2/track1.mp3 b/net/download/service/src/test/assets/local-feed2/track1.mp3
new file mode 100644
index 000000000..b1f993c3f
--- /dev/null
+++ b/net/download/service/src/test/assets/local-feed2/track1.mp3
Binary files differ
diff --git a/net/download/service/src/test/assets/local-feed2/track2.mp3 b/net/download/service/src/test/assets/local-feed2/track2.mp3
new file mode 100644
index 000000000..310cddd6b
--- /dev/null
+++ b/net/download/service/src/test/assets/local-feed2/track2.mp3
Binary files differ
diff --git a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java
new file mode 100644
index 000000000..484929136
--- /dev/null
+++ b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java
@@ -0,0 +1,306 @@
+package de.danoeh.antennapod.net.download.service.feed.local;
+
+import android.content.Context;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.webkit.MimeTypeMap;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterfaceStub;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.storage.database.PodDBAdapter;
+import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowMediaMetadataRetriever;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.database.DBWriter;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.robolectric.Shadows.shadowOf;
+
+/**
+ * Test local feeds handling in class LocalFeedUpdater.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class LocalFeedUpdaterTest {
+
+ /**
+ * URL to locate the local feed media files on the external storage (SD card).
+ * The exact URL doesn't matter here as access to external storage is mocked
+ * (seems not to be supported by Robolectric).
+ */
+ private static final String FEED_URL =
+ "content://com.android.externalstorage.documents/tree/primary%3ADownload%2Flocal-feed";
+ private static final String LOCAL_FEED_DIR1 = "src/test/assets/local-feed1";
+ private static final String LOCAL_FEED_DIR2 = "src/test/assets/local-feed2";
+
+ private Context context;
+
+ @Before
+ public void setUp() throws Exception {
+ // Initialize environment
+ context = InstrumentationRegistry.getInstrumentation().getContext();
+ UserPreferences.init(context);
+ PlaybackPreferences.init(context);
+ SynchronizationSettings.init(context);
+ DownloadServiceInterface.setImpl(new DownloadServiceInterfaceStub());
+
+ // Initialize database
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+
+ mapDummyMetadata(LOCAL_FEED_DIR1);
+ mapDummyMetadata(LOCAL_FEED_DIR2);
+ shadowOf(MimeTypeMap.getSingleton()).addExtensionMimeTypMapping("mp3", "audio/mp3");
+ }
+
+ @After
+ public void tearDown() {
+ DBWriter.tearDownTests();
+ PodDBAdapter.tearDownTests();
+ }
+
+ /**
+ * Test adding a new local feed.
+ */
+ @Test
+ public void testUpdateFeed_AddNewFeed() {
+ // check for empty database
+ List<Feed> feedListBefore = DBReader.getFeedList();
+ assertThat(feedListBefore, is(empty()));
+
+ callUpdateFeed(LOCAL_FEED_DIR2);
+
+ // verify new feed in database
+ verifySingleFeedInDatabaseAndItemCount(2);
+ Feed feedAfter = verifySingleFeedInDatabase();
+ assertEquals(FEED_URL, feedAfter.getDownloadUrl());
+ }
+
+ /**
+ * Test adding further items to an existing local feed.
+ */
+ @Test
+ public void testUpdateFeed_AddMoreItems() {
+ // add local feed with 1 item (localFeedDir1)
+ callUpdateFeed(LOCAL_FEED_DIR1);
+
+ // now add another item (by changing to local feed folder localFeedDir2)
+ callUpdateFeed(LOCAL_FEED_DIR2);
+
+ verifySingleFeedInDatabaseAndItemCount(2);
+ }
+
+ /**
+ * Test removing items from an existing local feed without a corresponding media file.
+ */
+ @Test
+ public void testUpdateFeed_RemoveItems() {
+ // add local feed with 2 items (localFeedDir1)
+ callUpdateFeed(LOCAL_FEED_DIR2);
+
+ // now remove an item (by changing to local feed folder localFeedDir1)
+ callUpdateFeed(LOCAL_FEED_DIR1);
+
+ verifySingleFeedInDatabaseAndItemCount(1);
+ }
+
+ /**
+ * Test feed icon defined in the local feed media folder.
+ */
+ @Test
+ public void testUpdateFeed_FeedIconFromFolder() {
+ callUpdateFeed(LOCAL_FEED_DIR2);
+
+ Feed feedAfter = verifySingleFeedInDatabase();
+ assertThat(feedAfter.getImageUrl(), endsWith("local-feed2/folder.png"));
+ }
+
+ /**
+ * Test default feed icon if there is no matching file in the local feed media folder.
+ */
+ @Test
+ public void testUpdateFeed_FeedIconDefault() {
+ callUpdateFeed(LOCAL_FEED_DIR1);
+
+ Feed feedAfter = verifySingleFeedInDatabase();
+ assertThat(feedAfter.getImageUrl(), startsWith(Feed.PREFIX_GENERATIVE_COVER));
+ }
+
+ /**
+ * Test default feed metadata.
+ *
+ * @see #mapDummyMetadata Title and PubDate are dummy values.
+ */
+ @Test
+ public void testUpdateFeed_FeedMetadata() {
+ callUpdateFeed(LOCAL_FEED_DIR1);
+
+ Feed feed = verifySingleFeedInDatabase();
+ List<FeedItem> feedItems = DBReader.getFeedItemList(feed);
+ assertEquals("track1.mp3", feedItems.get(0).getTitle());
+ }
+
+ @Test
+ public void testGetImageUrl_EmptyFolder() {
+ String imageUrl = LocalFeedUpdater.getImageUrl(Collections.emptyList(), Uri.EMPTY);
+ assertThat(imageUrl, startsWith(Feed.PREFIX_GENERATIVE_COVER));
+ }
+
+ @Test
+ public void testGetImageUrl_NoImageButAudioFiles() {
+ List<FastDocumentFile> folder = Collections.singletonList(mockDocumentFile("audio.mp3", "audio/mp3"));
+ String imageUrl = LocalFeedUpdater.getImageUrl(folder, Uri.EMPTY);
+ assertThat(imageUrl, startsWith(Feed.PREFIX_GENERATIVE_COVER));
+ }
+
+ @Test
+ public void testGetImageUrl_PreferredImagesFilenames() {
+ for (String filename : LocalFeedUpdater.PREFERRED_FEED_IMAGE_FILENAMES) {
+ List<FastDocumentFile> folder = Arrays.asList(mockDocumentFile("audio.mp3", "audio/mp3"),
+ mockDocumentFile(filename, "image/jpeg")); // image MIME type doesn't matter
+ String imageUrl = LocalFeedUpdater.getImageUrl(folder, Uri.EMPTY);
+ assertThat(imageUrl, endsWith(filename));
+ }
+ }
+
+ @Test
+ public void testGetImageUrl_OtherImageFilenameJpg() {
+ List<FastDocumentFile> folder = Arrays.asList(mockDocumentFile("audio.mp3", "audio/mp3"),
+ mockDocumentFile("my-image.jpg", "image/jpeg"));
+ String imageUrl = LocalFeedUpdater.getImageUrl(folder, Uri.EMPTY);
+ assertThat(imageUrl, endsWith("my-image.jpg"));
+ }
+
+ @Test
+ public void testGetImageUrl_OtherImageFilenameJpeg() {
+ List<FastDocumentFile> folder = Arrays.asList(mockDocumentFile("audio.mp3", "audio/mp3"),
+ mockDocumentFile("my-image.jpeg", "image/jpeg"));
+ String imageUrl = LocalFeedUpdater.getImageUrl(folder, Uri.EMPTY);
+ assertThat(imageUrl, endsWith("my-image.jpeg"));
+ }
+
+ @Test
+ public void testGetImageUrl_OtherImageFilenamePng() {
+ List<FastDocumentFile> folder = Arrays.asList(mockDocumentFile("audio.mp3", "audio/mp3"),
+ mockDocumentFile("my-image.png", "image/png"));
+ String imageUrl = LocalFeedUpdater.getImageUrl(folder, Uri.EMPTY);
+ assertThat(imageUrl, endsWith("my-image.png"));
+ }
+
+ @Test
+ public void testGetImageUrl_OtherImageFilenameUnsupportedMimeType() {
+ List<FastDocumentFile> folder = Arrays.asList(mockDocumentFile("audio.mp3", "audio/mp3"),
+ mockDocumentFile("my-image.svg", "image/svg+xml"));
+ String imageUrl = LocalFeedUpdater.getImageUrl(folder, Uri.EMPTY);
+ assertThat(imageUrl, startsWith(Feed.PREFIX_GENERATIVE_COVER));
+ }
+
+ /**
+ * Fill ShadowMediaMetadataRetriever with dummy duration and title.
+ *
+ * @param localFeedDir assets local feed folder with media files
+ */
+ private void mapDummyMetadata(@NonNull String localFeedDir) {
+ for (String fileName : Objects.requireNonNull(new File(localFeedDir).list())) {
+ String path = localFeedDir + '/' + fileName;
+ ShadowMediaMetadataRetriever.addMetadata(path,
+ MediaMetadataRetriever.METADATA_KEY_DURATION, "10");
+ ShadowMediaMetadataRetriever.addMetadata(path,
+ MediaMetadataRetriever.METADATA_KEY_TITLE, fileName);
+ ShadowMediaMetadataRetriever.addMetadata(path,
+ MediaMetadataRetriever.METADATA_KEY_DATE, "20200601T222324");
+ }
+ }
+
+ /**
+ * Calls the method LocalFeedUpdater#tryUpdateFeed with the given local feed folder.
+ *
+ * @param localFeedDir assets local feed folder with media files
+ */
+ private void callUpdateFeed(@NonNull String localFeedDir) {
+ try (MockedStatic<FastDocumentFile> dfMock = Mockito.mockStatic(FastDocumentFile.class)) {
+ // mock external storage
+ dfMock.when(() -> FastDocumentFile.list(any(), any())).thenReturn(mockLocalFolder(localFeedDir));
+
+ // call method to test
+ Feed feed = new Feed(FEED_URL, null);
+ try {
+ LocalFeedUpdater.tryUpdateFeed(feed, context, null, null);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Verify that the database contains exactly one feed and return that feed.
+ */
+ @NonNull
+ private static Feed verifySingleFeedInDatabase() {
+ List<Feed> feedListAfter = DBReader.getFeedList();
+ assertEquals(1, feedListAfter.size());
+ return feedListAfter.get(0);
+ }
+
+ /**
+ * Verify that the database contains exactly one feed and the number of
+ * items in the feed.
+ *
+ * @param expectedItemCount expected number of items in the feed
+ */
+ private static void verifySingleFeedInDatabaseAndItemCount(int expectedItemCount) {
+ Feed feed = verifySingleFeedInDatabase();
+ List<FeedItem> feedItems = DBReader.getFeedItemList(feed);
+ assertEquals(expectedItemCount, feedItems.size());
+ }
+
+ /**
+ * Create a DocumentFile mock object.
+ */
+ @NonNull
+ private static FastDocumentFile mockDocumentFile(@NonNull String fileName, @NonNull String mimeType) {
+ return new FastDocumentFile(fileName, mimeType, Uri.parse("file:///path/" + fileName), 0, 0);
+ }
+
+ private static List<FastDocumentFile> mockLocalFolder(String folderName) {
+ List<FastDocumentFile> files = new ArrayList<>();
+ for (File f : Objects.requireNonNull(new File(folderName).listFiles())) {
+ String extension = MimeTypeMap.getFileExtensionFromUrl(f.getPath());
+ String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ files.add(new FastDocumentFile(f.getName(), mimeType,
+ Uri.parse(f.toURI().toString()), f.length(), f.lastModified()));
+ }
+ return files;
+ }
+}