From 2c1f4968b624be54a98e84cebf03a65566c856b9 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 6 Nov 2018 13:52:56 +0100 Subject: [android] - add performance test setup --- .../android/MapboxGLAndroidSDKTestApp/build.gradle | 1 + .../src/androidTest/AndroidManifest.xml | 5 ++ .../com/mapbox/mapboxsdk/perf/AnimationTest.java | 35 +++++++++++ .../com/mapbox/mapboxsdk/perf/BasePerfTest.java | 24 ++++++++ .../mapboxsdk/rules/AbstractDumpsysRule.java | 72 ++++++++++++++++++++++ .../mapboxsdk/rules/BatteryStatsDumpsysRule.java | 11 ++++ .../mapbox/mapboxsdk/rules/CpuInfoDumpsysRule.java | 9 +++ .../mapboxsdk/rules/GraphicsDumpsysRule.java | 25 ++++++++ .../mapboxsdk/rules/MemoryInfoDumpsysRule.java | 9 +++ .../java/com/mapbox/mapboxsdk/rules/TraceRule.java | 36 +++++++++++ .../mapboxsdk/testapp/action/MapboxMapAction.java | 3 +- .../mapbox/mapboxsdk/utils/TestStorageUtils.java | 57 +++++++++++++++++ platform/android/gradle/dependencies.gradle | 4 +- 13 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/AndroidManifest.xml create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/AnimationTest.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/BasePerfTest.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/AbstractDumpsysRule.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/BatteryStatsDumpsysRule.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/CpuInfoDumpsysRule.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/GraphicsDumpsysRule.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/MemoryInfoDumpsysRule.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/TraceRule.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/TestStorageUtils.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 5588db5fc9..ed75c9885e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -77,6 +77,7 @@ dependencies { androidTestImplementation dependenciesList.testEspressoCore androidTestImplementation dependenciesList.testEspressoIntents androidTestImplementation dependenciesList.testEspressoContrib + androidTestImplementation dependenciesList.testUiAutomator } apply from: "${rootDir}/gradle/gradle-make.gradle" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/AndroidManifest.xml new file mode 100644 index 0000000000..43824257cd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/AnimationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/AnimationTest.java new file mode 100644 index 0000000000..1479448069 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/AnimationTest.java @@ -0,0 +1,35 @@ +package com.mapbox.mapboxsdk.perf; + +import android.Manifest; +import android.support.test.rule.GrantPermissionRule; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; +import org.junit.Rule; +import org.junit.Test; + +import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; + +public class AnimationTest extends BasePerfTest { + + private static final int ANIMATION_DURATION = 5000; + + @Rule + public GrantPermissionRule permissionRule = + GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE); + + @Test + public void testAnimation() { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom( + new LatLng(60.168577, 24.938001), 12), ANIMATION_DURATION); + uiController.loopMainThreadForAtLeast(ANIMATION_DURATION); + }); + } + + @Override + protected Class getActivityClass() { + return EspressoTestActivity.class; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/BasePerfTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/BasePerfTest.java new file mode 100644 index 0000000000..9ec83307f8 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/perf/BasePerfTest.java @@ -0,0 +1,24 @@ +package com.mapbox.mapboxsdk.perf; + +import com.mapbox.mapboxsdk.rules.*; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; +import org.junit.Rule; + +public abstract class BasePerfTest extends BaseActivityTest { + + @Rule + public TraceRule traceRule = new TraceRule(); + + @Rule + public BatteryStatsDumpsysRule batteryRule = new BatteryStatsDumpsysRule(); + + @Rule + public GraphicsDumpsysRule graphicsRule = new GraphicsDumpsysRule(); + + @Rule + public CpuInfoDumpsysRule cpuInfoRule = new CpuInfoDumpsysRule(); + + @Rule + public MemoryInfoDumpsysRule memoryInfoRule = new MemoryInfoDumpsysRule(); + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/AbstractDumpsysRule.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/AbstractDumpsysRule.java new file mode 100644 index 0000000000..58d0b94899 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/AbstractDumpsysRule.java @@ -0,0 +1,72 @@ +package com.mapbox.mapboxsdk.rules; + +import android.os.Trace; +import android.support.annotation.NonNull; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.UiDevice; + +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static com.mapbox.mapboxsdk.utils.TestStorageUtils.buildFileNameFrom; +import static com.mapbox.mapboxsdk.utils.TestStorageUtils.storeResponse; + +/** + * Abstract class to execute dumpsys commands, It will first reset the dumpsys data for the given service. + */ +abstract class AbstractDumpsysRule extends ExternalResource { + + private Logger logger = Logger.getLogger(this.getClass().getName()); + private String packageName; + private String fileName; + + @Override + public Statement apply(Statement base, Description description) { + String testName = description.getMethodName(); + packageName = InstrumentationRegistry.getTargetContext().getPackageName(); + fileName = buildFileNameFrom(testName); + return super.apply(base, description); + } + + @Override + public void before() { + try { + UiDevice + .getInstance(getInstrumentation()) + .executeShellCommand(String.format("dumpsys %s %s --reset", dumpsysService(), packageName)); + } catch (Exception exception) { + logger.log(Level.SEVERE, "Unable to reset dumpsys", exception); + } + } + + @Override + public void after() { + try { + Trace.beginSection("Taking Dumpsys"); + String response = UiDevice + .getInstance(getInstrumentation()) + .executeShellCommand(String.format("dumpsys %s %s %s", dumpsysService(), packageName, extraOptions())); + storeResponse(response, dumpsysService(), fileName); + logger.log(Level.INFO, "Response is: " + response); + } catch (Exception exception) { + logger.log(Level.SEVERE, "Unable to take a dumpsys", exception); + } finally { + Trace.endSection(); + } + logger.log(Level.INFO, "Dumpsys taken"); + } + + @NonNull + protected String extraOptions() { + return ""; + } + + protected abstract String dumpsysService(); +} + + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/BatteryStatsDumpsysRule.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/BatteryStatsDumpsysRule.java new file mode 100644 index 0000000000..512aac8ce0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/BatteryStatsDumpsysRule.java @@ -0,0 +1,11 @@ +package com.mapbox.mapboxsdk.rules; + +public class BatteryStatsDumpsysRule extends AbstractDumpsysRule { + + private static final String BATTERY_STATS = "batterystats"; + + @Override + protected String dumpsysService() { + return BATTERY_STATS; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/CpuInfoDumpsysRule.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/CpuInfoDumpsysRule.java new file mode 100644 index 0000000000..cf610f56d5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/CpuInfoDumpsysRule.java @@ -0,0 +1,9 @@ +package com.mapbox.mapboxsdk.rules; + +public class CpuInfoDumpsysRule extends AbstractDumpsysRule { + + @Override + protected String dumpsysService() { + return "cpuinfo"; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/GraphicsDumpsysRule.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/GraphicsDumpsysRule.java new file mode 100644 index 0000000000..98b5ca8ec7 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/GraphicsDumpsysRule.java @@ -0,0 +1,25 @@ +package com.mapbox.mapboxsdk.rules; + +import android.os.Build; +import android.support.annotation.NonNull; + +/** + * This rule executes a dumpsys graphics data dump after performing the test. If the API level is + * less than 23 then this rule will do nothing since this dumpsys command isn't supported. + */ +public class GraphicsDumpsysRule extends AbstractDumpsysRule { + + private static final String GFX_INFO = "gfxinfo"; + private static final String FRAME_STATS = "framestats"; + + @Override + protected String dumpsysService() { + return GFX_INFO; + } + + @NonNull + @Override + protected String extraOptions() { + return Build.VERSION.SDK_INT >= 23 ? FRAME_STATS : ""; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/MemoryInfoDumpsysRule.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/MemoryInfoDumpsysRule.java new file mode 100644 index 0000000000..e4b7bba7ec --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/MemoryInfoDumpsysRule.java @@ -0,0 +1,9 @@ +package com.mapbox.mapboxsdk.rules; + +public class MemoryInfoDumpsysRule extends AbstractDumpsysRule { + + @Override + protected String dumpsysService() { + return "procstats"; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/TraceRule.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/TraceRule.java new file mode 100644 index 0000000000..bba65b4476 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/rules/TraceRule.java @@ -0,0 +1,36 @@ +package com.mapbox.mapboxsdk.rules; + +import android.os.Trace; + +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * This rule enables {@link Trace Tracing} for each test. The section name + * used for the Trace API is the name of the test being run. + *

+ * To enable AndroidTracing on a test simply add this rule like so and it will be enabled/disabled + * when the platform support for Tracing exists (API Level 18 or higher). + *

+ */ +public class TraceRule extends ExternalResource { + + private String testName; + + @Override + public Statement apply(Statement base, Description description) { + testName = description.getMethodName(); + return super.apply(base, description); + } + + @Override + public void before() { + Trace.beginSection(testName); + } + + @Override + public void after() { + Trace.endSection(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java index 926212afc8..8b40a87244 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java @@ -7,6 +7,7 @@ import android.view.View; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; import org.hamcrest.Matcher; import static android.support.test.espresso.Espresso.onView; @@ -39,7 +40,7 @@ public class MapboxMapAction implements ViewAction { } public static void invoke(MapboxMap mapboxMap, OnInvokeActionListener invokeViewAction) { - onView(withId(android.R.id.content)).perform(new MapboxMapAction(invokeViewAction, mapboxMap)); + onView(withId(R.id.mapView)).perform(new MapboxMapAction(invokeViewAction, mapboxMap)); } public interface OnInvokeActionListener { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/TestStorageUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/TestStorageUtils.java new file mode 100644 index 0000000000..bd4686edc6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/TestStorageUtils.java @@ -0,0 +1,57 @@ +package com.mapbox.mapboxsdk.utils; + +import android.os.Environment; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.text.SimpleDateFormat; +import java.util.Date; + +import timber.log.Timber; + +public class TestStorageUtils { + + private static final String PERF_FOLDER = "performance"; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); + private static final String TXT_EXTENSION = ".txt"; + + public static String buildFileNameFrom(String testName) { + return testName + obtainCurrentTimeStamp() + TXT_EXTENSION; + } + + public static void storeResponse(String response, String dumpsysService, String fileName) { + if (isExternalStorageWritable()) { + File pathToExternalStorage = Environment.getExternalStorageDirectory(); + String storageAbsolutePath = pathToExternalStorage.getAbsolutePath(); + String pathname = String.format("%s/%s/%s", storageAbsolutePath, PERF_FOLDER, dumpsysService); + File appDirectory = new File(pathname); + appDirectory.mkdirs(); + File saveFilePath = new File(appDirectory, fileName); + write(response, saveFilePath); + } + } + + private static String obtainCurrentTimeStamp() { + Date now = new Date(); + return DATE_FORMAT.format(now); + } + + private static boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + return Environment.MEDIA_MOUNTED.equals(state); + } + + private static void write(String response, File saveFilePath) { + try { + FileOutputStream fos = new FileOutputStream(saveFilePath); + OutputStreamWriter outDataWriter = new OutputStreamWriter(fos); + outDataWriter.write(response); + outDataWriter.close(); + fos.flush(); + fos.close(); + } catch (Exception exception) { + Timber.e(exception); + } + } +} diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 28c22a222d..4ffdbaddba 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -23,7 +23,8 @@ ext { okhttp : '3.11.0', kotlin : '1.2.51', licenses : '0.8.41', - lint : '26.1.3' + lint : '26.1.3', + uiAutomator : '2.1.3' ] dependenciesList = [ @@ -44,6 +45,7 @@ ext { testEspressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}", testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", testEspressoContrib : "com.android.support.test.espresso:espresso-contrib:${versions.espresso}", + testUiAutomator : "com.android.support.test.uiautomator:uiautomator-v18:${versions.uiAutomator}", supportAnnotations : "com.android.support:support-annotations:${versions.supportLib}", supportAppcompatV7 : "com.android.support:appcompat-v7:${versions.supportLib}", -- cgit v1.2.1