Fixed issue with running many instrumentation tests (#536)

During tests, activities were not closed properly causing increasing
memory usage, which resulted in failing tests because of
out of memory errors.
This has been fixed by using the test orchestrator which runs
each test in its own instrumentation instance.

Removed the instrumentationTest flavor and replaced it by using the
default debug build type. The flavor was used to keep the required
permissions during testing separate from release versions. This can
also be accomplished using the debug build type.

The PagerSlidingTabStrip from astuetz doesn't work with the new
android build tools. As the project seems dead, I replaced it with
PagerTabStrip from the support library.
This commit is contained in:
Martijn Brekhof 2018-05-03 12:04:04 +02:00 committed by Synced Synapse
parent 75f8326fe4
commit f631efeca9
55 changed files with 120 additions and 145 deletions

View File

@ -14,9 +14,9 @@ android:
# "tools" always refers to the latest build tools version. Remove this
# when travis is able to parse "build-tools-23.0.3" correctly.
# See https://github.com/travis-ci/travis-ci/issues/5036
- tools
# - tools
# needed build tools
- build-tools-26.0.2
- build-tools-27.0.3
# The SDK version used to compile your project
- android-26
@ -27,4 +27,4 @@ android:
before_script:
script:
- ./gradlew lintFullRelease testFullDebugUnitTest assembleFullRelease
- ./gradlew assembleRelease lintRelease testDebugUnitTest

View File

@ -12,7 +12,7 @@ def getVersionName = { ->
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "org.xbmc.kore"
@ -20,6 +20,8 @@ android {
targetSdkVersion 26
versionCode 19
versionName = getVersionName()
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
File keystoreFile = file('keystore.properties')
@ -38,38 +40,22 @@ android {
}
}
sourceSets {
fullDebug {
assets.srcDirs += 'src/testUtils/assets'
}
test {
java.srcDirs += 'src/testUtils/java'
}
androidTest {
java.srcDirs += 'src/testUtils/java'
}
instrumentationTest {
assets.srcDirs += 'src/testUtils/assets'
}
}
// sourceSets {
// test {
// java.srcDirs += 'src/testUtils/java'
// assets.srcDirs += 'src/testUtils/assets'
// }
// androidTest {
// java.srcDirs += 'src/testUtils/java'
// assets.srcDirs += 'src/testUtils/assets'
// }
// }
productFlavors {
full {
}
instrumentationTest {
applicationId "org.xbmc.kore.instrumentationtest"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
proguardFiles 'proguardTest-rules.pro'
}
testOptions {
execution 'ANDROID_TEST_ORCHESTRATOR'
}
buildTypes {
// debug {
// minifyEnabled true
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// }
release {
if (keystoreFile.exists()) {
signingConfig signingConfigs.release
@ -86,7 +72,7 @@ android {
//The xml launcher icon introduced in API 26 causes lint to generate
//an error that the icon is not a PNG format
//This reduces the error to a warning
warning 'IconLauncherFormat'
// warning 'IconLauncherFormat'
}
// allprojects {
@ -111,66 +97,56 @@ ext {
}
dependencies {
compile "com.android.support:support-v4:${supportLibVersion}"
compile "com.android.support:appcompat-v7:${supportLibVersion}"
compile "com.android.support:cardview-v7:${supportLibVersion}"
compile "com.android.support:preference-v14:${supportLibVersion}"
compile "com.android.support:support-v13:${supportLibVersion}"
compile "com.android.support:design:${supportLibVersion}"
implementation "com.android.support:support-v4:${supportLibVersion}"
implementation "com.android.support:appcompat-v7:${supportLibVersion}"
implementation "com.android.support:cardview-v7:${supportLibVersion}"
implementation "com.android.support:preference-v14:${supportLibVersion}"
implementation "com.android.support:support-v13:${supportLibVersion}"
implementation "com.android.support:design:${supportLibVersion}"
compile 'com.fasterxml.jackson.core:jackson-databind:2.5.2'
compile 'com.jakewharton:butterknife:8.8.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.5.2'
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'de.greenrobot:eventbus:2.4.0'
compile 'org.jmdns:jmdns:3.5.1'
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
compile 'at.blogc:expandabletextview:1.0.3'
compile 'com.sothree.slidinguppanel:library:3.3.1'
implementation 'com.squareup.okhttp:okhttp:2.3.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'de.greenrobot:eventbus:2.4.0'
implementation 'org.jmdns:jmdns:3.5.1'
implementation 'at.blogc:expandabletextview:1.0.3'
implementation 'com.sothree.slidinguppanel:library:3.3.1'
androidTestCompile 'com.android.support.test:runner:1.0.1'
androidTestCompile 'com.android.support.test:rules:1.0.1'
androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestCompile "com.android.support:support-v13:${supportLibVersion}"
instrumentationTestCompile 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test:rules:1.0.1'
androidTestUtil 'com.android.support.test:orchestrator:1.0.1'
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestImplementation "com.android.support:support-v13:${supportLibVersion}"
androidTestImplementation 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.1.1'
testCompile 'junit:junit:4.12'
compile fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'org.robolectric:robolectric:3.1.1'
debugImplementation 'junit:junit:4.12'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
// Get the path to ADB. Required when running tests directly from Android Studio.
// Source: http://stackoverflow.com/a/26771087/112705
def adb = android.getAdbExe().toString()
// Source: http://stackoverflow.com/q/29908110/112705
def adb = android.getAdbExecutable().toString()
afterEvaluate {
task grantAnimationPermissionDev(type: Exec, dependsOn: 'installInstrumentationTestDebug') {
task grantAnimationPermissionDev(type: Exec, dependsOn: installDebug) {
doFirst {
println("Executing: $adb shell pm grant $android.productFlavors.instrumentationTest.applicationId android.permission.SET_ANIMATION_SCALE")
commandLine "$adb shell pm grant $android.productFlavors.instrumentationTest.applicationId android.permission.SET_ANIMATION_SCALE".split(' ')
println("Executing: $adb shell pm grant $android.defaultConfig.applicationId android.permission.SET_ANIMATION_SCALE")
commandLine "$adb shell pm grant $android.defaultConfig.applicationId android.permission.SET_ANIMATION_SCALE".split(' ')
}
}
// When launching individual tests from Android Studio, it seems that only the assemble tasks
// get called directly, not the install* versions
tasks.each { task ->
if (task.name.startsWith('assembleInstrumentationTestDebugAndroidTest')) {
if (task.name.startsWith('connectedDebugAndroidTest')) {
task.dependsOn grantAnimationPermissionDev
}
}
}
/**
* Android gradle plugin 2.3.x contains a bug where the assets for fullDebug
* are not copied. The task assembleFullDebug does copy the required assets,
* so this dependency fixes the issue.
* Makes sure assets are copied before running the unit tests
*/
tasks.whenTaskAdded { task ->
if (task.name.contains("compileFullDebugUnitTestSources")) {
task.dependsOn assembleFullDebug
if (task.name.contains("testDebugUnitTest")) {
task.dependsOn assembleDebug
}
}

View File

@ -1,8 +0,0 @@
-keep class org.junit.** { *; }
-dontwarn org.junit.**
-keep class junit.** { *; }
-dontwarn junit.**
-keep class sun.misc.** { *; }
-dontwarn sun.misc.**

View File

@ -39,8 +39,6 @@ import static android.support.test.espresso.Espresso.openActionBarOverflowOrOpti
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.clearText;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.swipeLeft;
import static android.support.test.espresso.action.ViewActions.swipeRight;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
@ -209,24 +207,10 @@ public class EspressoTestUtils {
/**
* 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());
}
}
onView(withId(R.id.pager)).perform(ViewActions.setCurrentViewPagerItem(stringResourceId));
}
/**

View File

@ -117,6 +117,8 @@ public class Utils {
int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
if (permStatus == PackageManager.PERMISSION_GRANTED) {
setSystemAnimationsScale(DEFAULT);
} else {
LogUtils.LOGD(TAG, "disableAnimations: permission " + ANIMATION_PERMISSION + " not granted");
}
}

View File

@ -25,6 +25,8 @@ import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.ScrollToAction;
import android.support.test.espresso.util.HumanReadables;
import android.support.test.espresso.util.TreeIterables;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.widget.SeekBar;
@ -157,4 +159,42 @@ public final class ViewActions {
}
};
}
public static ViewAction setCurrentViewPagerItem(final int pageTitleResourceId) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return new TypeSafeMatcher<View>() {
@Override
protected boolean matchesSafely(View item) {
return item instanceof ViewPager;
}
@Override
public void describeTo(Description description) {
description.appendText("is a SeekBar.");
}
};
}
@Override
public String getDescription() {
return null;
}
@Override
public void perform(UiController uiController, View view) {
ViewPager viewPager = (ViewPager) view;
String expectedTitle = view.getResources().getString(pageTitleResourceId);
PagerAdapter pagerAdapter = viewPager.getAdapter();
for(int i = 0; i < pagerAdapter.getCount(); i++) {
if (expectedTitle.contentEquals(pagerAdapter.getPageTitle(i))) {
viewPager.setCurrentItem(i);
return;
}
}
}
};
}
}

View File

@ -1,5 +0,0 @@
Resources required for the instrumentation tests in [androidTest](../androidTest)
**Note**: do not put any tests here! Put local tests
that DO NOT need to be executed on an android device in [test](../test).
Put tests that DO need to run on an android device in [androidTest](../androidTest).

View File

@ -23,8 +23,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.astuetz.PagerSlidingTabStrip;
import org.xbmc.kore.R;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.SharedElementTransition;
@ -39,7 +37,6 @@ abstract public class AbstractTabsFragment extends AbstractFragment
implements SharedElementTransition.SharedElement {
private static final String TAG = LogUtils.makeLogTag(AbstractTabsFragment.class);
@BindView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip;
@BindView(R.id.pager) ViewPager viewPager;
private Unbinder unbinder;
@ -64,8 +61,6 @@ abstract public class AbstractTabsFragment extends AbstractFragment
unbinder = ButterKnife.bind(this, root);
viewPager.setAdapter(createTabsAdapter(getDataHolder()));
pagerTabStrip.setViewPager(viewPager);
return root;
}

View File

@ -16,24 +16,17 @@
package org.xbmc.kore.ui.sections.audio;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.astuetz.PagerSlidingTabStrip;
import org.xbmc.kore.R;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractFragment;
import org.xbmc.kore.ui.AbstractTabsFragment;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.TabsAdapter;
import butterknife.ButterKnife;
import butterknife.BindView;
/**
* Container for the various music lists
*/

View File

@ -2,5 +2,5 @@
<vector android:alpha="1.00" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFFFF" android:pathData="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/>
<path android:fillColor="#FFFFFFFF" android:pathData="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V0.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/>
</vector>

View File

@ -21,15 +21,17 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.astuetz.PagerSlidingTabStrip
android:id="@+id/pager_tab_strip"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_strip_height"
style="@style/TabStrip"/>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
android:id="@+id/pager_tab_strip"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_strip_height"
style="@style/TabStrip"/>
</android.support.v4.view.ViewPager>
</LinearLayout>

View File

@ -52,7 +52,7 @@
android:paddingLeft="@dimen/small_padding"
android:paddingRight="@dimen/small_padding"
android:textAppearance="@style/TextAppearance.Notification.Title"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:gravity="center_vertical"/>
@ -64,7 +64,7 @@
android:paddingLeft="@dimen/small_padding"
android:paddingRight="@dimen/small_padding"
android:textAppearance="@style/TextAppearance.Notification.Details"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:gravity="center_vertical"/>

View File

@ -18,7 +18,7 @@
<fade>
<targets>
<target android:targetId="@id/art"/>
<target android:targetId="@id/pager_tab_strip"/>
<target android:targetId="@id/pager"/>
</targets>
</fade>
@ -26,7 +26,7 @@
<targets>
<target android:excludeId="@id/art"/>
<target android:excludeId="@id/fab"/>
<target android:excludeId="@id/pager_tab_strip"/>
<target android:excludeId="@id/pager"/>
</targets>
</slide>

View File

@ -101,15 +101,6 @@
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Subhead</item>
<item name="android:background">?attr/colorPrimary</item>
<item name="android:textColor">@color/white</item>
<item name="pstsDividerColor">@android:color/transparent</item>
<item name="pstsIndicatorColor">@color/white</item>
<!--<item name="pstsTabPaddingLeftRight">32dp</item>-->
<!--<item name="pstsTabBackground">?attr/systemBarsTopColor</item>-->
<item name="pstsUnderlineColor">@color/white_dim_50pct</item>
<item name="pstsUnderlineHeight">0dp</item>
<item name="pstsIndicatorHeight">2dp</item>
<item name="pstsShouldExpand">false</item>
<item name="pstsTextAllCaps">true</item>
</style>
<style name="ViewPagerIndicator">

View File

@ -3,9 +3,13 @@
buildscript {
repositories {
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -16,7 +20,8 @@ allprojects {
repositories {
jcenter()
maven {
url "https://maven.google.com"
url "https://maven.google.com/"
name 'Google'
}
}
}

View File

@ -1,6 +1,6 @@
#Fri Aug 04 21:29:21 CEST 2017
#Thu Apr 19 08:59:44 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

2
tools/jenkins/buildsteps/build-release Normal file → Executable file
View File

@ -1,4 +1,4 @@
WORKSPACE=${WORKSPACE:-$( cd $(dirname $0)/../.. ; pwd -P )}
WORKSPACE=${WORKSPACE:-$( cd $(dirname $0)/../../.. ; pwd -P )}
export ANDROID_HOME=$SDK_PATH_KORE
cd $WORKSPACE;./gradlew assembleRelease

4
tools/jenkins/buildsteps/package Normal file → Executable file
View File

@ -1,4 +1,4 @@
WORKSPACE=${WORKSPACE:-$( cd $(dirname $0)/../.. ; pwd -P )}
WORKSPACE=${WORKSPACE:-$( cd $(dirname $0)/../../.. ; pwd -P )}
$RUN_SIGNSTEP
@ -52,4 +52,4 @@ function getBuildRevDateStr ()
#rename for upload
#e.x. kore-20130314-8c2fb31.apk.
UPLOAD_FILENAME="kore-$(getBuildRevDateStr).apk"
cd $WORKSPACE;mv *.apk $UPLOAD_FILENAME
cd $WORKSPACE;mv app/build/outputs/apk/release/*.apk $UPLOAD_FILENAME