summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2019-08-11 21:21:28 +0200
committerByteHamster <info@bytehamster.com>2019-08-11 21:21:28 +0200
commitc29b0ce8c760b75b4c61781acaf56542530ca3cc (patch)
tree56f8509d615bd11ed9fe90c188fd7f6e7415587e /app
parentaca6e3e9e4a69cef4ad7032eb06d49f3c842408f (diff)
downloadAntennaPod-c29b0ce8c760b75b4c61781acaf56542530ca3cc.zip
Fixed app integration tests
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java102
-rw-r--r--app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java19
-rw-r--r--app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java16
-rw-r--r--app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java11
-rw-r--r--app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java26
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java37
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java124
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java74
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java203
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java49
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java29
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java12
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java50
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java38
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java54
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java242
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java253
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java52
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java52
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java294
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java7
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java31
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java42
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java29
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java25
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java32
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java1433
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java6
33 files changed, 1265 insertions, 2125 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 030dcfa4a..ddc6ece5e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -168,6 +168,7 @@ dependencies {
implementation 'com.github.ByteHamster:SearchPreference:v1.2.6'
implementation "org.awaitility:awaitility:$awaitilityVersion"
+ androidTestImplementation 'com.nanohttpd:nanohttpd-webserver:2.1.1'
androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
new file mode 100644
index 000000000..50109c71a
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -0,0 +1,102 @@
+package de.test.antennapod;
+
+import android.content.Context;
+import android.support.annotation.StringRes;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.contrib.DrawerActions;
+import android.support.test.espresso.contrib.RecyclerViewActions;
+import android.support.test.espresso.util.HumanReadables;
+import android.support.test.espresso.util.TreeIterables;
+import android.view.View;
+import de.danoeh.antennapod.R;
+import org.hamcrest.Matcher;
+
+import java.io.File;
+import java.util.concurrent.TimeoutException;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+public class EspressoTestUtils {
+ /**
+ * Perform action of waiting for a specific view id.
+ * https://stackoverflow.com/a/49814995/
+ * @param viewMatcher The view to wait for.
+ * @param millis The timeout of until when to wait for.
+ */
+ public static ViewAction waitForView(final Matcher<View> viewMatcher, final long millis) {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isRoot();
+ }
+
+ @Override
+ public String getDescription() {
+ return "wait for a specific view for" + millis + " millis.";
+ }
+
+ @Override
+ public void perform(final UiController uiController, final View view) {
+ uiController.loopMainThreadUntilIdle();
+ final long startTime = System.currentTimeMillis();
+ final long endTime = startTime + millis;
+
+ do {
+ for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
+ // found view with required ID
+ if (viewMatcher.matches(child)) {
+ return;
+ }
+ }
+
+ uiController.loopMainThreadForAtLeast(50);
+ }
+ while (System.currentTimeMillis() < endTime);
+
+ // timeout happens
+ throw new PerformException.Builder()
+ .withActionDescription(this.getDescription())
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(new TimeoutException())
+ .build();
+ }
+ };
+ }
+
+ /**
+ * Clear all app databases
+ */
+ public static void clearAppData() {
+ File root = InstrumentationRegistry.getTargetContext().getFilesDir().getParentFile();
+ String[] sharedPreferencesFileNames = new File(root, "shared_prefs").list();
+ for (String fileName : sharedPreferencesFileNames) {
+ System.out.println("Cleared database: " + fileName);
+ InstrumentationRegistry.getTargetContext().
+ getSharedPreferences(fileName.replace(".xml", ""), Context.MODE_PRIVATE).edit().clear().commit();
+ }
+ }
+
+ public static void clickPreference(@StringRes int title) {
+ onView(withId(R.id.list)).perform(
+ RecyclerViewActions.actionOnItem(hasDescendant(withText(title)),
+ click()));
+ }
+
+ public static void openNavDrawer() {
+ onView(isRoot()).perform(waitForView(withId(R.id.drawer_layout), 1000));
+ onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
+ }
+
+ public static void closeNavDrawer() {
+ onView(isRoot()).perform(waitForView(withId(R.id.drawer_layout), 1000));
+ onView(withId(R.id.drawer_layout)).perform(DrawerActions.close());
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
index 80dded59f..e8a87ecf1 100644
--- a/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
@@ -3,21 +3,27 @@ package de.test.antennapod.entities;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
/**
* Tests for {@link ExternalMedia} entity.
*/
-public class ExternalMediaTest extends InstrumentationTestCase {
+@SmallTest
+public class ExternalMediaTest {
private static final int NOT_SET = -1;
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
clearSharedPrefs();
}
@@ -30,9 +36,10 @@ public class ExternalMediaTest extends InstrumentationTestCase {
}
private SharedPreferences getDefaultSharedPrefs() {
- return PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext());
+ return PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getTargetContext());
}
+ @Test
public void testSaveCurrentPositionUpdatesPreferences() {
final int POSITION = 50;
final int LAST_PLAYED_TIME = 1650;
diff --git a/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java b/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
index 23a05ac8c..8cd9b0fa1 100644
--- a/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
@@ -1,12 +1,17 @@
package de.test.antennapod.feed;
-import android.test.AndroidTestCase;
-
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.FeedFilter;
import de.danoeh.antennapod.core.feed.FeedItem;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
-public class FeedFilterTest extends AndroidTestCase {
+@SmallTest
+public class FeedFilterTest {
+ @Test
public void testNullFilter() throws Exception {
FeedFilter filter = new FeedFilter();
FeedItem item = new FeedItem();
@@ -19,6 +24,7 @@ public class FeedFilterTest extends AndroidTestCase {
assertTrue(filter.shouldAutoDownload(item));
}
+ @Test
public void testBasicIncludeFilter() throws Exception {
String includeFilter = "Hello";
FeedFilter filter = new FeedFilter(includeFilter, "");
@@ -36,6 +42,7 @@ public class FeedFilterTest extends AndroidTestCase {
assertTrue(!filter.shouldAutoDownload(item2));
}
+ @Test
public void testBasicExcludeFilter() throws Exception {
String excludeFilter = "Hello";
FeedFilter filter = new FeedFilter("", excludeFilter);
@@ -53,6 +60,7 @@ public class FeedFilterTest extends AndroidTestCase {
assertTrue(filter.shouldAutoDownload(item2));
}
+ @Test
public void testComplexIncludeFilter() throws Exception {
String includeFilter = "Hello \n\"Two words\"";
FeedFilter filter = new FeedFilter(includeFilter, "");
@@ -74,6 +82,7 @@ public class FeedFilterTest extends AndroidTestCase {
assertTrue(filter.shouldAutoDownload(item3));
}
+ @Test
public void testComplexExcludeFilter() throws Exception {
String excludeFilter = "Hello \"Two words\"";
FeedFilter filter = new FeedFilter("", excludeFilter);
@@ -95,6 +104,7 @@ public class FeedFilterTest extends AndroidTestCase {
assertTrue(!filter.shouldAutoDownload(item3));
}
+ @Test
public void testComboFilter() throws Exception {
String includeFilter = "Hello world";
String excludeFilter = "dislike";
diff --git a/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java b/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
index ced0d7a28..9779b32a5 100644
--- a/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
@@ -1,16 +1,20 @@
package de.test.antennapod.feed;
-import android.test.AndroidTestCase;
-
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.FeedItem;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
-public class FeedItemTest extends AndroidTestCase {
+@SmallTest
+public class FeedItemTest {
private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
private static final String TEXT_SHORT = "Lorem ipsum";
/**
* If one of `description` or `content:encoded` is null, use the other one.
*/
+ @Test
public void testShownotesNullValues() throws Exception {
testShownotes(null, TEXT_LONG);
testShownotes(TEXT_LONG, null);
@@ -19,6 +23,7 @@ public class FeedItemTest extends AndroidTestCase {
/**
* If `description` is reasonably longer than `content:encoded`, use `description`.
*/
+ @Test
public void testShownotesLength() throws Exception {
testShownotes(TEXT_SHORT, TEXT_LONG);
testShownotes(TEXT_LONG, TEXT_SHORT);
diff --git a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
index 9419d2318..dfb78d5a9 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -2,14 +2,17 @@ package de.test.antennapod.handler;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
-import android.test.InstrumentationTestCase;
-
+import android.support.test.filters.SmallTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -26,17 +29,23 @@ import de.test.antennapod.util.syndication.feedgenerator.AtomGenerator;
import de.test.antennapod.util.syndication.feedgenerator.FeedGenerator;
import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
/**
* Tests for FeedHandler
*/
-public class FeedHandlerTest extends InstrumentationTestCase {
+@SmallTest
+public class FeedHandlerTest {
private static final String FEEDS_DIR = "testfeeds";
private File file = null;
private OutputStream outputStream = null;
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
File destDir = context.getExternalFilesDir(FEEDS_DIR);
assertNotNull(destDir);
@@ -51,9 +60,8 @@ public class FeedHandlerTest extends InstrumentationTestCase {
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
file.delete();
file = null;
@@ -130,12 +138,14 @@ public class FeedHandlerTest extends InstrumentationTestCase {
}
}
+ @Test
public void testRSS2Basic() throws IOException, UnsupportedFeedtypeException, SAXException, ParserConfigurationException {
Feed f1 = createTestFeed(10, false, true, true);
Feed f2 = runFeedTest(f1, new RSS2Generator(), "UTF-8", RSS2Generator.FEATURE_WRITE_GUID);
feedValid(f1, f2, Feed.TYPE_RSS2);
}
+ @Test
public void testAtomBasic() throws IOException, UnsupportedFeedtypeException, SAXException, ParserConfigurationException {
Feed f1 = createTestFeed(10, false, true, true);
Feed f2 = runFeedTest(f1, new AtomGenerator(), "UTF-8", 0);
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index 4a5883c64..967bf431c 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -1,6 +1,7 @@
package de.test.antennapod.service.download;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
import android.util.Log;
import java.io.File;
@@ -14,8 +15,17 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.util.DownloadError;
import de.test.antennapod.util.service.download.HTTPBin;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
-public class HttpDownloaderTest extends InstrumentationTestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@LargeTest
+public class HttpDownloaderTest {
private static final String TAG = "HttpDownloaderTest";
private static final String DOWNLOAD_DIR = "testdownloads";
@@ -29,9 +39,8 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
super();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
File[] contents = destDir.listFiles();
for (File f : contents) {
assertTrue(f.delete());
@@ -40,11 +49,10 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
httpServer.stop();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- UserPreferences.init(getInstrumentation().getTargetContext());
- destDir = getInstrumentation().getTargetContext().getExternalFilesDir(DOWNLOAD_DIR);
+ @Before
+ public void setUp() throws Exception {
+ UserPreferences.init(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ destDir = InstrumentationRegistry.getTargetContext().getExternalFilesDir(DOWNLOAD_DIR);
assertNotNull(destDir);
assertTrue(destDir.exists());
httpServer = new HTTPBin();
@@ -84,22 +92,27 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
private static final String URL_404 = HTTPBin.BASE_URL + "/status/404";
private static final String URL_AUTH = HTTPBin.BASE_URL + "/basic-auth/user/passwd";
+ @Test
public void testPassingHttp() {
download(HTTPBin.BASE_URL + "/status/200", "test200", true);
}
+ @Test
public void testRedirect() {
download(HTTPBin.BASE_URL + "/redirect/4", "testRedirect", true);
}
+ @Test
public void testGzip() {
download(HTTPBin.BASE_URL + "/gzip/100", "testGzip", true);
}
+ @Test
public void test404() {
download(URL_404, "test404", false);
}
+ @Test
public void testCancel() {
final String url = HTTPBin.BASE_URL + "/delay/3";
FeedFileImpl feedFile = setupFeedFile(url, "delay", true);
@@ -124,11 +137,13 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
assertFalse(new File(feedFile.getFile_url()).exists());
}
+ @Test
public void testDeleteOnFailShouldDelete() {
Downloader downloader = download(URL_404, "testDeleteOnFailShouldDelete", false, true, null, null, true);
assertFalse(new File(downloader.getDownloadRequest().getDestination()).exists());
}
+ @Test
public void testDeleteOnFailShouldNotDelete() throws IOException {
String filename = "testDeleteOnFailShouldDelete";
File dest = new File(destDir, filename);
@@ -138,10 +153,12 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
assertTrue(new File(downloader.getDownloadRequest().getDestination()).exists());
}
+ @Test
public void testAuthenticationShouldSucceed() throws InterruptedException {
download(URL_AUTH, "testAuthSuccess", true, true, "user", "passwd", true);
}
+ @Test
public void testAuthenticationShouldFail() {
Downloader downloader = download(URL_AUTH, "testAuthSuccess", false, true, "user", "Wrong passwd", true);
assertEquals(DownloadError.ERROR_UNAUTHORIZED, downloader.getResult().getReason());
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java b/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
new file mode 100644
index 000000000..085e2c479
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
@@ -0,0 +1,124 @@
+package de.test.antennapod.service.playback;
+
+import android.support.annotation.NonNull;
+import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+public class CancelablePSMPCallback implements PlaybackServiceMediaPlayer.PSMPCallback {
+
+ private final PlaybackServiceMediaPlayer.PSMPCallback originalCallback;
+ private boolean isCancelled = false;
+
+ public CancelablePSMPCallback(PlaybackServiceMediaPlayer.PSMPCallback originalCallback) {
+ this.originalCallback = originalCallback;
+ }
+
+ public void cancel() {
+ isCancelled = true;
+ }
+
+ @Override
+ public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.statusChanged(newInfo);
+ }
+
+ @Override
+ public void shouldStop() {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.shouldStop();
+ }
+
+ @Override
+ public void playbackSpeedChanged(float s) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.playbackSpeedChanged(s);
+ }
+
+ @Override
+ public void setSpeedAbilityChanged() {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.setSpeedAbilityChanged();
+ }
+
+ @Override
+ public void onBufferingUpdate(int percent) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.onBufferingUpdate(percent);
+ }
+
+ @Override
+ public void onMediaChanged(boolean reloadUI) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.onMediaChanged(reloadUI);
+ }
+
+ @Override
+ public boolean onMediaPlayerInfo(int code, int resourceId) {
+ if (isCancelled) {
+ return true;
+ }
+ return originalCallback.onMediaPlayerInfo(code, resourceId);
+ }
+
+ @Override
+ public boolean onMediaPlayerError(Object inObj, int what, int extra) {
+ if (isCancelled) {
+ return true;
+ }
+ return originalCallback.onMediaPlayerError(inObj, what, extra);
+ }
+
+ @Override
+ public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.onPostPlayback(media, ended, skipped, playingNext);
+ }
+
+ @Override
+ public void onPlaybackStart(@NonNull Playable playable, int position) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.onPlaybackStart(playable, position);
+ }
+
+ @Override
+ public void onPlaybackPause(Playable playable, int position) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.onPlaybackPause(playable, position);
+ }
+
+ @Override
+ public Playable getNextInQueue(Playable currentMedia) {
+ if (isCancelled) {
+ return null;
+ }
+ return originalCallback.getNextInQueue(currentMedia);
+ }
+
+ @Override
+ public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.onPlaybackEnded(mediaType, stopPlaying);
+ }
+} \ No newline at end of file
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java b/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
new file mode 100644
index 000000000..113239908
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
@@ -0,0 +1,74 @@
+package de.test.antennapod.service.playback;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+public class DefaultPSMPCallback implements PlaybackServiceMediaPlayer.PSMPCallback {
+ @Override
+ public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
+
+ }
+
+ @Override
+ public void shouldStop() {
+
+ }
+
+ @Override
+ public void playbackSpeedChanged(float s) {
+
+ }
+
+ @Override
+ public void setSpeedAbilityChanged() {
+
+ }
+
+ @Override
+ public void onBufferingUpdate(int percent) {
+
+ }
+
+ @Override
+ public void onMediaChanged(boolean reloadUI) {
+
+ }
+
+ @Override
+ public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
+ return false;
+ }
+
+ @Override
+ public boolean onMediaPlayerError(Object inObj, int what, int extra) {
+ return false;
+ }
+
+ @Override
+ public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
+
+ }
+
+ @Override
+ public void onPlaybackStart(@NonNull Playable playable, int position) {
+
+ }
+
+ @Override
+ public void onPlaybackPause(Playable playable, int position) {
+
+ }
+
+ @Override
+ public Playable getNextInQueue(Playable currentMedia) {
+ return null;
+ }
+
+ @Override
+ public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
+
+ }
+ } \ No newline at end of file
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
index 2f53ea8a6..7a9ede8c5 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
@@ -1,10 +1,10 @@
package de.test.antennapod.service.playback;
import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import de.test.antennapod.EspressoTestUtils;
import junit.framework.AssertionFailedError;
import org.apache.commons.io.IOUtils;
@@ -22,42 +22,49 @@ 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.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.LocalPSMP;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.test.antennapod.util.service.download.HTTPBin;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Test class for LocalPSMP
*/
-public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
- private static final String TAG = "PlaybackServiceMediaPlayerTest";
-
+@MediumTest
+public class PlaybackServiceMediaPlayerTest {
private static final String PLAYABLE_FILE_URL = "http://127.0.0.1:" + HTTPBin.PORT + "/files/0";
private static final String PLAYABLE_DEST_URL = "psmptestfile.mp3";
private String PLAYABLE_LOCAL_URL = null;
- private static final int LATCH_TIMEOUT_SECONDS = 10;
+ private static final int LATCH_TIMEOUT_SECONDS = 3;
private HTTPBin httpServer;
private volatile AssertionFailedError assertionError;
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
PodDBAdapter.deleteDatabase();
httpServer.stop();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
assertionError = null;
-
- final Context context = getInstrumentation().getTargetContext();
+ EspressoTestUtils.clearAppData();
+ final Context context = InstrumentationRegistry.getTargetContext();
// create new database
PodDBAdapter.init(context);
@@ -78,14 +85,14 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertTrue(cacheDir.canWrite());
assertTrue(cacheDir.canRead());
if (!dest.exists()) {
- InputStream i = getInstrumentation().getContext().getAssets().open("testfile.mp3");
+ InputStream i = InstrumentationRegistry.getContext().getAssets().open("testfile.mp3");
OutputStream o = new FileOutputStream(new File(cacheDir, PLAYABLE_DEST_URL));
IOUtils.copy(i, o);
o.flush();
o.close();
i.close();
}
- PLAYABLE_LOCAL_URL = "file://" + dest.getAbsolutePath();
+ PLAYABLE_LOCAL_URL = dest.getAbsolutePath();
assertEquals(0, httpServer.serveFile(dest));
}
@@ -102,10 +109,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertNotNull(info.playable);
break;
case STOPPED:
- assertNull(info.playable);
- break;
case ERROR:
assertNull(info.playable);
+ break;
}
} catch (AssertionFailedError e) {
if (assertionError == null)
@@ -113,6 +119,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
}
+ @Test
public void testInit() {
final Context c = getInstrumentation().getTargetContext();
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, new DefaultPSMPCallback());
@@ -137,11 +144,11 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
return media;
}
-
+ @Test
public void testPlayMediaObjectStreamNoStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -162,7 +169,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
psmp.playMediaObject(p, true, false, false);
@@ -173,13 +180,15 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.INITIALIZED);
assertFalse(psmp.isStartWhenPrepared());
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectStreamStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -200,7 +209,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
psmp.playMediaObject(p, true, true, false);
@@ -212,13 +221,15 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.INITIALIZED);
assertTrue(psmp.isStartWhenPrepared());
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectStreamNoStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(4);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -242,7 +253,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
psmp.playMediaObject(p, true, false, true);
@@ -251,14 +262,16 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
throw assertionError;
assertTrue(res);
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.PREPARED);
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectStreamStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(5);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -285,7 +298,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
psmp.playMediaObject(p, true, true, true);
@@ -294,13 +307,15 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
throw assertionError;
assertTrue(res);
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.PLAYING);
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectLocalNoStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -321,7 +336,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, false, false);
@@ -331,13 +346,15 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertTrue(res);
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.INITIALIZED);
assertFalse(psmp.isStartWhenPrepared());
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectLocalStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -358,7 +375,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, true, false);
@@ -368,13 +385,15 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertTrue(res);
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.INITIALIZED);
assertTrue(psmp.isStartWhenPrepared());
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectLocalNoStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(4);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -398,7 +417,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, false, true);
@@ -407,13 +426,15 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
throw assertionError;
assertTrue(res);
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.PREPARED);
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPlayMediaObjectLocalStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(5);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@@ -441,7 +462,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
countDownLatch.countDown();
}
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, true, true);
@@ -450,6 +471,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
throw assertionError;
assertTrue(res);
assertTrue(psmp.getPSMPInfo().playerStatus == PlayerStatus.PLAYING);
+ callback.cancel();
psmp.shutdown();
}
@@ -458,7 +480,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
final int latchCount = (stream && reinit) ? 2 : 1;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@@ -503,7 +525,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
return false;
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
if (initialState == PlayerStatus.PLAYING) {
@@ -514,41 +536,51 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
if (assertionError != null)
throw assertionError;
assertTrue(res || initialState != PlayerStatus.PLAYING);
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPauseDefaultState() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.STOPPED, false, false, false, 1);
}
+ @Test
public void testPausePlayingStateNoAbandonNoReinitNoStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, false, false, false, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateNoAbandonNoReinitStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, true, false, false, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateAbandonNoReinitNoStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, false, true, false, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateAbandonNoReinitStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, true, true, false, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateNoAbandonReinitNoStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, false, false, true, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateNoAbandonReinitStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, true, false, true, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateAbandonReinitNoStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, false, true, true, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPausePlayingStateAbandonReinitStream() throws InterruptedException {
pauseTestSkeleton(PlayerStatus.PLAYING, true, true, true, LATCH_TIMEOUT_SECONDS);
}
@@ -559,7 +591,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
(initialState == PlayerStatus.PREPARED) ? 1 : 0;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@@ -584,7 +616,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
return false;
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
if (initialState == PlayerStatus.PREPARED || initialState == PlayerStatus.PLAYING || initialState == PlayerStatus.PAUSED) {
boolean startWhenPrepared = (initialState != PlayerStatus.PREPARED);
@@ -598,17 +630,21 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
if (assertionError != null)
throw assertionError;
assertTrue(res || (initialState != PlayerStatus.PAUSED && initialState != PlayerStatus.PREPARED));
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testResumePausedState() throws InterruptedException {
resumeTestSkeleton(PlayerStatus.PAUSED, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testResumePreparedState() throws InterruptedException {
resumeTestSkeleton(PlayerStatus.PREPARED, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testResumePlayingState() throws InterruptedException {
resumeTestSkeleton(PlayerStatus.PLAYING, 1);
}
@@ -617,7 +653,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
final Context c = getInstrumentation().getTargetContext();
final int latchCount = 1;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@@ -639,7 +675,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
return false;
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
if (initialState == PlayerStatus.INITIALIZED
@@ -663,21 +699,26 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
if (assertionError != null)
throw assertionError;
assertTrue(res);
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testPrepareInitializedState() throws InterruptedException {
prepareTestSkeleton(PlayerStatus.INITIALIZED, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPreparePlayingState() throws InterruptedException {
prepareTestSkeleton(PlayerStatus.PLAYING, 1);
}
+ @Test
public void testPreparePausedState() throws InterruptedException {
prepareTestSkeleton(PlayerStatus.PAUSED, 1);
}
+ @Test
public void testPreparePreparedState() throws InterruptedException {
prepareTestSkeleton(PlayerStatus.PREPARED, 1);
}
@@ -686,7 +727,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
final Context c = getInstrumentation().getTargetContext();
final int latchCount = 2;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
- PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
+ CancelablePSMPCallback callback = new CancelablePSMPCallback(new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@@ -708,7 +749,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
return false;
}
- };
+ });
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
boolean prepareImmediately = initialState != PlayerStatus.INITIALIZED;
@@ -722,21 +763,26 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
if (assertionError != null)
throw assertionError;
assertTrue(res);
+ callback.cancel();
psmp.shutdown();
}
+ @Test
public void testReinitPlayingState() throws InterruptedException {
reinitTestSkeleton(PlayerStatus.PLAYING, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testReinitPausedState() throws InterruptedException {
reinitTestSkeleton(PlayerStatus.PAUSED, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testPreparedPlayingState() throws InterruptedException {
reinitTestSkeleton(PlayerStatus.PREPARED, LATCH_TIMEOUT_SECONDS);
}
+ @Test
public void testReinitInitializedState() throws InterruptedException {
reinitTestSkeleton(PlayerStatus.INITIALIZED, LATCH_TIMEOUT_SECONDS);
}
@@ -746,71 +792,4 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
super("Unexpected state change: " + status);
}
}
-
- private class DefaultPSMPCallback implements PlaybackServiceMediaPlayer.PSMPCallback {
- @Override
- public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
-
- }
-
- @Override
- public void shouldStop() {
-
- }
-
- @Override
- public void playbackSpeedChanged(float s) {
-
- }
-
- @Override
- public void setSpeedAbilityChanged() {
-
- }
-
- @Override
- public void onBufferingUpdate(int percent) {
-
- }
-
- @Override
- public void onMediaChanged(boolean reloadUI) {
-
- }
-
- @Override
- public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
- return false;
- }
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- return false;
- }
-
- @Override
- public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
-
- }
-
- @Override
- public void onPlaybackStart(@NonNull Playable playable, int position) {
-
- }
-
- @Override
- public void onPlaybackPause(Playable playable, int position) {
-
- }
-
- @Override
- public Playable getNextInQueue(Playable currentMedia) {
- return null;
- }
-
- @Override
- public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
-
- }
- }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index c8222b376..9c0e90929 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
@@ -3,8 +3,7 @@ package de.test.antennapod.service.playback;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.InstrumentationTestCase;
+import android.support.test.filters.LargeTest;
import java.util.ArrayList;
import java.util.Date;
@@ -23,9 +22,8 @@ import org.greenrobot.eventbus.EventBus;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -33,7 +31,7 @@ import static org.junit.Assert.fail;
/**
* Test class for PlaybackServiceTaskManager
*/
-@RunWith(AndroidJUnit4.class)
+@LargeTest
public class PlaybackServiceTaskManagerTest {
@After
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
index 5cd4e9906..e78894e59 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -3,8 +3,6 @@ package de.test.antennapod.storage;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.test.FlakyTest;
-import android.test.InstrumentationTestCase;
import java.io.File;
import java.io.IOException;
@@ -12,41 +10,46 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Test class for DBTasks
*/
-public class DBCleanupTests extends InstrumentationTestCase {
-
- private static final String TAG = "DBTasksTest";
+@SmallTest
+public class DBCleanupTests {
static final int EPISODE_CACHE_SIZE = 5;
- private final int cleanupAlgorithm;
+ private int cleanupAlgorithm;
Context context;
private File destFolder;
public DBCleanupTests() {
- this.cleanupAlgorithm = UserPreferences.EPISODE_CLEANUP_DEFAULT;
+ setCleanupAlgorithm(UserPreferences.EPISODE_CLEANUP_DEFAULT);
}
- public DBCleanupTests(int cleanupAlgorithm) {
+ protected void setCleanupAlgorithm(int cleanupAlgorithm) {
this.cleanupAlgorithm = cleanupAlgorithm;
}
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
+ @After
+ public void tearDown() throws Exception {
assertTrue(PodDBAdapter.deleteDatabase());
cleanupDestFolder(destFolder);
@@ -59,11 +62,11 @@ public class DBCleanupTests extends InstrumentationTestCase {
}
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- context = getInstrumentation().getTargetContext();
- destFolder = context.getExternalCacheDir();
+ @Before
+ public void setUp() throws Exception {
+ context = InstrumentationRegistry.getTargetContext();
+ destFolder = new File(context.getCacheDir(), "DDCleanupTests");
+ destFolder.mkdir();
cleanupDestFolder(destFolder);
assertNotNull(destFolder);
assertTrue(destFolder.exists());
@@ -84,7 +87,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
UserPreferences.init(context);
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupShouldDelete() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
@@ -140,7 +143,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
}
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupHandleUnplayed() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
@@ -156,7 +159,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
}
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
@@ -177,7 +180,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
* call to DBWriter.deleteFeedMediaOfItem instead of the ID of the FeedMedia. This would cause the wrong item to be deleted.
* @throws IOException
*/
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue_withFeedsWithNoMedia() throws IOException {
// add feed with no enclosures so that item ID != media ID
saveFeedlist(1, 10, false);
@@ -195,7 +198,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
testPerformAutoCleanupShouldNotDeleteBecauseInQueue();
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupShouldNotDeleteBecauseFavorite() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
index 7925941ec..fe5091eb9 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
@@ -3,8 +3,6 @@ package de.test.antennapod.storage;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.test.FlakyTest;
-import android.test.InstrumentationTestCase;
import java.io.File;
import java.io.IOException;
@@ -12,17 +10,27 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Tests that the APNullCleanupAlgorithm is working correctly.
*/
-public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase {
+@SmallTest
+public class DBNullCleanupAlgorithmTest {
private static final String TAG = "DBNullCleanupAlgorithmTest";
private static final int EPISODE_CACHE_SIZE = 5;
@@ -31,10 +39,8 @@ public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase {
private File destFolder;
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
+ @After
+ public void tearDown() throws Exception {
assertTrue(PodDBAdapter.deleteDatabase());
cleanupDestFolder(destFolder);
@@ -47,10 +53,9 @@ public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase {
}
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- context = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() throws Exception {
+ context = InstrumentationRegistry.getTargetContext();
destFolder = context.getExternalCacheDir();
cleanupDestFolder(destFolder);
assertNotNull(destFolder);
@@ -77,7 +82,7 @@ public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase {
* The null algorithm should never delete any items, even if they're played and not in the queue.
* @throws IOException
*/
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupShouldNotDelete() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
index d602d150b..3320aa545 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
@@ -1,33 +1,37 @@
package de.test.antennapod.storage;
-import android.test.FlakyTest;
-
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBTasks;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* Tests that the APQueueCleanupAlgorithm is working correctly.
*/
+@SmallTest
public class DBQueueCleanupAlgorithmTest extends DBCleanupTests {
private static final String TAG = "DBQueueCleanupAlgorithmTest";
public DBQueueCleanupAlgorithmTest() {
- super(UserPreferences.EPISODE_CLEANUP_QUEUE);
+ setCleanupAlgorithm(UserPreferences.EPISODE_CLEANUP_QUEUE);
}
/**
* For APQueueCleanupAlgorithm we expect even unplayed episodes to be deleted if needed
* if they aren't in the queue
*/
- @FlakyTest(tolerance = 3)
+ @Test
public void testPerformAutoCleanupHandleUnplayed() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
index 0c5cf10cb..d99a409d3 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
@@ -1,13 +1,15 @@
package de.test.antennapod.storage;
import android.content.Context;
-import android.test.InstrumentationTestCase;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -15,32 +17,39 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.FeedItemStatistics;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.LongList;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
/**
* Test class for DBReader
*/
-public class DBReaderTest extends InstrumentationTestCase {
+@SmallTest
+public class DBReaderTest {
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
assertTrue(PodDBAdapter.deleteDatabase());
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
// create new database
- PodDBAdapter.init(getInstrumentation().getTargetContext());
+ PodDBAdapter.init(InstrumentationRegistry.getTargetContext());
PodDBAdapter.deleteDatabase();
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
}
+ @Test
public void testGetFeedList() {
List<Feed> feeds = saveFeedlist(10, 0, false);
List<Feed> savedFeeds = DBReader.getFeedList();
@@ -51,6 +60,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetFeedListSortOrder() {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -80,6 +90,7 @@ public class DBReaderTest extends InstrumentationTestCase {
assertEquals("Wrong id of feed 4: ", feed4.getId(), saved.get(3).getId());
}
+ @Test
public void testFeedListDownloadUrls() {
List<Feed> feeds = saveFeedlist(10, 0, false);
List<String> urls = DBReader.getFeedListDownloadUrls();
@@ -90,8 +101,9 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testLoadFeedDataOfFeedItemlist() {
- final Context context = getInstrumentation().getTargetContext();
+ final Context context = InstrumentationRegistry.getTargetContext();
final int numFeeds = 10;
final int numItems = 1;
List<Feed> feeds = saveFeedlist(numFeeds, numItems, false);
@@ -114,6 +126,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetFeedItemList() {
final int numFeeds = 1;
final int numItems = 10;
@@ -153,6 +166,7 @@ public class DBReaderTest extends InstrumentationTestCase {
return queue;
}
+ @Test
public void testGetQueueIDList() {
final int numItems = 10;
List<FeedItem> queue = saveQueue(numItems);
@@ -165,6 +179,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetQueue() {
final int numItems = 10;
List<FeedItem> queue = saveQueue(numItems);
@@ -205,6 +220,7 @@ public class DBReaderTest extends InstrumentationTestCase {
return downloaded;
}
+ @Test
public void testGetDownloadedItems() {
final int numItems = 10;
List<FeedItem> downloaded = saveDownloadedItems(numItems);
@@ -242,6 +258,7 @@ public class DBReaderTest extends InstrumentationTestCase {
return newItems;
}
+ @Test
public void testGetNewItemIds() {
final int numItems = 10;
@@ -266,6 +283,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetPlaybackHistory() {
final int numItems = (DBReader.PLAYBACK_HISTORY_SIZE + 1) * 2;
final int playedItems = DBReader.PLAYBACK_HISTORY_SIZE + 1;
@@ -295,6 +313,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetFeedStatisticsCheckOrder() {
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
@@ -307,6 +326,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetNavDrawerDataQueueEmptyNoUnreadItems() {
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
@@ -317,6 +337,7 @@ public class DBReaderTest extends InstrumentationTestCase {
assertEquals(0, navDrawerData.queueSize);
}
+ @Test
public void testGetNavDrawerDataQueueNotEmptyWithUnreadItems() {
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
@@ -345,8 +366,9 @@ public class DBReaderTest extends InstrumentationTestCase {
assertEquals(NUM_QUEUE, navDrawerData.queueSize);
}
+ @Test
public void testGetFeedItemlistCheckChaptersFalse() throws Exception {
- Context context = getInstrumentation().getTargetContext();
+ Context context = InstrumentationRegistry.getTargetContext();
List<Feed> feeds = DBTestUtils.saveFeedlist(10, 10, false, false, 0);
for (Feed feed : feeds) {
for (FeedItem item : feed.getItems()) {
@@ -355,6 +377,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetFeedItemlistCheckChaptersTrue() throws Exception {
List<Feed> feeds = saveFeedlist(10, 10, false, true, 10);
for (Feed feed : feeds) {
@@ -364,6 +387,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testLoadChaptersOfFeedItemNoChapters() throws Exception {
List<Feed> feeds = saveFeedlist(1, 3, false, false, 0);
saveFeedlist(1, 3, false, true, 3);
@@ -377,6 +401,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testLoadChaptersOfFeedItemWithChapters() throws Exception {
final int NUM_CHAPTERS = 3;
DBTestUtils.saveFeedlist(1, 3, false, false, 0);
@@ -392,6 +417,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
+ @Test
public void testGetItemWithChapters() throws Exception {
final int NUM_CHAPTERS = 3;
List<Feed> feeds = saveFeedlist(1, 1, false, true, NUM_CHAPTERS);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
index 9cd7689ba..6af12315c 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -1,14 +1,15 @@
package de.test.antennapod.storage;
import android.content.Context;
-import android.test.FlakyTest;
-import android.test.InstrumentationTestCase;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -16,28 +17,30 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Test class for DBTasks
*/
-public class DBTasksTest extends InstrumentationTestCase {
-
- private static final String TAG = "DBTasksTest";
-
+@SmallTest
+public class DBTasksTest {
private Context context;
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
assertTrue(PodDBAdapter.deleteDatabase());
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- context = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() throws Exception {
+ context = InstrumentationRegistry.getTargetContext();
// create new database
PodDBAdapter.init(context);
@@ -49,7 +52,7 @@ public class DBTasksTest extends InstrumentationTestCase {
UserPreferences.init(context);
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testUpdateFeedNewFeed() {
final int NUM_ITEMS = 10;
@@ -69,6 +72,7 @@ public class DBTasksTest extends InstrumentationTestCase {
}
/** Two feeds with the same title, but different download URLs should be treated as different feeds. */
+ @Test
public void testUpdateFeedSameTitle() {
Feed feed1 = new Feed("url1", null, "title");
@@ -83,6 +87,7 @@ public class DBTasksTest extends InstrumentationTestCase {
assertTrue(savedFeed1.getId() != savedFeed2.getId());
}
+ @Test
public void testUpdateFeedUpdatedFeed() {
final int NUM_ITEMS_OLD = 10;
final int NUM_ITEMS_NEW = 10;
@@ -123,6 +128,7 @@ public class DBTasksTest extends InstrumentationTestCase {
updatedFeedTest(feedFromDB, feedID, itemIDs, NUM_ITEMS_OLD, NUM_ITEMS_NEW);
}
+ @Test
public void testUpdateFeedMediaUrlResetState() {
final Feed feed = new Feed("url", null, "title");
FeedItem item = new FeedItem(0, "item", "id", "link", new Date(), FeedItem.PLAYED, feed);
@@ -139,7 +145,9 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedMedia media = new FeedMedia(item, "url", 1024, "mime/type");
item.setMedia(media);
- feed.setItems(singletonList(item));
+ List<FeedItem> list = new ArrayList<>();
+ list.add(item);
+ feed.setItems(list);
final Feed newFeed = DBTasks.updateFeed(context, feed)[0];
assertTrue(feed != newFeed);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
index f58008172..09a8466e6 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -1,7 +1,5 @@
package de.test.antennapod.storage;
-import junit.framework.Assert;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -15,6 +13,8 @@ import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
+import static org.junit.Assert.assertTrue;
+
/**
* Utility methods for DB* tests.
*/
@@ -65,9 +65,9 @@ class DBTestUtils {
}
Collections.sort(f.getItems(), new FeedItemPubdateComparator());
adapter.setCompleteFeed(f);
- Assert.assertTrue(f.getId() != 0);
+ assertTrue(f.getId() != 0);
for (FeedItem item : f.getItems()) {
- Assert.assertTrue(item.getId() != 0);
+ assertTrue(item.getId() != 0);
}
feeds.add(f);
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
index 27d76116d..7663bb3c9 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -4,7 +4,9 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.util.Log;
import org.awaitility.Awaitility;
@@ -27,23 +29,32 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.Consumer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
/**
* Test class for DBWriter
*/
-public class DBWriterTest extends InstrumentationTestCase {
+@MediumTest
+public class DBWriterTest {
private static final String TAG = "DBWriterTest";
private static final String TEST_FOLDER = "testDBWriter";
private static final long TIMEOUT = 5L;
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
+ @After
+ public void tearDown() throws Exception {
assertTrue(PodDBAdapter.deleteDatabase());
- final Context context = getInstrumentation().getTargetContext();
+ final Context context = InstrumentationRegistry.getTargetContext();
File testDir = context.getExternalFilesDir(TEST_FOLDER);
assertNotNull(testDir);
for (File f : testDir.listFiles()) {
@@ -51,24 +62,23 @@ public class DBWriterTest extends InstrumentationTestCase {
}
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
// create new database
- PodDBAdapter.init(getInstrumentation().getTargetContext());
+ PodDBAdapter.init(InstrumentationRegistry.getTargetContext());
PodDBAdapter.deleteDatabase();
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
- Context context = getInstrumentation().getTargetContext();
+ Context context = InstrumentationRegistry.getTargetContext();
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
prefEdit.putBoolean(UserPreferences.PREF_DELETE_REMOVES_FROM_QUEUE, true).commit();
UserPreferences.init(context);
}
+ @Test
public void testSetFeedMediaPlaybackInformation()
throws IOException, ExecutionException, InterruptedException, TimeoutException {
final int POSITION = 50;
@@ -101,6 +111,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertEquals(DURATION, mediaFromDb.getDuration());
}
+ @Test
public void testDeleteFeedMediaOfItemFileExists()
throws IOException, ExecutionException, InterruptedException, TimeoutException {
File dest = new File(getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER), "testFile");
@@ -133,6 +144,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNull(media.getFile_url());
}
+ @Test
public void testDeleteFeedMediaOfItemRemoveFromQueue()
throws IOException, ExecutionException, InterruptedException, TimeoutException {
assertTrue(UserPreferences.shouldDeleteRemoveFromQueue());
@@ -174,6 +186,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(queue.size() == 0);
}
+ @Test
public void testDeleteFeed() throws ExecutionException, InterruptedException, IOException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@@ -229,6 +242,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testDeleteFeedNoItems() throws IOException, ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@@ -254,6 +268,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testDeleteFeedNoFeedMedia() throws IOException, ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@@ -296,6 +311,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testDeleteFeedWithQueueItems() throws ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@@ -360,6 +376,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testDeleteFeedNoDownloadedFiles() throws ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@@ -427,6 +444,7 @@ public class DBWriterTest extends InstrumentationTestCase {
return media;
}
+ @Test
public void testAddItemToPlaybackHistoryNotPlayedYet()
throws ExecutionException, InterruptedException, TimeoutException {
FeedMedia media = playbackHistorySetup(null);
@@ -440,6 +458,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(media.getPlaybackCompletionDate());
}
+ @Test
public void testAddItemToPlaybackHistoryAlreadyPlayed()
throws ExecutionException, InterruptedException, TimeoutException {
final long OLD_DATE = 0;
@@ -483,6 +502,7 @@ public class DBWriterTest extends InstrumentationTestCase {
return feed;
}
+ @Test
public void testAddQueueItemSingleItem() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", null, "title");
@@ -507,6 +527,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testAddQueueItemSingleItemAlreadyInQueue() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", null, "title");
@@ -541,6 +562,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testAddQueueItemMultipleItems() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
@@ -559,6 +581,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testClearQueue() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
@@ -572,6 +595,7 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
+ @Test
public void testRemoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
final Context context = getInstrumentation().getTargetContext();
@@ -604,6 +628,7 @@ public class DBWriterTest extends InstrumentationTestCase {
}
}
+ @Test
public void testRemoveQueueItemMultipleItems() throws InterruptedException, ExecutionException, TimeoutException {
// Setup test data
//
@@ -641,6 +666,7 @@ public class DBWriterTest extends InstrumentationTestCase {
}
+ @Test
public void testMoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title");
@@ -687,6 +713,7 @@ public class DBWriterTest extends InstrumentationTestCase {
}
}
+ @Test
public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title");
@@ -713,6 +740,7 @@ public class DBWriterTest extends InstrumentationTestCase {
}
}
+ @Test
public void testMarkAllItemsReadSameFeed() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title");
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 8e0064079..d013ab9cf 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -1,28 +1,22 @@
package de.test.antennapod.ui;
+import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
-import android.support.test.espresso.contrib.DrawerActions;
+import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.intent.Intents;
-import android.support.test.filters.FlakyTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Gravity;
-import android.widget.ListView;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
-import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.dialog.RatingDialog;
-import de.danoeh.antennapod.fragment.DownloadsFragment;
-import de.danoeh.antennapod.fragment.EpisodesFragment;
-import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
-import de.danoeh.antennapod.fragment.QueueFragment;
+import de.test.antennapod.EspressoTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -30,23 +24,18 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.action.ViewActions.longClick;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.contrib.DrawerMatchers.isClosed;
-import static android.support.test.espresso.intent.Intents.intended;
-import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static android.support.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static de.test.antennapod.NthMatcher.first;
+import static de.test.antennapod.EspressoTestUtils.clickPreference;
+import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
/**
* User interface tests for MainActivity
@@ -59,10 +48,19 @@ public class MainActivityTest {
private SharedPreferences prefs;
@Rule
- public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
+ public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() throws IOException {
+ // override first launch preference
+ // do this BEFORE calling getActivity()!
+ EspressoTestUtils.clearAppData();
+ prefs = InstrumentationRegistry.getContext()
+ .getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
+ prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
+
+ mActivityRule.launchActivity(new Intent());
+
Intents.init();
Context context = mActivityRule.getActivity();
uiTestUtils = new UITestUtils(context);
@@ -75,11 +73,6 @@ public class MainActivityTest {
adapter.open();
adapter.close();
- // override first launch preference
- // do this BEFORE calling getActivity()!
- prefs = context.getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
- prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
-
RatingDialog.init(context);
RatingDialog.saveRated();
@@ -95,19 +88,13 @@ public class MainActivityTest {
prefs.edit().clear().commit();
}
- private void openNavDrawer() {
- onView(withId(R.id.drawer_layout))
- .check(matches(isClosed(Gravity.LEFT)))
- .perform(DrawerActions.open());
- }
-
@Test
public void testAddFeed() throws Exception {
uiTestUtils.addHostedFeedData();
final Feed feed = uiTestUtils.hostedFeeds.get(0);
openNavDrawer();
solo.clickOnText(solo.getString(R.string.add_feed_label));
- solo.enterText(0, feed.getDownload_url());
+ solo.enterText(1, feed.getDownload_url());
solo.clickOnButton(solo.getString(R.string.confirm_label));
solo.waitForActivity(OnlineFeedViewActivity.class);
solo.waitForView(R.id.butSubscribe);
@@ -116,154 +103,23 @@ public class MainActivityTest {
assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
}
-
- @Test
- @FlakyTest
- public void testClickNavDrawer() throws Exception {
- uiTestUtils.addLocalFeedData(false);
-
- UserPreferences.setHiddenDrawerItems(new ArrayList<>());
-
- // queue
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.queue_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.queue_label), getActionbarTitle());
-
- // episodes
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.episodes_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.episodes_label), getActionbarTitle());
-
- // Subscriptions
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.subscriptions_label));
- solo.waitForView(R.id.subscriptions_grid);
- assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
-
- // downloads
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.downloads_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.downloads_label), getActionbarTitle());
-
- // playback history
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.playback_history_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.playback_history_label), getActionbarTitle());
-
- // add podcast
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.add_feed_label));
- solo.waitForView(R.id.txtvFeedurl);
- assertEquals(solo.getString(R.string.add_feed_label), getActionbarTitle());
-
- // podcasts
- ListView list = (ListView) solo.getView(R.id.nav_list);
- for (int i = 0; i < uiTestUtils.hostedFeeds.size(); i++) {
- Feed f = uiTestUtils.hostedFeeds.get(i);
- openNavDrawer();
- solo.scrollListToLine(list, i);
- solo.clickOnText(f.getTitle());
- solo.waitForView(android.R.id.list);
- assertEquals("", getActionbarTitle());
- }
- }
-
private String getActionbarTitle() {
return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
}
-
@Test
- @FlakyTest
- public void testGoToPreferences() {
+ public void testBackButtonBehaviorGoToPage() {
openNavDrawer();
onView(withText(R.string.settings_label)).perform(click());
- intended(hasComponent(PreferenceActivity.class.getName()));
- }
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_back_button_behavior_title);
- @Test
- public void testDrawerPreferencesHideSomeElements() {
- UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- openNavDrawer();
- onView(first(withText(R.string.queue_label))).perform(longClick());
- onView(withText(R.string.episodes_label)).perform(click());
- onView(withText(R.string.playback_history_label)).perform(click());
- onView(withText(R.string.confirm_label)).perform(click());
-
- List<String> hidden = UserPreferences.getHiddenDrawerItems();
- assertEquals(2, hidden.size());
- assertTrue(hidden.contains(EpisodesFragment.TAG));
- assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
- }
-
- @Test
- public void testDrawerPreferencesUnhideSomeElements() {
- List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
- UserPreferences.setHiddenDrawerItems(hidden);
- openNavDrawer();
- onView(first(withText(R.string.queue_label))).perform(longClick());
-
- onView(withText(R.string.downloads_label)).perform(click());
- onView(withText(R.string.queue_label)).perform(click());
- onView(withText(R.string.confirm_label)).perform(click());
-
- hidden = UserPreferences.getHiddenDrawerItems();
- assertEquals(2, hidden.size());
- assertTrue(hidden.contains(QueueFragment.TAG));
- assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
- }
-
-
- @Test
- public void testDrawerPreferencesHideAllElements() {
- UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
-
- openNavDrawer();
- onView(first(withText(R.string.queue_label))).perform(longClick());
- for (String title : titles) {
- onView(first(withText(title))).perform(click());
- }
+ onView(withText(R.string.back_button_go_to_page)).perform(click());
+ onView(withText(R.string.subscriptions_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- List<String> hidden = UserPreferences.getHiddenDrawerItems();
- assertEquals(titles.length, hidden.size());
- for (String tag : MainActivity.NAV_DRAWER_TAGS) {
- assertTrue(hidden.contains(tag));
- }
- }
-
- @Test
- public void testDrawerPreferencesHideCurrentElement() {
- UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- openNavDrawer();
- onView(withText(R.string.downloads_label)).perform(click());
- openNavDrawer();
-
- onView(first(withText(R.string.queue_label))).perform(longClick());
- onView(first(withText(R.string.downloads_label))).perform(click());
- onView(withText(R.string.confirm_label)).perform(click());
-
- List<String> hidden = UserPreferences.getHiddenDrawerItems();
- assertEquals(1, hidden.size());
- assertTrue(hidden.contains(DownloadsFragment.TAG));
- }
-
- @Test
- public void testBackButtonBehaviorGoToPage() {
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.settings_label));
- solo.clickOnText(solo.getString(R.string.user_interface_label));
- solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
- solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.subscriptions_label));
- solo.clickOnText(solo.getString(R.string.confirm_label));
solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack(); // Close nav drawer
solo.goBack();
assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
}
@@ -271,11 +127,12 @@ public class MainActivityTest {
@Test
public void testBackButtonBehaviorOpenDrawer() {
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.settings_label));
- solo.clickOnText(solo.getString(R.string.user_interface_label));
- solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
- solo.clickOnText(solo.getString(R.string.back_button_open_drawer));
+ onView(withText(R.string.settings_label)).perform(click());
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_open_drawer)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack(); // Close nav drawer
solo.goBack();
assertTrue(((MainActivity)solo.getCurrentActivity()).isDrawerOpen());
}
@@ -283,39 +140,42 @@ public class MainActivityTest {
@Test
public void testBackButtonBehaviorDoubleTap() {
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.settings_label));
- solo.clickOnText(solo.getString(R.string.user_interface_label));
- solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
- solo.clickOnText(solo.getString(R.string.back_button_double_tap));
+ onView(withText(R.string.settings_label)).perform(click());
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_double_tap)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack(); // Close nav drawer
solo.goBack();
solo.goBack();
- assertTrue(solo.getCurrentActivity().isFinishing());
+ assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
}
@Test
- public void testBackButtonBehaviorPrompt() {
+ public void testBackButtonBehaviorPrompt() throws Exception {
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.settings_label));
- solo.clickOnText(solo.getString(R.string.user_interface_label));
- solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
- solo.clickOnText(solo.getString(R.string.back_button_show_prompt));
+ onView(withText(R.string.settings_label)).perform(click());
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_show_prompt)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack(); // Close nav drawer
solo.goBack();
- solo.clickOnText(solo.getString(R.string.yes));
- solo.waitForDialogToClose();
- assertTrue(solo.getCurrentActivity().isFinishing());
+ onView(withText(R.string.yes)).perform(click());
+ Thread.sleep(100);
+ assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
}
@Test
public void testBackButtonBehaviorDefault() {
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.settings_label));
- solo.clickOnText(solo.getString(R.string.user_interface_label));
- solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
- solo.clickOnText(solo.getString(R.string.back_button_default));
+ onView(withText(R.string.settings_label)).perform(click());
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_default)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack(); // Close nav drawer
solo.goBack();
- assertTrue(solo.getCurrentActivity().isFinishing());
+ assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
new file mode 100644
index 000000000..0ed62010b
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
@@ -0,0 +1,253 @@
+package de.test.antennapod.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.contrib.DrawerActions;
+import android.support.test.espresso.intent.Intents;
+import android.support.test.filters.FlakyTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.ListView;
+import com.robotium.solo.Solo;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.dialog.RatingDialog;
+import de.danoeh.antennapod.fragment.DownloadsFragment;
+import de.danoeh.antennapod.fragment.EpisodesFragment;
+import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
+import de.danoeh.antennapod.fragment.QueueFragment;
+import de.test.antennapod.EspressoTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.longClick;
+import static android.support.test.espresso.intent.Intents.intended;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.NthMatcher.first;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * User interface tests for MainActivity drawer
+ */
+@RunWith(AndroidJUnit4.class)
+public class NavigationDrawerTest {
+
+ private Solo solo;
+ private UITestUtils uiTestUtils;
+ private SharedPreferences prefs;
+
+ @Rule
+ public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
+
+ @Before
+ public void setUp() throws IOException {
+ // override first launch preference
+ // do this BEFORE calling getActivity()!
+ EspressoTestUtils.clearAppData();
+ prefs = InstrumentationRegistry.getContext()
+ .getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
+ prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
+
+ mActivityRule.launchActivity(new Intent());
+
+ Intents.init();
+ Context context = mActivityRule.getActivity();
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+
+ // create new database
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+
+ RatingDialog.init(context);
+ RatingDialog.saveRated();
+
+ solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uiTestUtils.tearDown();
+ solo.finishOpenedActivities();
+ Intents.release();
+ PodDBAdapter.deleteDatabase();
+ prefs.edit().clear().commit();
+ }
+
+ private void openNavDrawer() {
+ onView(isRoot()).perform(waitForView(withId(R.id.drawer_layout), 1000));
+ onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
+ }
+
+ @Test
+ @FlakyTest
+ public void testClickNavDrawer() throws Exception {
+ uiTestUtils.addLocalFeedData(false);
+
+ setHiddenDrawerItems(new ArrayList<>());
+
+ // queue
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.queue_label));
+ solo.waitForView(R.id.recyclerView);
+ assertEquals(solo.getString(R.string.queue_label), getActionbarTitle());
+
+ // episodes
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.episodes_label));
+ solo.waitForView(android.R.id.list);
+ assertEquals(solo.getString(R.string.episodes_label), getActionbarTitle());
+
+ // Subscriptions
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.subscriptions_label));
+ solo.waitForView(R.id.subscriptions_grid);
+ assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
+
+ // downloads
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.downloads_label));
+ solo.waitForView(android.R.id.list);
+ assertEquals(solo.getString(R.string.downloads_label), getActionbarTitle());
+
+ // playback history
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.playback_history_label));
+ solo.waitForView(android.R.id.list);
+ assertEquals(solo.getString(R.string.playback_history_label), getActionbarTitle());
+
+ // add podcast
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.add_feed_label));
+ solo.waitForView(R.id.txtvFeedurl);
+ assertEquals(solo.getString(R.string.add_feed_label), getActionbarTitle());
+
+ // podcasts
+ ListView list = (ListView) solo.getView(R.id.nav_list);
+ for (int i = 0; i < uiTestUtils.hostedFeeds.size(); i++) {
+ Feed f = uiTestUtils.hostedFeeds.get(i);
+ openNavDrawer();
+ solo.scrollListToLine(list, i);
+ solo.clickOnText(f.getTitle());
+ solo.waitForView(android.R.id.list);
+ assertEquals("", getActionbarTitle());
+ }
+ }
+
+ private String getActionbarTitle() {
+ return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
+ }
+
+
+ @Test
+ @FlakyTest
+ public void testGoToPreferences() {
+ openNavDrawer();
+ onView(withText(R.string.settings_label)).perform(click());
+ intended(hasComponent(PreferenceActivity.class.getName()));
+ }
+
+ @Test
+ public void testDrawerPreferencesHideSomeElements() {
+ setHiddenDrawerItems(new ArrayList<>());
+ openNavDrawer();
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+ onView(withText(R.string.episodes_label)).perform(click());
+ onView(withText(R.string.playback_history_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
+
+ List<String> hidden = UserPreferences.getHiddenDrawerItems();
+ assertEquals(2, hidden.size());
+ assertTrue(hidden.contains(EpisodesFragment.TAG));
+ assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
+ }
+
+ @Test
+ public void testDrawerPreferencesUnhideSomeElements() {
+ List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
+ setHiddenDrawerItems(hidden);
+ openNavDrawer();
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+
+ onView(withText(R.string.downloads_label)).perform(click());
+ onView(withText(R.string.queue_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
+
+ hidden = UserPreferences.getHiddenDrawerItems();
+ assertEquals(2, hidden.size());
+ assertTrue(hidden.contains(QueueFragment.TAG));
+ assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
+ }
+
+
+ @Test
+ public void testDrawerPreferencesHideAllElements() {
+ setHiddenDrawerItems(new ArrayList<>());
+ String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
+
+ openNavDrawer();
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+ for (String title : titles) {
+ onView(first(withText(title))).perform(click());
+ }
+ onView(withText(R.string.confirm_label)).perform(click());
+
+ List<String> hidden = UserPreferences.getHiddenDrawerItems();
+ assertEquals(titles.length, hidden.size());
+ for (String tag : MainActivity.NAV_DRAWER_TAGS) {
+ assertTrue(hidden.contains(tag));
+ }
+ }
+
+ @Test
+ public void testDrawerPreferencesHideCurrentElement() {
+ setHiddenDrawerItems(new ArrayList<>());
+ openNavDrawer();
+ onView(withText(R.string.downloads_label)).perform(click());
+ openNavDrawer();
+
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+ onView(first(withText(R.string.downloads_label))).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
+
+ List<String> hidden = UserPreferences.getHiddenDrawerItems();
+ assertEquals(1, hidden.size());
+ assertTrue(hidden.contains(DownloadsFragment.TAG));
+ }
+
+ private void setHiddenDrawerItems(List<String> items) {
+ UserPreferences.setHiddenDrawerItems(items);
+ try {
+ mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().updateNavDrawer());
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ fail();
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
index 55ed998bb..6b41ad0ea 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
@@ -1,13 +1,12 @@
package de.test.antennapod.ui;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.os.Build;
import android.preference.PreferenceManager;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
import android.view.View;
import android.widget.ListView;
@@ -25,14 +24,21 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.test.antennapod.EspressoTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
*/
-@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
-public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActivity> {
-
- private static final String TAG = PlaybackTest.class.getSimpleName();
+@LargeTest
+public class PlaybackSonicTest {
private static final int EPISODES_DRAWER_LIST_INDEX = 1;
private static final int QUEUE_DRAWER_LIST_INDEX = 0;
@@ -41,15 +47,13 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
private Context context;
- public PlaybackSonicTest() {
- super(MainActivity.class);
- }
+ @Rule
+ public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
-
- context = getInstrumentation().getTargetContext();
+ EspressoTestUtils.clearAppData();
+ context = InstrumentationRegistry.getTargetContext();
PodDBAdapter.init(context);
PodDBAdapter.deleteDatabase();
@@ -62,7 +66,8 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
.putString(UserPreferences.PREF_MEDIA_PLAYER, "sonic")
.commit();
- solo = new Solo(getInstrumentation(), getActivity());
+ activityTestRule.launchActivity(new Intent());
+ solo = new Solo(getInstrumentation(), activityTestRule.getActivity());
uiTestUtils = new UITestUtils(context);
uiTestUtils.setup();
@@ -73,7 +78,7 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
adapter.close();
}
- @Override
+ @After
public void tearDown() throws Exception {
solo.finishOpenedActivities();
uiTestUtils.tearDown();
@@ -81,8 +86,10 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
// shut down playback service
skipEpisode();
context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ }
- super.tearDown();
+ private MainActivity getActivity() {
+ return activityTestRule.getActivity();
}
private void openNavDrawer() {
@@ -158,12 +165,14 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
assertTrue(playing);
}
+ @Test
public void testStartLocal() throws Exception {
uiTestUtils.addLocalFeedData(true);
DBWriter.clearQueue().get();
startLocalPlayback();
}
+ @Test
public void testContinousPlaybackOffSingleEpisode() throws Exception {
setContinuousPlaybackPreference(false);
uiTestUtils.addLocalFeedData(true);
@@ -171,7 +180,7 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
startLocalPlayback();
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
setContinuousPlaybackPreference(false);
uiTestUtils.addLocalFeedData(true);
@@ -196,7 +205,7 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
assertFalse(status.equals(PlayerStatus.PLAYING));
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
setContinuousPlaybackPreference(true);
uiTestUtils.addLocalFeedData(true);
@@ -262,13 +271,14 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
assertTrue(startedReplay);
}
+ @Test
public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
replayEpisodeCheck(true);
}
+ @Test
public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
replayEpisodeCheck(false);
}
-
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
index 74d59abd7..099549a69 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
@@ -4,8 +4,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
import android.view.View;
import android.widget.ListView;
@@ -23,30 +24,32 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
*/
-public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity> {
-
- private static final String TAG = PlaybackTest.class.getSimpleName();
+@LargeTest
+public class PlaybackTest {
private static final int EPISODES_DRAWER_LIST_INDEX = 1;
private static final int QUEUE_DRAWER_LIST_INDEX = 0;
+ @Rule
+ public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);
+
private Solo solo;
private UITestUtils uiTestUtils;
-
private Context context;
- public PlaybackTest() {
- super(MainActivity.class);
- }
-
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
-
- context = getInstrumentation().getTargetContext();
+ context = InstrumentationRegistry.getTargetContext();
PodDBAdapter.init(context);
PodDBAdapter.deleteDatabase();
@@ -58,7 +61,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
.putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
.commit();
- solo = new Solo(getInstrumentation(), getActivity());
+ solo = new Solo(InstrumentationRegistry.getInstrumentation(), getActivity());
uiTestUtils = new UITestUtils(context);
uiTestUtils.setup();
@@ -69,7 +72,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
adapter.close();
}
- @Override
+ @After
public void tearDown() throws Exception {
solo.finishOpenedActivities();
uiTestUtils.tearDown();
@@ -77,12 +80,15 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
// shut down playback service
skipEpisode();
context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ }
- super.tearDown();
+ private MainActivity getActivity() {
+ return activityTestRule.getActivity();
}
+
private void openNavDrawer() {
solo.clickOnImageButton(0);
- getInstrumentation().waitForIdleSync();
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
private void setContinuousPlaybackPreference(boolean value) {
@@ -150,12 +156,14 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
assertTrue(playing);
}
+ @Test
public void testStartLocal() throws Exception {
uiTestUtils.addLocalFeedData(true);
DBWriter.clearQueue().get();
startLocalPlayback();
}
+ @Test
public void testContinousPlaybackOffSingleEpisode() throws Exception {
setContinuousPlaybackPreference(false);
uiTestUtils.addLocalFeedData(true);
@@ -163,7 +171,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
startLocalPlayback();
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
setContinuousPlaybackPreference(false);
uiTestUtils.addLocalFeedData(true);
@@ -187,7 +195,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
assertFalse(status.equals(PlayerStatus.PLAYING));
}
- @FlakyTest(tolerance = 3)
+ @Test
public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
setContinuousPlaybackPreference(true);
uiTestUtils.addLocalFeedData(true);
@@ -252,13 +260,13 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
assertTrue(startedReplay);
}
+ @Test
public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
replayEpisodeCheck(true);
}
+ @Test
public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
replayEpisodeCheck(false);
}
-
-
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index d934bf3e2..4b2dc75a9 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -1,22 +1,19 @@
package de.test.antennapod.ui;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
-import android.support.test.espresso.contrib.RecyclerViewActions;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
-import org.hamcrest.Matcher;
-import org.junit.After;
+import de.test.antennapod.EspressoTestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@@ -35,38 +32,33 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
-@RunWith(AndroidJUnit4.class)
+@LargeTest
public class PreferencesTest {
private Solo solo;
private Resources res;
- private SharedPreferences prefs;
@Rule
- public ActivityTestRule<PreferenceActivity> mActivityRule = new ActivityTestRule<>(PreferenceActivity.class);
+ public ActivityTestRule<PreferenceActivity> mActivityRule = new ActivityTestRule<>(PreferenceActivity.class, false, false);
@Before
public void setUp() {
+ EspressoTestUtils.clearAppData();
+ mActivityRule.launchActivity(new Intent());
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
+ prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
+
solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
Timeout.setSmallTimeout(500);
Timeout.setLargeTimeout(1000);
res = mActivityRule.getActivity().getResources();
UserPreferences.init(mActivityRule.getActivity());
-
- prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
- prefs.edit().clear();
- prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
- }
-
- @After
- public void tearDown() {
- solo.finishOpenedActivities();
- prefs.edit().clear();
}
@Test
@@ -78,8 +70,8 @@ public class PreferencesTest {
} else {
otherTheme = R.string.pref_theme_title_light;
}
- clickPreference(withText(R.string.user_interface_label));
- clickPreference(withText(R.string.pref_set_theme_title));
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
@@ -93,8 +85,8 @@ public class PreferencesTest {
} else {
otherTheme = R.string.pref_theme_title_light;
}
- clickPreference(withText(R.string.user_interface_label));
- clickPreference(withText(R.string.pref_set_theme_title));
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
@@ -102,34 +94,31 @@ public class PreferencesTest {
@Test
public void testEnablePersistentPlaybackControls() {
final boolean persistNotify = UserPreferences.isPersistNotify();
- clickPreference(withText(R.string.user_interface_label));
- clickPreference(withText(R.string.pref_persistNotify_title));
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_persistNotify_title);
assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
- clickPreference(withText(R.string.pref_persistNotify_title));
+ clickPreference(R.string.pref_persistNotify_title);
assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
}
@Test
public void testSetLockscreenButtons() {
- solo.clickOnText(solo.getString(R.string.user_interface_label));
+ onView(withText(R.string.user_interface_label)).perform(click());
solo.scrollDown();
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
- solo.clickOnText(solo.getString(R.string.pref_compact_notification_buttons_title));
+ onView(withText(R.string.pref_compact_notification_buttons_title)).perform(click());
solo.waitForDialogToOpen(1000);
- // First uncheck every checkbox
- for (String button : buttons) {
- assertTrue(solo.searchText(button));
- if (solo.isTextChecked(button)) {
- solo.clickOnText(button);
- }
- }
+ // First uncheck checkbox
+ onView(withText(buttons[2])).perform(click());
+
// Now try to check all checkboxes
- solo.clickOnText(buttons[0]);
- solo.clickOnText(buttons[1]);
- solo.clickOnText(buttons[2]);
+ onView(withText(buttons[0])).perform(click());
+ onView(withText(buttons[1])).perform(click());
+ onView(withText(buttons[2])).perform(click());
+
// Make sure that the third checkbox is unchecked
assertTrue(!solo.isTextChecked(buttons[2]));
- solo.clickOnText(solo.getString(R.string.confirm_label));
+ onView(withText(R.string.confirm_label)).perform(click());
solo.waitForDialogToClose(1000);
assertTrue(solo.waitForCondition(UserPreferences::showRewindOnCompactNotification, Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(UserPreferences::showFastForwardOnCompactNotification, Timeout.getLargeTimeout()));
@@ -138,107 +127,109 @@ public class PreferencesTest {
@Test
public void testEnqueueAtFront() {
- solo.clickOnText(solo.getString(R.string.playback_pref));
+ onView(withText(R.string.playback_pref)).perform(click());
final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
solo.scrollDown();
solo.scrollDown();
- solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
+ onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
+ onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
}
@Test
public void testHeadPhonesDisconnect() {
- solo.clickOnText(solo.getString(R.string.playback_pref));
+ onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
- solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
+ onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
+ onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
}
@Test
public void testHeadPhonesReconnect() {
- solo.clickOnText(solo.getString(R.string.playback_pref));
+ onView(withText(R.string.playback_pref)).perform(click());
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
- solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
+ onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
}
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
- solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title));
+ onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title));
+ onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
}
@Test
public void testBluetoothReconnect() {
- solo.clickOnText(solo.getString(R.string.playback_pref));
+ onView(withText(R.string.playback_pref)).perform(click());
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
- solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
+ onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
}
final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
- solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title));
+ onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title));
+ onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
}
@Test
public void testContinuousPlayback() {
- solo.clickOnText(solo.getString(R.string.playback_pref));
+ onView(withText(R.string.playback_pref)).perform(click());
final boolean continuousPlayback = UserPreferences.isFollowQueue();
solo.scrollDown();
solo.scrollDown();
- solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
+ onView(withText(R.string.pref_followQueue_title)).perform(click());
assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
+ onView(withText(R.string.pref_followQueue_title)).perform(click());
assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
}
@Test
public void testAutoDelete() {
- solo.clickOnText(solo.getString(R.string.storage_pref));
+ onView(withText(R.string.storage_pref)).perform(click());
final boolean autoDelete = UserPreferences.isAutoDelete();
- solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
+ onView(withText(R.string.pref_auto_delete_title)).perform(click());
assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
+ onView(withText(R.string.pref_auto_delete_title)).perform(click());
assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
}
@Test
public void testPlaybackSpeeds() {
- clickPreference(withText(R.string.playback_pref));
- clickPreference(withText(R.string.pref_playback_speed_title));
- assertTrue(solo.searchText(res.getStringArray(R.array.playback_speed_values)[0]));
+ clickPreference(R.string.playback_pref);
+ clickPreference(R.string.media_player);
+ onView(withText(R.string.media_player_exoplayer)).perform(click());
+ clickPreference(R.string.pref_playback_speed_title);
+ solo.waitForDialogToOpen();
+ onView(withText("0.50")).check(matches(isDisplayed()));
onView(withText(R.string.cancel_label)).perform(click());
}
@Test
public void testPauseForInterruptions() {
- solo.clickOnText(solo.getString(R.string.playback_pref));
+ onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
- solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
+ onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
+ onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
}
@Test
public void testDisableUpdateInterval() {
- solo.clickOnText(solo.getString(R.string.network_pref));
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable));
+ onView(withText(R.string.network_pref)).perform(click());
+ onView(withText(R.string.pref_autoUpdateIntervallOrTime_title)).perform(click());
+ onView(withText(R.string.pref_autoUpdateIntervallOrTime_Disable)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
}
@Test
public void testSetUpdateInterval() {
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_autoUpdateIntervallOrTime_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_autoUpdateIntervallOrTime_title);
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Interval)).perform(click());
String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
onView(withText(search)).perform(click());
@@ -247,41 +238,31 @@ public class PreferencesTest {
}
@Test
- public void testMobileUpdates() {
- clickPreference(withText(R.string.network_pref));
- final boolean mobileUpdates = UserPreferences.isAllowMobileUpdate();
- clickPreference(withText(R.string.pref_mobileUpdate_title));
- assertTrue(solo.waitForCondition(() -> mobileUpdates != UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
- clickPreference(withText(R.string.pref_mobileUpdate_title));
- assertTrue(solo.waitForCondition(() -> mobileUpdates == UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
- }
-
- @Test
public void testSetSequentialDownload() {
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_parallel_downloads_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_parallel_downloads_title);
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "1");
- solo.clickOnText(solo.getString(android.R.string.ok));
+ onView(withText(android.R.string.ok)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
}
@Test
public void testSetParallelDownloads() {
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_parallel_downloads_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_parallel_downloads_title);
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "10");
- solo.clickOnText(solo.getString(android.R.string.ok));
+ onView(withText(android.R.string.ok)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
}
@Test
public void testSetParallelDownloadsInvalidInput() {
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_parallel_downloads_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_parallel_downloads_title);
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "0");
@@ -297,9 +278,9 @@ public class PreferencesTest {
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String entry = entries[entries.length/2];
final int value = Integer.valueOf(values[values.length/2]);
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_automatic_download_title));
- clickPreference(withText(R.string.pref_episode_cache_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_automatic_download_title);
+ clickPreference(R.string.pref_episode_cache_title);
solo.waitForDialogToOpen();
solo.clickOnText(entry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
@@ -312,12 +293,11 @@ public class PreferencesTest {
String minEntry = entries[0];
final int minValue = Integer.valueOf(values[0]);
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_automatic_download_title));
- clickPreference(withText(R.string.pref_episode_cache_title));
- solo.waitForDialogToOpen(1000);
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_automatic_download_title);
+ clickPreference(R.string.pref_episode_cache_title);
solo.scrollUp();
- solo.clickOnText(minEntry);
+ onView(withText(minEntry)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
}
@@ -327,46 +307,44 @@ public class PreferencesTest {
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String maxEntry = entries[entries.length-1];
final int maxValue = Integer.valueOf(values[values.length-1]);
- solo.clickOnText(solo.getString(R.string.network_pref));
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
- solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
- solo.waitForDialogToOpen();
- solo.clickOnText(maxEntry);
+ onView(withText(R.string.network_pref)).perform(click());
+ onView(withText(R.string.pref_automatic_download_title)).perform(click());
+ onView(withText(R.string.pref_episode_cache_title)).perform(click());
+ onView(withText(maxEntry)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
}
@Test
public void testAutomaticDownload() {
final boolean automaticDownload = UserPreferences.isEnableAutodownload();
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_automatic_download_title));
- clickPreference(withText(R.string.pref_automatic_download_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_automatic_download_title);
+ clickPreference(R.string.pref_automatic_download_title);
assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
if(UserPreferences.isEnableAutodownload() == false) {
- clickPreference(withText(R.string.pref_automatic_download_title));
+ clickPreference(R.string.pref_automatic_download_title);
}
assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_on_battery_title));
+ onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_on_battery_title));
+ onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
final boolean enableWifiFilter = UserPreferences.isEnableAutodownloadWifiFilter();
- solo.clickOnText(solo.getString(R.string.pref_autodl_wifi_filter_title));
+ onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
assertTrue(solo.waitForCondition(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_autodl_wifi_filter_title));
+ onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
}
@Test
public void testEpisodeCleanupQueueOnly() {
- solo.clickOnText(solo.getString(R.string.network_pref));
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ onView(withText(R.string.network_pref)).perform(click());
+ onView(withText(R.string.pref_automatic_download_title)).perform(click());
+ onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
- solo.clickOnText(solo.getString(R.string.episode_cleanup_queue_removal));
+ onView(withText(R.string.episode_cleanup_queue_removal)).perform(click());
assertTrue(solo.waitForCondition(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
return alg instanceof APQueueCleanupAlgorithm;
@@ -376,11 +354,11 @@ public class PreferencesTest {
@Test
public void testEpisodeCleanupNeverAlg() {
- solo.clickOnText(solo.getString(R.string.network_pref));
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ onView(withText(R.string.network_pref)).perform(click());
+ onView(withText(R.string.pref_automatic_download_title)).perform(click());
+ onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
solo.waitForText(solo.getString(R.string.episode_cleanup_never));
- solo.clickOnText(solo.getString(R.string.episode_cleanup_never));
+ onView(withText(R.string.episode_cleanup_never)).perform(click());
assertTrue(solo.waitForCondition(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
return alg instanceof APNullCleanupAlgorithm;
@@ -390,11 +368,11 @@ public class PreferencesTest {
@Test
public void testEpisodeCleanupClassic() {
- solo.clickOnText(solo.getString(R.string.network_pref));
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ onView(withText(R.string.network_pref)).perform(click());
+ onView(withText(R.string.pref_automatic_download_title)).perform(click());
+ onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
- solo.clickOnText(solo.getString(R.string.episode_cleanup_after_listening));
+ onView(withText(R.string.episode_cleanup_after_listening)).perform(click());
assertTrue(solo.waitForCondition(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
@@ -408,9 +386,9 @@ public class PreferencesTest {
@Test
public void testEpisodeCleanupNumDays() {
- clickPreference(withText(R.string.network_pref));
- clickPreference(withText(R.string.pref_automatic_download_title));
- clickPreference(withText(R.string.pref_episode_cleanup_title));
+ clickPreference(R.string.network_pref);
+ clickPreference(R.string.pref_automatic_download_title);
+ clickPreference(R.string.pref_episode_cleanup_title);
solo.waitForDialogToOpen();
String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 5, 5);
onView(withText(search)).perform(click());
@@ -430,9 +408,8 @@ public class PreferencesTest {
int seconds = UserPreferences.getRewindSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values);
- clickPreference(withText(R.string.playback_pref));
- clickPreference(withText(R.string.pref_rewind));
- solo.waitForDialogToOpen();
+ clickPreference(R.string.playback_pref);
+ clickPreference(R.string.pref_rewind);
int currentIndex = Arrays.binarySearch(deltas, seconds);
assertTrue(currentIndex >= 0 && currentIndex < deltas.length); // found?
@@ -442,20 +419,18 @@ public class PreferencesTest {
onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
@Test
public void testFastForwardChange() {
- clickPreference(withText(R.string.playback_pref));
+ clickPreference(R.string.playback_pref);
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values);
- clickPreference(withText(R.string.pref_fast_forward));
- solo.waitForDialogToOpen();
+ clickPreference(R.string.pref_fast_forward);
int currentIndex = Arrays.binarySearch(deltas, seconds);
assertTrue(currentIndex >= 0 && currentIndex < deltas.length); // found?
@@ -463,7 +438,7 @@ public class PreferencesTest {
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
- onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
+ onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
solo.waitForDialogToClose();
@@ -474,33 +449,27 @@ public class PreferencesTest {
@Test
public void testBackButtonBehaviorGoToPageSelector() {
- clickPreference(withText(R.string.user_interface_label));
- clickPreference(withText(R.string.pref_back_button_behavior_title));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.queue_label));
- solo.clickOnText(solo.getString(R.string.confirm_label));
+ clickPreference(R.string.user_interface_label);
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_go_to_page)).perform(click());
+ onView(withText(R.string.queue_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG),
Timeout.getLargeTimeout()));
- clickPreference(withText(R.string.pref_back_button_behavior_title));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.episodes_label));
- solo.clickOnText(solo.getString(R.string.confirm_label));
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_go_to_page)).perform(click());
+ onView(withText(R.string.episodes_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG),
Timeout.getLargeTimeout()));
- clickPreference(withText(R.string.pref_back_button_behavior_title));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.subscriptions_label));
- solo.clickOnText(solo.getString(R.string.confirm_label));
+ clickPreference(R.string.pref_back_button_behavior_title);
+ onView(withText(R.string.back_button_go_to_page)).perform(click());
+ onView(withText(R.string.subscriptions_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG),
@@ -509,20 +478,15 @@ public class PreferencesTest {
@Test
public void testDeleteRemovesFromQueue() {
- clickPreference(withText(R.string.storage_pref));
+ clickPreference(R.string.storage_pref);
if (!UserPreferences.shouldDeleteRemoveFromQueue()) {
- clickPreference(withText(R.string.pref_delete_removes_from_queue_title));
+ clickPreference(R.string.pref_delete_removes_from_queue_title);
assertTrue(solo.waitForCondition(UserPreferences::shouldDeleteRemoveFromQueue, Timeout.getLargeTimeout()));
}
final boolean deleteRemovesFromQueue = UserPreferences.shouldDeleteRemoveFromQueue();
- solo.clickOnText(solo.getString(R.string.pref_delete_removes_from_queue_title));
+ onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_delete_removes_from_queue_title));
+ onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
}
-
- private void clickPreference(Matcher<View> matcher) {
- onView(withId(R.id.list))
- .perform(RecyclerViewActions.actionOnItem(hasDescendant(matcher), click()));
- }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
index 263790c2f..20c54cb15 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -4,8 +4,6 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
-import junit.framework.Assert;
-
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -30,6 +28,7 @@ import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
import de.test.antennapod.util.service.download.HTTPBin;
import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
import org.greenrobot.eventbus.EventBus;
+import org.junit.Assert;
/**
* Utility methods for UI tests.
@@ -39,8 +38,6 @@ class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName();
- private static final String DATA_FOLDER = "test/UITestUtils";
-
private static final int NUM_FEEDS = 5;
private static final int NUM_ITEMS_PER_FEED = 10;
@@ -61,7 +58,7 @@ class UITestUtils {
public void setup() throws IOException {
- destDir = context.getExternalFilesDir(DATA_FOLDER);
+ destDir = new File(context.getFilesDir(), "test/UITestUtils");
destDir.mkdir();
hostedFeedDir = new File(destDir, "hostedFeeds");
hostedFeedDir.mkdir();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
index 45ba472ff..7555bb69a 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
@@ -1,35 +1,44 @@
package de.test.antennapod.ui;
-import android.test.InstrumentationTestCase;
-
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Test for the UITestUtils. Makes sure that all URLs are reachable and that the class does not cause any crashes.
*/
-public class UITestUtilsTest extends InstrumentationTestCase {
+@MediumTest
+public class UITestUtilsTest {
private UITestUtils uiTestUtils;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- uiTestUtils = new UITestUtils(getInstrumentation().getTargetContext());
+ @Before
+ public void setUp() throws Exception {
+ uiTestUtils = new UITestUtils(InstrumentationRegistry.getTargetContext());
uiTestUtils.setup();
}
- @Override
+ @After
public void tearDown() throws Exception {
- super.tearDown();
uiTestUtils.tearDown();
}
+ @Test
public void testAddHostedFeeds() throws Exception {
uiTestUtils.addHostedFeedData();
final List<Feed> feeds = uiTestUtils.hostedFeeds;
@@ -46,7 +55,7 @@ public class UITestUtilsTest extends InstrumentationTestCase {
}
}
- private void testUrlReachable(String strUtl) throws Exception {
+ public void testUrlReachable(String strUtl) throws Exception {
URL url = new URL(strUtl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
@@ -78,10 +87,12 @@ public class UITestUtilsTest extends InstrumentationTestCase {
}
}
+ @Test
public void testAddLocalFeedDataNoDownload() throws Exception {
addLocalFeedDataCheck(false);
}
+ @Test
public void testAddLocalFeedDataDownload() throws Exception {
addLocalFeedDataCheck(true);
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java
index da6f07cab..c4dd032d7 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java
@@ -1,38 +1,36 @@
package de.test.antennapod.ui;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.robotium.solo.Solo;
+import android.content.Intent;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.VideoplayerActivity;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
/**
* Test class for VideoplayerActivity
*/
-public class VideoplayerActivityTest extends ActivityInstrumentationTestCase2<VideoplayerActivity> {
-
- private Solo solo;
-
- public VideoplayerActivityTest() {
- super(VideoplayerActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- solo = new Solo(getInstrumentation(), getActivity());
- }
+@MediumTest
+@Ignore
+public class VideoplayerActivityTest {
- @Override
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- super.tearDown();
- }
+ @Rule
+ public ActivityTestRule<VideoplayerActivity> activityTestRule = new ActivityTestRule<>(VideoplayerActivity.class, false, false);
/**
* Test if activity can be started.
*/
+ @Test
public void testStartActivity() throws Exception {
- solo.waitForActivity(VideoplayerActivity.class);
+ activityTestRule.launchActivity(new Intent());
+ onView(withId(R.id.videoframe)).check(matches(isDisplayed()));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
index a36b3b65a..ac98d2802 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
@@ -1,14 +1,22 @@
package de.test.antennapod.util;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
import de.danoeh.antennapod.core.util.FileNameGenerator;
+import org.junit.After;
+import org.junit.Test;
-public class FilenameGeneratorTest extends AndroidTestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@SmallTest
+public class FilenameGeneratorTest {
private static final String VALID1 = "abc abc";
private static final String INVALID1 = "ab/c: <abc";
@@ -18,34 +26,40 @@ public class FilenameGeneratorTest extends AndroidTestCase {
super();
}
+ @Test
public void testGenerateFileName() throws IOException {
String result = FileNameGenerator.generateFileName(VALID1);
assertEquals(result, VALID1);
createFiles(result);
}
+ @Test
public void testGenerateFileName1() throws IOException {
String result = FileNameGenerator.generateFileName(INVALID1);
assertEquals(result, VALID1);
createFiles(result);
}
-
+
+ @Test
public void testGenerateFileName2() throws IOException {
String result = FileNameGenerator.generateFileName(INVALID2);
assertEquals(result, VALID1);
createFiles(result);
}
+ @Test
public void testFeedTitleContainsApostrophe() {
String result = FileNameGenerator.generateFileName("Feed's Title ...");
assertEquals("Feeds Title", result);
}
+ @Test
public void testFeedTitleContainsDash() {
String result = FileNameGenerator.generateFileName("Left - Right");
assertEquals("Left - Right", result);
}
+ @Test
public void testInvalidInput() {
String result = FileNameGenerator.generateFileName("???");
assertTrue(!TextUtils.isEmpty(result));
@@ -57,7 +71,7 @@ public class FilenameGeneratorTest extends AndroidTestCase {
* @throws IOException
*/
private void createFiles(String name) throws IOException {
- File cache = getContext().getExternalCacheDir();
+ File cache = InstrumentationRegistry.getContext().getExternalCacheDir();
File testFile = new File(cache, name);
testFile.mkdir();
assertTrue(testFile.exists());
@@ -66,10 +80,9 @@ public class FilenameGeneratorTest extends AndroidTestCase {
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- File f = new File(getContext().getExternalCacheDir(), VALID1);
+ @After
+ public void tearDown() throws Exception {
+ File f = new File(InstrumentationRegistry.getContext().getExternalCacheDir(), VALID1);
f.delete();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java b/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
index 1b444bfa9..b96cb273b 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
@@ -1,80 +1,95 @@
package de.test.antennapod.util;
-import android.test.AndroidTestCase;
-
+import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.util.URLChecker;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
/**
* Test class for URLChecker
*/
-public class URLCheckerTest extends AndroidTestCase {
+@SmallTest
+public class URLCheckerTest {
+ @Test
public void testCorrectURLHttp() {
final String in = "http://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals(in, out);
}
+ @Test
public void testCorrectURLHttps() {
final String in = "https://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals(in, out);
}
+ @Test
public void testMissingProtocol() {
final String in = "example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testFeedProtocol() {
final String in = "feed://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testPcastProtocolNoScheme() {
final String in = "pcast://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testItpcProtocol() {
final String in = "itpc://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testWhiteSpaceUrlShouldNotAppend() {
final String in = "\n http://example.com \t";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testWhiteSpaceShouldAppend() {
final String in = "\n example.com \t";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testAntennaPodSubscribeProtocolNoScheme() throws Exception {
final String in = "antennapod-subscribe://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+ @Test
public void testPcastProtocolWithScheme() {
final String in = "pcast://https://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("https://example.com", out);
}
+ @Test
public void testAntennaPodSubscribeProtocolWithScheme() throws Exception {
final String in = "antennapod-subscribe://https://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("https://example.com", out);
}
+ @Test
public void testProtocolRelativeUrlIsAbsolute() throws Exception {
final String in = "https://example.com";
final String inBase = "http://examplebase.com";
@@ -82,14 +97,15 @@ public class URLCheckerTest extends AndroidTestCase {
assertEquals(in, out);
}
+ @Test
public void testProtocolRelativeUrlIsRelativeHttps() throws Exception {
final String in = "//example.com";
final String inBase = "https://examplebase.com";
final String out = URLChecker.prepareURL(in, inBase);
assertEquals("https://example.com", out);
-
}
+ @Test
public void testProtocolRelativeUrlIsHttpsWithAPSubscribeProtocol() throws Exception {
final String in = "//example.com";
final String inBase = "antennapod-subscribe://https://examplebase.com";
@@ -97,6 +113,7 @@ public class URLCheckerTest extends AndroidTestCase {
assertEquals("https://example.com", out);
}
+ @Test
public void testProtocolRelativeUrlBaseUrlNull() throws Exception {
final String in = "example.com";
final String out = URLChecker.prepareURL(in, null);
diff --git a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
index 4bef14cd9..df69859a0 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
@@ -1,8 +1,9 @@
package de.test.antennapod.util.playback;
import android.content.Context;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@@ -16,18 +17,25 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.Timeline;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Test class for timeline
*/
-public class TimelineTest extends InstrumentationTestCase {
+@SmallTest
+public class TimelineTest {
private Context context;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- context = getInstrumentation().getTargetContext();
+ context = InstrumentationRegistry.getTargetContext();
}
private Playable newTestPlayable(List<Chapter> chapters, String shownotes, int duration) {
@@ -40,6 +48,7 @@ public class TimelineTest extends InstrumentationTestCase {
return media;
}
+ @Test
public void testProcessShownotesAddTimecodeHHMMSSNoChapters() throws Exception {
final String timeStr = "10:11:12";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
@@ -50,6 +59,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeHHMMSSMoreThen24HoursNoChapters() throws Exception {
final String timeStr = "25:00:00";
final long time = 25 * 60 * 60 * 1000;
@@ -60,6 +70,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeHHMMNoChapters() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
@@ -70,6 +81,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeMMSSNoChapters() throws Exception {
final String timeStr = "10:11";
final long time = 10 * 60 * 1000 + 11 * 1000;
@@ -80,6 +92,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeHMMSSNoChapters() throws Exception {
final String timeStr = "2:11:12";
final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
@@ -90,6 +103,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeMSSNoChapters() throws Exception {
final String timeStr = "1:12";
final long time = 60 * 1000 + 12 * 1000;
@@ -100,6 +114,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeMultipleFormatsNoChapters() throws Exception {
final String[] timeStrings = new String[]{ "10:12", "1:10:12" };
@@ -109,6 +124,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000 }, timeStrings);
}
+ @Test
public void testProcessShownotesAddTimecodeMultipleShortFormatNoChapters() throws Exception {
// One of these timecodes fits as HH:MM and one does not so both should be parsed as MM:SS.
@@ -120,6 +136,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000 }, timeStrings);
}
+ @Test
public void testProcessShownotesAddTimecodeParentheses() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
@@ -130,6 +147,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeBrackets() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
@@ -140,6 +158,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAddTimecodeAngleBrackets() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
@@ -150,6 +169,7 @@ public class TimelineTest extends InstrumentationTestCase {
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ @Test
public void testProcessShownotesAndInvalidTimecode() throws Exception {
final String[] timeStrs = new String[] {"2:1", "0:0", "000", "00", "00:000"};
@@ -183,6 +203,7 @@ public class TimelineTest extends InstrumentationTestCase {
assertEquals(timecodes.length, countedLinks);
}
+ @Test
public void testIsTimecodeLink() throws Exception {
assertFalse(Timeline.isTimecodeLink(null));
assertFalse(Timeline.isTimecodeLink("http://antennapod/timecode/123123"));
@@ -193,6 +214,7 @@ public class TimelineTest extends InstrumentationTestCase {
assertTrue(Timeline.isTimecodeLink("antennapod://timecode/1"));
}
+ @Test
public void testGetTimecodeLinkTime() throws Exception {
assertEquals(-1, Timeline.getTimecodeLinkTime(null));
assertEquals(-1, Timeline.getTimecodeLinkTime("http://timecode/123"));
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
index cde93fd7e..3d8417bf6 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
@@ -3,6 +3,7 @@ package de.test.antennapod.util.service.download;
import android.util.Base64;
import android.util.Log;
+import fi.iki.elonen.NanoHTTPD;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
deleted file mode 100644
index 8d9cedbd1..000000000
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
+++ /dev/null
@@ -1,1433 +0,0 @@
-package de.test.antennapod.util.service.download;
-
-import android.support.v4.util.ArrayMap;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.PushbackInputStream;
-import java.io.RandomAccessFile;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.URLDecoder;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-
-/**
- * A simple, tiny, nicely embeddable HTTP server in Java
- * <p/>
- * <p/>
- * NanoHTTPD
- * <p></p>Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 2010 by Konstantinos Togias</p>
- * <p/>
- * <p/>
- * <b>Features + limitations: </b>
- * <ul>
- * <p/>
- * <li>Only one Java file</li>
- * <li>Java 5 compatible</li>
- * <li>Released as open source, Modified BSD licence</li>
- * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li>
- * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li>
- * <li>Supports both dynamic content and file serving</li>
- * <li>Supports file upload (since version 1.2, 2010)</li>
- * <li>Supports partial content (streaming)</li>
- * <li>Supports ETags</li>
- * <li>Never caches anything</li>
- * <li>Doesn't limit bandwidth, request time or simultaneous connections</li>
- * <li>Default code serves files and shows all HTTP parameters and headers</li>
- * <li>File server supports directory listing, index.html and index.htm</li>
- * <li>File server supports partial content (streaming)</li>
- * <li>File server supports ETags</li>
- * <li>File server does the 301 redirection trick for directories without '/'</li>
- * <li>File server supports simple skipping for files (continue download)</li>
- * <li>File server serves also very long files without memory overhead</li>
- * <li>Contains a built-in list of most common mime types</li>
- * <li>All header names are converted lowercase so they don't vary between browsers/clients</li>
- * <p/>
- * </ul>
- * <p/>
- * <p/>
- * <b>How to use: </b>
- * <ul>
- * <p/>
- * <li>Subclass and implement serve() and embed to your own program</li>
- * <p/>
- * </ul>
- * <p/>
- * See the separate "LICENSE.md" file for the distribution license (Modified BSD licence)
- */
-public abstract class NanoHTTPD {
- /**
- * Maximum time to wait on Socket.getInputStream().read() (in milliseconds)
- * This is required as the Keep-Alive HTTP connections would otherwise
- * block the socket reading thread forever (or as long the browser is open).
- */
- private static final int SOCKET_READ_TIMEOUT = 5000;
- /**
- * Common mime type for dynamic content: plain text
- */
- private static final String MIME_PLAINTEXT = "text/plain";
- /**
- * Common mime type for dynamic content: html
- */
- private static final String MIME_HTML = "text/html";
- /**
- * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
- */
- private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
- private final String hostname;
- private final int myPort;
- private ServerSocket myServerSocket;
- private final Set<Socket> openConnections = new HashSet<>();
- private Thread myThread;
- /**
- * Pluggable strategy for asynchronously executing requests.
- */
- private AsyncRunner asyncRunner;
- /**
- * Pluggable strategy for creating and cleaning up temporary files.
- */
- private TempFileManagerFactory tempFileManagerFactory;
-
- /**
- * Constructs an HTTP server on given port.
- */
- NanoHTTPD(int port) {
- this(null, port);
- }
-
- /**
- * Constructs an HTTP server on given hostname and port.
- */
- private NanoHTTPD(String hostname, int port) {
- this.hostname = hostname;
- this.myPort = port;
- setTempFileManagerFactory(new DefaultTempFileManagerFactory());
- setAsyncRunner(new DefaultAsyncRunner());
- }
-
- private static final void safeClose(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException e) {
- }
- }
- }
-
- private static final void safeClose(Socket closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException e) {
- }
- }
- }
-
- private static final void safeClose(ServerSocket closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException e) {
- }
- }
- }
-
- /**
- * Start the server.
- *
- * @throws IOException if the socket is in use.
- */
- public void start() throws IOException {
- myServerSocket = new ServerSocket();
- myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
-
- myThread = new Thread(() -> {
- do {
- try {
- final Socket finalAccept = myServerSocket.accept();
- registerConnection(finalAccept);
- finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
- final InputStream inputStream = finalAccept.getInputStream();
- asyncRunner.exec(() -> {
- OutputStream outputStream = null;
- try {
- outputStream = finalAccept.getOutputStream();
- TempFileManager tempFileManager = tempFileManagerFactory.create();
- HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
- while (!finalAccept.isClosed()) {
- session.execute();
- }
- } catch (Exception e) {
- // When the socket is closed by the client, we throw our own SocketException
- // to break the "keep alive" loop above.
- if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
- e.printStackTrace();
- }
- } finally {
- safeClose(outputStream);
- safeClose(inputStream);
- safeClose(finalAccept);
- unRegisterConnection(finalAccept);
- }
- });
- } catch (IOException e) {
- }
- } while (!myServerSocket.isClosed());
- });
- myThread.setDaemon(true);
- myThread.setName("NanoHttpd Main Listener");
- myThread.start();
- }
-
- /**
- * Stop the server.
- */
- public void stop() {
- try {
- safeClose(myServerSocket);
- closeAllConnections();
- if (myThread != null) {
- myThread.join();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Registers that a new connection has been set up.
- *
- * @param socket the {@link Socket} for the connection.
- */
- private synchronized void registerConnection(Socket socket) {
- openConnections.add(socket);
- }
-
- /**
- * Registers that a connection has been closed
- *
- * @param socket
- * the {@link Socket} for the connection.
- */
- private synchronized void unRegisterConnection(Socket socket) {
- openConnections.remove(socket);
- }
-
- /**
- * Forcibly closes all connections that are open.
- */
- private synchronized void closeAllConnections() {
- for (Socket socket : openConnections) {
- safeClose(socket);
- }
- }
-
- public final int getListeningPort() {
- return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
- }
-
- private boolean wasStarted() {
- return myServerSocket != null && myThread != null;
- }
-
- public final boolean isAlive() {
- return wasStarted() && !myServerSocket.isClosed() && myThread.isAlive();
- }
-
- /**
- * Override this to customize the server.
- * <p/>
- * <p/>
- * (By default, this delegates to serveFile() and allows directory listing.)
- *
- * @param uri Percent-decoded URI without parameters, for example "/index.cgi"
- * @param method "GET", "POST" etc.
- * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data.
- * @param headers Header entries, percent decoded
- * @return HTTP response, see class Response for details
- */
- @Deprecated
- public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,
- Map<String, String> files) {
- return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found");
- }
-
- /**
- * Override this to customize the server.
- * <p/>
- * <p/>
- * (By default, this delegates to serveFile() and allows directory listing.)
- *
- * @param session The HTTP session
- * @return HTTP response, see class Response for details
- */
- Response serve(IHTTPSession session) {
- Map<String, String> files = new ArrayMap<>();
- Method method = session.getMethod();
- if (Method.PUT.equals(method) || Method.POST.equals(method)) {
- try {
- session.parseBody(files);
- } catch (IOException ioe) {
- return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
- } catch (ResponseException re) {
- return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
- }
- }
-
- Map<String, String> parms = session.getParms();
- parms.put(QUERY_STRING_PARAMETER, session.getQueryParameterString());
- return serve(session.getUri(), method, session.getHeaders(), parms, files);
- }
-
- /**
- * Decode percent encoded <code>String</code> values.
- *
- * @param str the percent encoded <code>String</code>
- * @return expanded form of the input, for example "foo%20bar" becomes "foo bar"
- */
- private String decodePercent(String str) {
- String decoded = null;
- try {
- decoded = URLDecoder.decode(str, "UTF8");
- } catch (UnsupportedEncodingException ignored) {
- }
- return decoded;
- }
-
- /**
- * Decode parameters from a URL, handing the case where a single parameter name might have been
- * supplied several times, by return lists of values. In general these lists will contain a single
- * element.
- *
- * @param parms original <b>NanoHttpd</b> parameters values, as passed to the <code>serve()</code> method.
- * @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
- */
- protected Map<String, List<String>> decodeParameters(Map<String, String> parms) {
- return this.decodeParameters(parms.get(QUERY_STRING_PARAMETER));
- }
-
- /**
- * Decode parameters from a URL, handing the case where a single parameter name might have been
- * supplied several times, by return lists of values. In general these lists will contain a single
- * element.
- *
- * @param queryString a query string pulled from the URL.
- * @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
- */
- private Map<String, List<String>> decodeParameters(String queryString) {
- Map<String, List<String>> parms = new ArrayMap<>();
- if (queryString != null) {
- StringTokenizer st = new StringTokenizer(queryString, "&");
- while (st.hasMoreTokens()) {
- String e = st.nextToken();
- int sep = e.indexOf('=');
- String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
- if (!parms.containsKey(propertyName)) {
- parms.put(propertyName, new ArrayList<>());
- }
- String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null;
- if (propertyValue != null) {
- parms.get(propertyName).add(propertyValue);
- }
- }
- }
- return parms;
- }
-
- // ------------------------------------------------------------------------------- //
- //
- // Threading Strategy.
- //
- // ------------------------------------------------------------------------------- //
-
- /**
- * Pluggable strategy for asynchronously executing requests.
- *
- * @param asyncRunner new strategy for handling threads.
- */
- private void setAsyncRunner(AsyncRunner asyncRunner) {
- this.asyncRunner = asyncRunner;
- }
-
- // ------------------------------------------------------------------------------- //
- //
- // Temp file handling strategy.
- //
- // ------------------------------------------------------------------------------- //
-
- /**
- * Pluggable strategy for creating and cleaning up temporary files.
- *
- * @param tempFileManagerFactory new strategy for handling temp files.
- */
- private void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
- this.tempFileManagerFactory = tempFileManagerFactory;
- }
-
- /**
- * HTTP Request methods, with the ability to decode a <code>String</code> back to its enum value.
- */
- public enum Method {
- GET, PUT, POST, DELETE, HEAD, OPTIONS;
-
- static Method lookup(String method) {
- for (Method m : Method.values()) {
- if (m.toString().equalsIgnoreCase(method)) {
- return m;
- }
- }
- return null;
- }
- }
-
- /**
- * Pluggable strategy for asynchronously executing requests.
- */
- public interface AsyncRunner {
- void exec(Runnable code);
- }
-
- /**
- * Factory to create temp file managers.
- */
- public interface TempFileManagerFactory {
- TempFileManager create();
- }
-
- // ------------------------------------------------------------------------------- //
-
- /**
- * Temp file manager.
- * <p/>
- * <p>Temp file managers are created 1-to-1 with incoming requests, to create and cleanup
- * temporary files created as a result of handling the request.</p>
- */
- public interface TempFileManager {
- TempFile createTempFile() throws Exception;
-
- void clear();
- }
-
- /**
- * A temp file.
- * <p/>
- * <p>Temp files are responsible for managing the actual temporary storage and cleaning
- * themselves up when no longer needed.</p>
- */
- public interface TempFile {
- OutputStream open();
-
- void delete();
-
- String getName();
- }
-
- /**
- * Default threading strategy for NanoHttpd.
- * <p/>
- * <p>By default, the server spawns a new Thread for every incoming request. These are set
- * to <i>daemon</i> status, and named according to the request number. The name is
- * useful when profiling the application.</p>
- */
- public static class DefaultAsyncRunner implements AsyncRunner {
- private long requestCount;
-
- @Override
- public void exec(Runnable code) {
- ++requestCount;
- Thread t = new Thread(code);
- t.setDaemon(true);
- t.setName("NanoHttpd Request Processor (#" + requestCount + ")");
- t.start();
- }
- }
-
- /**
- * Default strategy for creating and cleaning up temporary files.
- * <p/>
- * <p></p>This class stores its files in the standard location (that is,
- * wherever <code>java.io.tmpdir</code> points to). Files are added
- * to an internal list, and deleted when no longer needed (that is,
- * when <code>clear()</code> is invoked at the end of processing a
- * request).</p>
- */
- public static class DefaultTempFileManager implements TempFileManager {
- private final String tmpdir;
- private final List<TempFile> tempFiles;
-
- public DefaultTempFileManager() {
- tmpdir = System.getProperty("java.io.tmpdir");
- tempFiles = new ArrayList<>();
- }
-
- @Override
- public TempFile createTempFile() throws Exception {
- DefaultTempFile tempFile = new DefaultTempFile(tmpdir);
- tempFiles.add(tempFile);
- return tempFile;
- }
-
- @Override
- public void clear() {
- for (TempFile file : tempFiles) {
- try {
- file.delete();
- } catch (Exception ignored) {
- }
- }
- tempFiles.clear();
- }
- }
-
- /**
- * Default strategy for creating and cleaning up temporary files.
- * <p/>
- * <p></p></[>By default, files are created by <code>File.createTempFile()</code> in
- * the directory specified.</p>
- */
- public static class DefaultTempFile implements TempFile {
- private File file;
- private OutputStream fstream;
-
- public DefaultTempFile(String tempdir) throws IOException {
- file = File.createTempFile("NanoHTTPD-", "", new File(tempdir));
- fstream = new FileOutputStream(file);
- }
-
- @Override
- public OutputStream open() {
- return fstream;
- }
-
- @Override
- public void delete() {
- safeClose(fstream);
- file.delete();
- }
-
- @Override
- public String getName() {
- return file.getAbsolutePath();
- }
- }
-
- /**
- * HTTP response. Return one of these from serve().
- */
- public static class Response {
- /**
- * HTTP status code after processing, e.g. "200 OK", HTTP_OK
- */
- private IStatus status;
- /**
- * MIME type of content, e.g. "text/html"
- */
- private String mimeType;
- /**
- * Data of the response, may be null.
- */
- private InputStream data;
- /**
- * Headers for the HTTP response. Use addHeader() to add lines.
- */
- private final Map<String, String> header = new ArrayMap<>();
- /**
- * The request method that spawned this response.
- */
- private Method requestMethod;
- /**
- * Use chunkedTransfer
- */
- private boolean chunkedTransfer;
-
- /**
- * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message
- */
- public Response(String msg) {
- this(Status.OK, MIME_HTML, msg);
- }
-
- /**
- * Basic constructor.
- */
- public Response(IStatus status, String mimeType, InputStream data) {
- this.status = status;
- this.mimeType = mimeType;
- this.data = data;
- }
-
- /**
- * Convenience method that makes an InputStream out of given text.
- */
- public Response(IStatus status, String mimeType, String txt) {
- this.status = status;
- this.mimeType = mimeType;
- try {
- this.data = txt != null ? new ByteArrayInputStream(txt.getBytes("UTF-8")) : null;
- } catch (java.io.UnsupportedEncodingException uee) {
- uee.printStackTrace();
- }
- }
-
- /**
- * Adds given line to the header.
- */
- public void addHeader(String name, String value) {
- header.put(name, value);
- }
-
- public String getHeader(String name) {
- return header.get(name);
- }
-
- /**
- * Sends given response to the socket.
- */
- void send(OutputStream outputStream) {
- String mime = mimeType;
- SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
- gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
-
- try {
- if (status == null) {
- throw new Error("sendResponse(): Status can't be null.");
- }
- PrintWriter pw = new PrintWriter(outputStream);
- pw.print("HTTP/1.1 " + status.getDescription() + " \r\n");
-
- if (mime != null) {
- pw.print("Content-Type: " + mime + "\r\n");
- }
-
- if (header == null || header.get("Date") == null) {
- pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n");
- }
-
- if (header != null) {
- for (String key : header.keySet()) {
- String value = header.get(key);
- pw.print(key + ": " + value + "\r\n");
- }
- }
-
- sendConnectionHeaderIfNotAlreadyPresent(pw, header);
-
- if (requestMethod != Method.HEAD && chunkedTransfer) {
- sendAsChunked(outputStream, pw);
- } else {
- int pending = data != null ? data.available() : 0;
- sendContentLengthHeaderIfNotAlreadyPresent(pw, header, pending);
- pw.print("\r\n");
- pw.flush();
- sendAsFixedLength(outputStream, pending);
- }
- outputStream.flush();
- safeClose(data);
- } catch (IOException ioe) {
- // Couldn't write? No can do.
- }
- }
-
- void sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, int size) {
- if (!headerAlreadySent(header, "content-length")) {
- pw.print("Content-Length: "+ size +"\r\n");
- }
- }
-
- void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) {
- if (!headerAlreadySent(header, "connection")) {
- pw.print("Connection: keep-alive\r\n");
- }
- }
-
- private boolean headerAlreadySent(Map<String, String> header, String name) {
- boolean alreadySent = false;
- for (String headerName : header.keySet()) {
- alreadySent |= headerName.equalsIgnoreCase(name);
- }
- return alreadySent;
- }
-
- private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException {
- pw.print("Transfer-Encoding: chunked\r\n");
- pw.print("\r\n");
- pw.flush();
- int BUFFER_SIZE = 16 * 1024;
- byte[] CRLF = "\r\n".getBytes();
- byte[] buff = new byte[BUFFER_SIZE];
- int read;
- while ((read = data.read(buff)) > 0) {
- outputStream.write(String.format("%x\r\n", read).getBytes());
- outputStream.write(buff, 0, read);
- outputStream.write(CRLF);
- }
- outputStream.write("0\r\n\r\n".getBytes());
- }
-
- private void sendAsFixedLength(OutputStream outputStream, int pending) throws IOException {
- if (requestMethod != Method.HEAD && data != null) {
- int BUFFER_SIZE = 16 * 1024;
- byte[] buff = new byte[BUFFER_SIZE];
- while (pending > 0) {
- int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending));
- if (read <= 0) {
- break;
- }
- outputStream.write(buff, 0, read);
- pending -= read;
- }
- }
- }
-
- public IStatus getStatus() {
- return status;
- }
-
- public void setStatus(Status status) {
- this.status = status;
- }
-
- public String getMimeType() {
- return mimeType;
- }
-
- public void setMimeType(String mimeType) {
- this.mimeType = mimeType;
- }
-
- public InputStream getData() {
- return data;
- }
-
- public void setData(InputStream data) {
- this.data = data;
- }
-
- public Method getRequestMethod() {
- return requestMethod;
- }
-
- public void setRequestMethod(Method requestMethod) {
- this.requestMethod = requestMethod;
- }
-
- public void setChunkedTransfer(boolean chunkedTransfer) {
- this.chunkedTransfer = chunkedTransfer;
- }
-
- public interface IStatus {
- int getRequestStatus();
- String getDescription();
- }
-
- /**
- * Some HTTP response status codes
- */
- public enum Status implements IStatus {
- SWITCH_PROTOCOL(101, "Switching Protocols"), OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), NO_CONTENT(204, "No Content"), PARTIAL_CONTENT(206, "Partial Content"), REDIRECT(301,
- "Moved Permanently"), NOT_MODIFIED(304, "Not Modified"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401,
- "Unauthorized"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), METHOD_NOT_ALLOWED(405, "Method Not Allowed"), RANGE_NOT_SATISFIABLE(416,
- "Requested Range Not Satisfiable"), INTERNAL_ERROR(500, "Internal Server Error");
- private final int requestStatus;
- private final String description;
-
- Status(int requestStatus, String description) {
- this.requestStatus = requestStatus;
- this.description = description;
- }
-
- @Override
- public int getRequestStatus() {
- return this.requestStatus;
- }
-
- @Override
- public String getDescription() {
- return "" + this.requestStatus + " " + description;
- }
- }
- }
-
- public static final class ResponseException extends Exception {
- private static final long serialVersionUID = 1L;
-
- private final Response.Status status;
-
- public ResponseException(Response.Status status, String message) {
- super(message);
- this.status = status;
- }
-
- public ResponseException(Response.Status status, String message, Exception e) {
- super(message, e);
- this.status = status;
- }
-
- public Response.Status getStatus() {
- return status;
- }
- }
-
- /**
- * Default strategy for creating and cleaning up temporary files.
- */
- private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
- @Override
- public TempFileManager create() {
- return new DefaultTempFileManager();
- }
- }
-
- /**
- * Handles one session, i.e. parses the HTTP request and returns the response.
- */
- public interface IHTTPSession {
- void execute() throws IOException;
-
- Map<String, String> getParms();
-
- Map<String, String> getHeaders();
-
- /**
- * @return the path part of the URL.
- */
- String getUri();
-
- String getQueryParameterString();
-
- Method getMethod();
-
- InputStream getInputStream();
-
- CookieHandler getCookies();
-
- /**
- * Adds the files in the request body to the files map.
- * @arg files - map to modify
- */
- void parseBody(Map<String, String> files) throws IOException, ResponseException;
- }
-
- protected class HTTPSession implements IHTTPSession {
- public static final int BUFSIZE = 8192;
- private final TempFileManager tempFileManager;
- private final OutputStream outputStream;
- private final PushbackInputStream inputStream;
- private int splitbyte;
- private int rlen;
- private String uri;
- private Method method;
- private Map<String, String> parms;
- private Map<String, String> headers;
- private CookieHandler cookies;
- private String queryParameterString;
-
- public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
- this.tempFileManager = tempFileManager;
- this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
- this.outputStream = outputStream;
- }
-
- public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
- this.tempFileManager = tempFileManager;
- this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
- this.outputStream = outputStream;
- String remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();
- headers = new ArrayMap<>();
-
- headers.put("remote-addr", remoteIp);
- headers.put("http-client-ip", remoteIp);
- }
-
- @Override
- public void execute() throws IOException {
- try {
- // Read the first 8192 bytes.
- // The full header should fit in here.
- // Apache's default header limit is 8KB.
- // Do NOT assume that a single read will get the entire header at once!
- byte[] buf = new byte[BUFSIZE];
- splitbyte = 0;
- rlen = 0;
- {
- int read = -1;
- try {
- read = inputStream.read(buf, 0, BUFSIZE);
- } catch (Exception e) {
- safeClose(inputStream);
- safeClose(outputStream);
- throw new SocketException("NanoHttpd Shutdown");
- }
- if (read == -1) {
- // socket was been closed
- safeClose(inputStream);
- safeClose(outputStream);
- throw new SocketException("NanoHttpd Shutdown");
- }
- while (read > 0) {
- rlen += read;
- splitbyte = findHeaderEnd(buf, rlen);
- if (splitbyte > 0)
- break;
- read = inputStream.read(buf, rlen, BUFSIZE - rlen);
- }
- }
-
- if (splitbyte < rlen) {
- inputStream.unread(buf, splitbyte, rlen - splitbyte);
- }
-
- parms = new ArrayMap<>();
- if(null == headers) {
- headers = new ArrayMap<>();
- }
-
- // Create a BufferedReader for parsing the header.
- BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
-
- // Decode the header into parms and header java properties
- Map<String, String> pre = new ArrayMap<>();
- decodeHeader(hin, pre, parms, headers);
-
- method = Method.lookup(pre.get("method"));
- if (method == null) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
- }
-
- uri = pre.get("uri");
-
- cookies = new CookieHandler(headers);
-
- // Ok, now do the serve()
- Response r = serve(this);
- if (r == null) {
- throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
- } else {
- cookies.unloadQueue(r);
- r.setRequestMethod(method);
- r.send(outputStream);
- }
- } catch (SocketException e) {
- // throw it out to close socket object (finalAccept)
- throw e;
- } catch (SocketTimeoutException ste) {
- throw ste;
- } catch (IOException ioe) {
- Response r = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
- r.send(outputStream);
- safeClose(outputStream);
- } catch (ResponseException re) {
- Response r = new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
- r.send(outputStream);
- safeClose(outputStream);
- } finally {
- tempFileManager.clear();
- }
- }
-
- @Override
- public void parseBody(Map<String, String> files) throws IOException, ResponseException {
- RandomAccessFile randomAccessFile = null;
- BufferedReader in = null;
- try {
-
- randomAccessFile = getTmpBucket();
-
- long size;
- if (headers.containsKey("content-length")) {
- size = Integer.parseInt(headers.get("content-length"));
- } else if (splitbyte < rlen) {
- size = rlen - splitbyte;
- } else {
- size = 0;
- }
-
- // Now read all the body and write it to f
- byte[] buf = new byte[512];
- while (rlen >= 0 && size > 0) {
- rlen = inputStream.read(buf, 0, (int)Math.min(size, 512));
- size -= rlen;
- if (rlen > 0) {
- randomAccessFile.write(buf, 0, rlen);
- }
- }
-
- // Get the raw body as a byte []
- ByteBuffer fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length());
- randomAccessFile.seek(0);
-
- // Create a BufferedReader for easily reading it as string.
- InputStream bin = new FileInputStream(randomAccessFile.getFD());
- in = new BufferedReader(new InputStreamReader(bin));
-
- // If the method is POST, there may be parameters
- // in data section, too, read it:
- if (Method.POST.equals(method)) {
- String contentType = "";
- String contentTypeHeader = headers.get("content-type");
-
- StringTokenizer st = null;
- if (contentTypeHeader != null) {
- st = new StringTokenizer(contentTypeHeader, ",; ");
- if (st.hasMoreTokens()) {
- contentType = st.nextToken();
- }
- }
-
- if ("multipart/form-data".equalsIgnoreCase(contentType)) {
- // Handle multipart/form-data
- if (!st.hasMoreTokens()) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
- }
-
- String boundaryStartString = "boundary=";
- int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length();
- String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length());
- if (boundary.startsWith("\"") && boundary.endsWith("\"")) {
- boundary = boundary.substring(1, boundary.length() - 1);
- }
-
- decodeMultipartData(boundary, fbuf, in, parms, files);
- } else {
- String postLine = "";
- StringBuilder postLineBuffer = new StringBuilder();
- char pbuf[] = new char[512];
- int read = in.read(pbuf);
- while (read >= 0 && !postLine.endsWith("\r\n")) {
- postLine = String.valueOf(pbuf, 0, read);
- postLineBuffer.append(postLine);
- read = in.read(pbuf);
- }
- postLine = postLineBuffer.toString().trim();
- // Handle application/x-www-form-urlencoded
- if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) {
- decodeParms(postLine, parms);
- } else if (postLine.length() != 0) {
- // Special case for raw POST data => create a special files entry "postData" with raw content data
- files.put("postData", postLine);
- }
- }
- } else if (Method.PUT.equals(method)) {
- files.put("content", saveTmpFile(fbuf, 0, fbuf.limit()));
- }
- } finally {
- safeClose(randomAccessFile);
- safeClose(in);
- }
- }
-
- /**
- * Decodes the sent headers and loads the data into Key/value pairs
- */
- private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers)
- throws ResponseException {
- try {
- // Read the request line
- String inLine = in.readLine();
- if (inLine == null) {
- return;
- }
-
- StringTokenizer st = new StringTokenizer(inLine);
- if (!st.hasMoreTokens()) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
- }
-
- pre.put("method", st.nextToken());
-
- if (!st.hasMoreTokens()) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
- }
-
- String uri = st.nextToken();
-
- // Decode parameters from the URI
- int qmi = uri.indexOf('?');
- if (qmi >= 0) {
- decodeParms(uri.substring(qmi + 1), parms);
- uri = decodePercent(uri.substring(0, qmi));
- } else {
- uri = decodePercent(uri);
- }
-
- // If there's another token, it's protocol version,
- // followed by HTTP headers. Ignore version but parse headers.
- // NOTE: this now forces header names lowercase since they are
- // case insensitive and vary by client.
- if (st.hasMoreTokens()) {
- String line = in.readLine();
- while (line != null && line.trim().length() > 0) {
- int p = line.indexOf(':');
- if (p >= 0)
- headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
- line = in.readLine();
- }
- }
-
- pre.put("uri", uri);
- } catch (IOException ioe) {
- throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
- }
- }
-
- /**
- * Decodes the Multipart Body data and put it into Key/Value pairs.
- */
- private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
- Map<String, String> files) throws ResponseException {
- try {
- int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
- int boundarycount = 1;
- String mpline = in.readLine();
- while (mpline != null) {
- if (!mpline.contains(boundary)) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
- }
- boundarycount++;
- Map<String, String> item = new ArrayMap<>();
- mpline = in.readLine();
- while (mpline != null && mpline.trim().length() > 0) {
- int p = mpline.indexOf(':');
- if (p != -1) {
- item.put(mpline.substring(0, p).trim().toLowerCase(Locale.US), mpline.substring(p + 1).trim());
- }
- mpline = in.readLine();
- }
- if (mpline != null) {
- String contentDisposition = item.get("content-disposition");
- if (contentDisposition == null) {
- throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
- }
- StringTokenizer st = new StringTokenizer(contentDisposition, ";");
- Map<String, String> disposition = new ArrayMap<>();
- while (st.hasMoreTokens()) {
- String token = st.nextToken().trim();
- int p = token.indexOf('=');
- if (p != -1) {
- disposition.put(token.substring(0, p).trim().toLowerCase(Locale.US), token.substring(p + 1).trim());
- }
- }
- String pname = disposition.get("name");
- pname = pname.substring(1, pname.length() - 1);
-
- String value = "";
- if (item.get("content-type") == null) {
- StringBuilder tmp = new StringBuilder();
- while (mpline != null && !mpline.contains(boundary)) {
- mpline = in.readLine();
- if (mpline != null) {
- int d = mpline.indexOf(boundary);
- if (d == -1) {
- tmp.append(mpline);
- } else {
- tmp.append(mpline.substring(0, d - 2));
- }
- }
- }
- value = tmp.toString();
- } else {
- if (boundarycount > bpositions.length) {
- throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request");
- }
- int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
- String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
- files.put(pname, path);
- value = disposition.get("filename");
- value = value.substring(1, value.length() - 1);
- do {
- mpline = in.readLine();
- } while (mpline != null && !mpline.contains(boundary));
- }
- parms.put(pname, value);
- }
- }
- } catch (IOException ioe) {
- throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
- }
- }
-
- /**
- * Find byte index separating header from body. It must be the last byte of the first two sequential new lines.
- */
- private int findHeaderEnd(final byte[] buf, int rlen) {
- int splitbyte = 0;
- while (splitbyte + 3 < rlen) {
- if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') {
- return splitbyte + 4;
- }
- splitbyte++;
- }
- return 0;
- }
-
- /**
- * Find the byte positions where multipart boundaries start.
- */
- private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
- int matchcount = 0;
- int matchbyte = -1;
- List<Integer> matchbytes = new ArrayList<>();
- for (int i = 0; i < b.limit(); i++) {
- if (b.get(i) == boundary[matchcount]) {
- if (matchcount == 0)
- matchbyte = i;
- matchcount++;
- if (matchcount == boundary.length) {
- matchbytes.add(matchbyte);
- matchcount = 0;
- matchbyte = -1;
- }
- } else {
- i -= matchcount;
- matchcount = 0;
- matchbyte = -1;
- }
- }
- int[] ret = new int[matchbytes.size()];
- for (int i = 0; i < ret.length; i++) {
- ret[i] = matchbytes.get(i);
- }
- return ret;
- }
-
- /**
- * Retrieves the content of a sent file and saves it to a temporary file. The full path to the saved file is returned.
- */
- private String saveTmpFile(ByteBuffer b, int offset, int len) {
- String path = "";
- if (len > 0) {
- FileOutputStream fileOutputStream = null;
- try {
- TempFile tempFile = tempFileManager.createTempFile();
- ByteBuffer src = b.duplicate();
- fileOutputStream = new FileOutputStream(tempFile.getName());
- FileChannel dest = fileOutputStream.getChannel();
- src.position(offset).limit(offset + len);
- dest.write(src.slice());
- path = tempFile.getName();
- } catch (Exception e) { // Catch exception if any
- throw new Error(e); // we won't recover, so throw an error
- } finally {
- safeClose(fileOutputStream);
- }
- }
- return path;
- }
-
- private RandomAccessFile getTmpBucket() {
- try {
- TempFile tempFile = tempFileManager.createTempFile();
- return new RandomAccessFile(tempFile.getName(), "rw");
- } catch (Exception e) {
- throw new Error(e); // we won't recover, so throw an error
- }
- }
-
- /**
- * It returns the offset separating multipart file headers from the file's data.
- */
- private int stripMultipartHeaders(ByteBuffer b, int offset) {
- int i;
- for (i = offset; i < b.limit(); i++) {
- if (b.get(i) == '\r' && b.get(++i) == '\n' && b.get(++i) == '\r' && b.get(++i) == '\n') {
- break;
- }
- }
- return i + 1;
- }
-
- /**
- * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
- * adds them to given Map. NOTE: this doesn't support multiple identical keys due to the simplicity of Map.
- */
- private void decodeParms(String parms, Map<String, String> p) {
- if (parms == null) {
- queryParameterString = "";
- return;
- }
-
- queryParameterString = parms;
- StringTokenizer st = new StringTokenizer(parms, "&");
- while (st.hasMoreTokens()) {
- String e = st.nextToken();
- int sep = e.indexOf('=');
- if (sep >= 0) {
- p.put(decodePercent(e.substring(0, sep)).trim(),
- decodePercent(e.substring(sep + 1)));
- } else {
- p.put(decodePercent(e).trim(), "");
- }
- }
- }
-
- @Override
- public final Map<String, String> getParms() {
- return parms;
- }
-
- public String getQueryParameterString() {
- return queryParameterString;
- }
-
- @Override
- public final Map<String, String> getHeaders() {
- return headers;
- }
-
- @Override
- public final String getUri() {
- return uri;
- }
-
- @Override
- public final Method getMethod() {
- return method;
- }
-
- @Override
- public final InputStream getInputStream() {
- return inputStream;
- }
-
- @Override
- public CookieHandler getCookies() {
- return cookies;
- }
- }
-
- public static class Cookie {
- private final String n;
- private final String v;
- private final String e;
-
- public Cookie(String name, String value, String expires) {
- n = name;
- v = value;
- e = expires;
- }
-
- public Cookie(String name, String value) {
- this(name, value, 30);
- }
-
- public Cookie(String name, String value, int numDays) {
- n = name;
- v = value;
- e = getHTTPTime(numDays);
- }
-
- public String getHTTPHeader() {
- String fmt = "%s=%s; expires=%s";
- return String.format(fmt, n, v, e);
- }
-
- public static String getHTTPTime(int days) {
- Calendar calendar = Calendar.getInstance();
- SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
- dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
- calendar.add(Calendar.DAY_OF_MONTH, days);
- return dateFormat.format(calendar.getTime());
- }
- }
-
- /**
- * Provides rudimentary support for cookies.
- * Doesn't support 'path', 'secure' nor 'httpOnly'.
- * Feel free to improve it and/or add unsupported features.
- *
- * @author LordFokas
- */
- public class CookieHandler implements Iterable<String> {
- private final ArrayMap<String, String> cookies = new ArrayMap<>();
- private final ArrayList<Cookie> queue = new ArrayList<>();
-
- public CookieHandler(Map<String, String> httpHeaders) {
- String raw = httpHeaders.get("cookie");
- if (raw != null) {
- String[] tokens = raw.split(";");
- for (String token : tokens) {
- String[] data = token.trim().split("=");
- if (data.length == 2) {
- cookies.put(data[0], data[1]);
- }
- }
- }
- }
-
- @Override public Iterator<String> iterator() {
- return cookies.keySet().iterator();
- }
-
- /**
- * Read a cookie from the HTTP Headers.
- *
- * @param name The cookie's name.
- * @return The cookie's value if it exists, null otherwise.
- */
- public String read(String name) {
- return cookies.get(name);
- }
-
- /**
- * Sets a cookie.
- *
- * @param name The cookie's name.
- * @param value The cookie's value.
- * @param expires How many days until the cookie expires.
- */
- public void set(String name, String value, int expires) {
- queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
- }
-
- public void set(Cookie cookie) {
- queue.add(cookie);
- }
-
- /**
- * Set a cookie with an expiration date from a month ago, effectively deleting it on the client side.
- *
- * @param name The cookie name.
- */
- public void delete(String name) {
- set(name, "-delete-", -30);
- }
-
- /**
- * Internally used by the webserver to add all queued cookies into the Response's HTTP Headers.
- *
- * @param response The Response object to which headers the queued cookies will be added.
- */
- public void unloadQueue(Response response) {
- for (Cookie cookie : queue) {
- response.addHeader("Set-Cookie", cookie.getHTTPHeader());
- }
- }
- }
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java
index 809b9769a..d4837ef60 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java
@@ -1,38 +1,43 @@
package de.test.antennapod.util.syndication;
-import android.test.InstrumentationTestCase;
-
+import android.support.test.InstrumentationRegistry;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
+import java.nio.file.Files;
import java.util.Map;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Test class for FeedDiscoverer
*/
-public class FeedDiscovererTest extends InstrumentationTestCase {
+public class FeedDiscovererTest {
private FeedDiscoverer fd;
private File testDir;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
fd = new FeedDiscoverer();
- testDir = getInstrumentation().getTargetContext().getExternalFilesDir("FeedDiscovererTest");
+ testDir = new File(InstrumentationRegistry.getTargetContext().getFilesDir(), "FeedDiscovererTest");
testDir.mkdir();
assertTrue(testDir.exists());
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
FileUtils.deleteDirectory(testDir);
- super.tearDown();
}
private String createTestHtmlString(String rel, String type, String href, String title) {
@@ -81,30 +86,37 @@ public class FeedDiscovererTest extends InstrumentationTestCase {
}
}
+ @Test
public void testAlternateRSSWithTitleAbsolute() throws Exception {
checkFindUrls(true, true, true, true, true);
}
+ @Test
public void testAlternateRSSWithTitleRelative() throws Exception {
checkFindUrls(true, true, true, false, true);
}
+ @Test
public void testAlternateRSSNoTitleAbsolute() throws Exception {
checkFindUrls(true, true, false, true, true);
}
+ @Test
public void testAlternateRSSNoTitleRelative() throws Exception {
checkFindUrls(true, true, false, false, true);
}
+ @Test
public void testAlternateAtomWithTitleAbsolute() throws Exception {
checkFindUrls(true, false, true, true, true);
}
+ @Test
public void testFeedAtomWithTitleAbsolute() throws Exception {
checkFindUrls(false, false, true, true, true);
}
+ @Test
public void testAlternateRSSWithTitleAbsoluteFromFile() throws Exception {
checkFindUrls(true, true, true, true, false);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 728019196..102d30bcf 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -10,6 +10,7 @@ import android.database.DataSetObserver;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.support.annotation.VisibleForTesting;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -843,4 +844,9 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
super.onNewIntent(intent);
setIntent(intent);
}
+
+ @VisibleForTesting
+ public void updateNavDrawer() {
+ navAdapter.notifyDataSetChanged();
+ }
}