From 9e65350b5a75ee239ec139da249d7aae9293804f Mon Sep 17 00:00:00 2001 From: Martijn Brekhof Date: Tue, 10 Oct 2017 12:06:10 +0200 Subject: [PATCH] Bug/androidtests (#466) * Fixed issue with selecting tabs * Fixed issue with switching host on other thread than UI thread * Fixed concurrent modification exception in JSONConnectionHandlerManager * Fixed issue with drawer not closing * Fixes issue with running method only supported on API 21 and higher --- .../kore/testhelpers/EspressoTestUtils.java | 34 +++++++--- .../java/org/xbmc/kore/testhelpers/Utils.java | 66 +++++++++---------- .../xbmc/kore/tests/ui/AbstractTestClass.java | 41 ++++++++---- .../tests/ui/addons/AddonsActivityTests.java | 9 ++- .../tests/ui/movies/MoviesActivityTests.java | 5 +- .../ui/generic/NavigationDrawerFragment.java | 2 +- .../ui/sections/addon/AddonsActivity.java | 6 +- .../JSONConnectionHandlerManager.java | 20 +++--- 8 files changed, 111 insertions(+), 72 deletions(-) diff --git a/app/src/androidTest/java/org/xbmc/kore/testhelpers/EspressoTestUtils.java b/app/src/androidTest/java/org/xbmc/kore/testhelpers/EspressoTestUtils.java index 2159e4e..c744d6d 100644 --- a/app/src/androidTest/java/org/xbmc/kore/testhelpers/EspressoTestUtils.java +++ b/app/src/androidTest/java/org/xbmc/kore/testhelpers/EspressoTestUtils.java @@ -207,36 +207,54 @@ public class EspressoTestUtils { return activity[0]; } + /** + * Clicks on tab that contains the text given by stringResourceId. + * If the click fails tries again after a swipe left. If the click fails + * after that try once more after swiping right two times. The first swipe right + * is performed to negate the previous swipe left. + * @param stringResourceId text displayed in Tab that should be clicked + */ + public static void clickTab(int stringResourceId) { + try { + onView(withText(stringResourceId)).perform(click()); + } catch (Exception e1) { + try { + onView(withId(R.id.pager_tab_strip)).perform(swipeLeft()); + onView(withText(stringResourceId)).perform(click()); + } catch (Exception e2) { + onView(withId(R.id.pager_tab_strip)).perform(swipeRight()); + onView(withId(R.id.pager_tab_strip)).perform(swipeRight()); + onView(withText(stringResourceId)).perform(click()); + } + } + } + /** * Clicks the album tab in the music activity */ public static void clickAlbumsTab() { - onView(withId(R.id.pager_tab_strip)).perform(swipeLeft()); - onView(withText(R.string.albums)).perform(click()); + clickTab(R.string.albums); } /** * Clicks the artists tab in the music activity */ public static void clickArtistsTab() { - onView(withId(R.id.pager_tab_strip)).perform(swipeRight()); - onView(withText(R.string.artists)).perform(click()); + clickTab(R.string.artists); } /** * Clicks the genres tab in the music activity */ public static void clickGenresTab() { - onView(withId(R.id.pager_tab_strip)).perform(swipeLeft()); - onView(withText(R.string.genres)).perform(click()); + clickTab(R.string.genres); } /** * Clicks the music videos tab in the music activity */ public static void clickMusicVideosTab() { - onView(withId(R.id.pager_tab_strip)).perform(swipeLeft()); - onView(withText(R.string.videos)).perform(click()); + clickTab(R.string.videos); } /** diff --git a/app/src/androidTest/java/org/xbmc/kore/testhelpers/Utils.java b/app/src/androidTest/java/org/xbmc/kore/testhelpers/Utils.java index 69ebe09..766df0c 100644 --- a/app/src/androidTest/java/org/xbmc/kore/testhelpers/Utils.java +++ b/app/src/androidTest/java/org/xbmc/kore/testhelpers/Utils.java @@ -16,26 +16,27 @@ package org.xbmc.kore.testhelpers; +import android.app.Activity; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.IBinder; -import android.support.annotation.IntDef; import android.support.test.rule.ActivityTestRule; import android.support.v4.widget.DrawerLayout; +import android.support.v7.preference.PreferenceManager; import android.util.Log; import android.view.Gravity; -import android.view.View; -import org.junit.internal.runners.statements.RunAfters; import org.xbmc.kore.R; import org.xbmc.kore.host.HostInfo; import org.xbmc.kore.host.HostManager; import org.xbmc.kore.provider.MediaProvider; -import org.xbmc.kore.testutils.Database; import org.xbmc.kore.utils.LogUtils; import java.lang.reflect.Method; +import static org.xbmc.kore.ui.generic.NavigationDrawerFragment.PREF_USER_LEARNED_DRAWER; + public class Utils { private static final String TAG = LogUtils.makeLogTag(Utils.class); @@ -43,16 +44,11 @@ public class Utils { private static final float DISABLED = 0.0f; private static final float DEFAULT = 1.0f; - private static boolean isInitialized; - - private static HostInfo hostInfo; - private static Context context; - - public static void closeDrawer(final ActivityTestRule activityTestRule) throws Throwable { - activityTestRule.runOnUiThread(new Runnable() { + public static void closeDrawer(final Activity activity) throws Throwable { + activity.runOnUiThread(new Runnable() { @Override public void run() { - DrawerLayout drawerLayout = (DrawerLayout) activityTestRule.getActivity().findViewById(R.id.drawer_layout); + DrawerLayout drawerLayout = (DrawerLayout) activity.findViewById(R.id.drawer_layout); drawerLayout.closeDrawers(); } }); @@ -73,43 +69,43 @@ public class Utils { } } - public static void initialize(ActivityTestRule activityTestRule, HostInfo info) throws Throwable { - if (isInitialized) - return; + public static void switchHost(final Context context, Activity activity, final HostInfo hostInfo) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + HostManager.getInstance(context).switchHost(hostInfo); + } + }); + } - hostInfo = info; - context = activityTestRule.getActivity(); + public static void clearSharedPreferences(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + editor.clear(); + editor.commit(); + } - disableAnimations(); + public static void setLearnedAboutDrawerPreference(Context context, boolean learned) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(PREF_USER_LEARNED_DRAWER, true); + editor.commit(); + } + public static void setupMediaProvider(Context context) { MediaProvider mediaProvider = new MediaProvider(); mediaProvider.setContext(context); mediaProvider.onCreate(); - - Database.fill(hostInfo, context, context.getContentResolver()); - - HostManager.getInstance(context).switchHost(hostInfo); - Utils.closeDrawer(activityTestRule); - - isInitialized = true; } - public static void cleanup() { - Database.flush(context.getContentResolver(), hostInfo); - - enableAnimations(); - - isInitialized = false; - } - - private static void disableAnimations() { + public static void disableAnimations(Context context) { int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION); if (permStatus == PackageManager.PERMISSION_GRANTED) { setSystemAnimationsScale(DISABLED); } } - private static void enableAnimations() { + public static void enableAnimations(Context context) { int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION); if (permStatus == PackageManager.PERMISSION_GRANTED) { setSystemAnimationsScale(DEFAULT); diff --git a/app/src/androidTest/java/org/xbmc/kore/tests/ui/AbstractTestClass.java b/app/src/androidTest/java/org/xbmc/kore/tests/ui/AbstractTestClass.java index 33e8501..c5cf67f 100644 --- a/app/src/androidTest/java/org/xbmc/kore/tests/ui/AbstractTestClass.java +++ b/app/src/androidTest/java/org/xbmc/kore/tests/ui/AbstractTestClass.java @@ -16,8 +16,10 @@ package org.xbmc.kore.tests.ui; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.support.test.InstrumentationRegistry; import android.support.test.espresso.Espresso; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; @@ -31,7 +33,9 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.runner.RunWith; import org.xbmc.kore.host.HostInfo; +import org.xbmc.kore.host.HostManager; import org.xbmc.kore.jsonrpc.HostConnection; +import org.xbmc.kore.provider.MediaProvider; import org.xbmc.kore.testhelpers.LoaderIdlingResource; import org.xbmc.kore.testhelpers.Utils; import org.xbmc.kore.testutils.Database; @@ -58,6 +62,8 @@ abstract public class AbstractTestClass { private static PlayerHandler playerHandler; private static ApplicationHandler applicationHandler; + private HostInfo hostInfo; + @BeforeClass public static void setupMockTCPServer() throws Throwable { playerHandler = new PlayerHandler(); @@ -75,31 +81,38 @@ abstract public class AbstractTestClass { activityTestRule = getActivityTestRule(); +// final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + final Context context = activityTestRule.getActivity(); + //Note: as the activity is not yet available in @BeforeClass we need // to add the handler here if (addonsHandler == null) { - addonsHandler = new AddonsHandler(activityTestRule.getActivity()); + addonsHandler = new AddonsHandler(context); manager.addHandler(addonsHandler); } - HostInfo hostInfo = Database.addHost(activityTestRule.getActivity(), server.getHostName(), - HostConnection.PROTOCOL_TCP, HostInfo.DEFAULT_HTTP_PORT, - server.getPort()); + hostInfo = Database.addHost(context, server.getHostName(), + HostConnection.PROTOCOL_TCP, HostInfo.DEFAULT_HTTP_PORT, + server.getPort()); - Utils.initialize(activityTestRule, hostInfo); + Utils.clearSharedPreferences(context); + + //Prevent drawer from opening when we start a new activity + Utils.setLearnedAboutDrawerPreference(context, true); loaderIdlingResource = new LoaderIdlingResource(activityTestRule.getActivity().getSupportLoaderManager()); Espresso.registerIdlingResources(loaderIdlingResource); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activityTestRule.getActivity()); - SharedPreferences.Editor editor = prefs.edit(); - editor.clear(); - editor.commit(); + Utils.disableAnimations(context); - //Relaunch the activity for the changes (Host selections, preference reset) to take effect + Utils.setupMediaProvider(context); + + Database.fill(hostInfo, context, context.getContentResolver()); + + Utils.switchHost(context, activityTestRule.getActivity(), hostInfo); + + //Relaunch the activity for the changes (Host selection and database fill) to take effect activityTestRule.launchActivity(new Intent()); - - Utils.closeDrawer(activityTestRule); } @After @@ -110,7 +123,9 @@ abstract public class AbstractTestClass { applicationHandler.reset(); playerHandler.reset(); - Utils.cleanup(); + Context context = activityTestRule.getActivity(); + Database.flush(context.getContentResolver(), hostInfo); + Utils.enableAnimations(context); } @AfterClass diff --git a/app/src/androidTest/java/org/xbmc/kore/tests/ui/addons/AddonsActivityTests.java b/app/src/androidTest/java/org/xbmc/kore/tests/ui/addons/AddonsActivityTests.java index 659cd65..50877e4 100644 --- a/app/src/androidTest/java/org/xbmc/kore/tests/ui/addons/AddonsActivityTests.java +++ b/app/src/androidTest/java/org/xbmc/kore/tests/ui/addons/AddonsActivityTests.java @@ -16,6 +16,8 @@ package org.xbmc.kore.tests.ui.addons; +import android.content.Intent; +import android.os.SystemClock; import android.support.test.rule.ActivityTestRule; import android.widget.TextView; @@ -27,6 +29,7 @@ import org.xbmc.kore.testhelpers.EspressoTestUtils; import org.xbmc.kore.testhelpers.Utils; import org.xbmc.kore.tests.ui.AbstractTestClass; import org.xbmc.kore.tests.ui.BaseMediaActivityTests; +import org.xbmc.kore.ui.sections.addon.AddonsActivity; import org.xbmc.kore.ui.sections.video.MoviesActivity; import static android.support.test.espresso.Espresso.onView; @@ -71,9 +74,9 @@ public class AddonsActivityTests extends BaseMediaActivityTests @Override public void setUp() throws Throwable { super.setUp(); - //Start the AddonsActivity from the MoviesActivity - Utils.openDrawer(getActivityTestRule()); - EspressoTestUtils.clickAdapterViewItem(7, R.id.navigation_drawer); + + Intent intent = new Intent(getActivity(), AddonsActivity.class); + getActivity().startActivity(intent); } /** diff --git a/app/src/androidTest/java/org/xbmc/kore/tests/ui/movies/MoviesActivityTests.java b/app/src/androidTest/java/org/xbmc/kore/tests/ui/movies/MoviesActivityTests.java index 4c8a382..5c1c562 100644 --- a/app/src/androidTest/java/org/xbmc/kore/tests/ui/movies/MoviesActivityTests.java +++ b/app/src/androidTest/java/org/xbmc/kore/tests/ui/movies/MoviesActivityTests.java @@ -16,6 +16,7 @@ package org.xbmc.kore.tests.ui.movies; +import android.os.SystemClock; import android.support.test.rule.ActivityTestRule; import android.widget.TextView; @@ -80,7 +81,7 @@ public class MoviesActivityTests extends BaseMediaActivityTests public void restoreActionBarTitleOnConfigurationStateChanged() { EspressoTestUtils.selectListItemRotateDeviceAndCheckActionbarTitle(0, R.id.list, "#Rookie93 Marc Marquez: Beyond the Smile", - mActivityRule.getActivity()); + getActivity()); } /** @@ -94,6 +95,6 @@ public class MoviesActivityTests extends BaseMediaActivityTests @Test public void restoreActionBarTitleOnReturningFromMovie() { selectListItemPressBackAndCheckActionbarTitle(0, R.id.list, - mActivityRule.getActivity().getString(R.string.movies)); + getActivity().getString(R.string.movies)); } } diff --git a/app/src/main/java/org/xbmc/kore/ui/generic/NavigationDrawerFragment.java b/app/src/main/java/org/xbmc/kore/ui/generic/NavigationDrawerFragment.java index d6a1028..29b9f11 100644 --- a/app/src/main/java/org/xbmc/kore/ui/generic/NavigationDrawerFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/generic/NavigationDrawerFragment.java @@ -74,7 +74,7 @@ public class NavigationDrawerFragment extends Fragment { * Per the design guidelines, you should show the drawer on launch until the user manually * expands it. This shared preference tracks this. */ - private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; + public static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; public static final int ACTIVITY_HOSTS = 0, ACTIVITY_REMOTE = 1, diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonsActivity.java b/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonsActivity.java index 1b6d0fa..7cec1d3 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonsActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonsActivity.java @@ -27,6 +27,7 @@ import org.xbmc.kore.ui.AbstractFragment; import org.xbmc.kore.ui.BaseMediaActivity; import org.xbmc.kore.ui.sections.remote.RemoteActivity; import org.xbmc.kore.utils.LogUtils; +import org.xbmc.kore.utils.Utils; /** * Controls the presentation of Addons information (list, details) @@ -127,8 +128,9 @@ public class AddonsActivity extends BaseMediaActivity ; addonDetailsFragment.setDataHolder(vh.dataHolder); vh.dataHolder.setSquarePoster(true); - vh.dataHolder.setPosterTransitionName(vh.artView.getTransitionName()); - + if(Utils.isLollipopOrLater()) { + vh.dataHolder.setPosterTransitionName(vh.artView.getTransitionName()); + } showFragment(addonDetailsFragment, vh.artView, vh.dataHolder); updateActionBar(getActionBarTitle(), true); diff --git a/app/src/testUtils/java/org/xbmc/kore/testutils/tcpserver/handlers/JSONConnectionHandlerManager.java b/app/src/testUtils/java/org/xbmc/kore/testutils/tcpserver/handlers/JSONConnectionHandlerManager.java index 2c80cfc..ff354f0 100644 --- a/app/src/testUtils/java/org/xbmc/kore/testutils/tcpserver/handlers/JSONConnectionHandlerManager.java +++ b/app/src/testUtils/java/org/xbmc/kore/testutils/tcpserver/handlers/JSONConnectionHandlerManager.java @@ -120,15 +120,17 @@ public class JSONConnectionHandlerManager implements MockTcpServer.TcpServerConn @Override public String getResponse() { StringBuffer stringBuffer = new StringBuffer(); - - //Handle responses - Collection> jsonResponses = clientResponses.values(); - for(ArrayList arrayList : jsonResponses) { - for (JsonResponse response : arrayList) { - stringBuffer.append(response.toJsonString() + "\n"); + + synchronized (clientResponses) { + //Handle responses + Collection> jsonResponses = clientResponses.values(); + for (ArrayList arrayList : jsonResponses) { + for (JsonResponse response : arrayList) { + stringBuffer.append(response.toJsonString() + "\n"); + } } + clientResponses.clear(); } - clientResponses.clear(); //Handle notifications for(ConnectionHandler handler : handlers) { @@ -168,7 +170,9 @@ public class JSONConnectionHandlerManager implements MockTcpServer.TcpServerConn ArrayList responses = clientResponses.get(String.valueOf(id)); if (responses == null) { responses = new ArrayList<>(); - clientResponses.put(String.valueOf(id), responses); + synchronized (clientResponses) { + clientResponses.put(String.valueOf(id), responses); + } } responses.addAll(jsonResponses); }