From 7c4bef6083e1615e3dce8ff8a28c2628b776d941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 1 May 2018 14:05:34 +0200 Subject: wip --- .../com/mapbox/mapboxsdk/maps/PerformanceTest.java | 31 ++++++ .../mapboxsdk/testapp/utils/PerfTestingUtils.java | 76 +++++++++++++++ .../mapboxsdk/testrule/EnableLogcatDump.java | 73 ++++++++++++++ .../mapboxsdk/testrule/EnablePerTestTraceFile.java | 66 +++++++++++++ .../mapboxsdk/testrule/EnablePostTestDumpsys.java | 105 +++++++++++++++++++++ .../mapboxsdk/testrule/EnableTestTracing.java | 44 +++++++++ .../src/main/AndroidManifest.xml | 3 + .../activity/maplayout/SimpleMapActivity.java | 80 ++++++++++++++++ 8 files changed, 478 insertions(+) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/PerfTestingUtils.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableLogcatDump.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePerTestTraceFile.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePostTestDumpsys.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableTestTracing.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/PerformanceTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/PerformanceTest.java index 59f6b5ae0e..e9478a6a80 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/PerformanceTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/PerformanceTest.java @@ -1,6 +1,8 @@ package com.mapbox.mapboxsdk.maps; +import android.Manifest; import android.support.test.espresso.UiController; +import android.support.test.rule.GrantPermissionRule; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdate; @@ -8,7 +10,10 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; +import com.mapbox.mapboxsdk.testrule.EnableLogcatDump; +import com.mapbox.mapboxsdk.testrule.EnablePostTestDumpsys; +import org.junit.Rule; import org.junit.Test; import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; @@ -27,6 +32,32 @@ public class PerformanceTest extends BaseActivityTest { return EspressoTestActivity.class; } +// @Rule +// public EnableTestTracing enableTestTracing = new EnableTestTracing(); + + // @Rule +// public EnablePerTestTraceFile enablePerTestTraceFile = new EnablePerTestTraceFile(); + @Rule + public GrantPermissionRule readPermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE); + + @Rule + public GrantPermissionRule writePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE); + + @Rule + public GrantPermissionRule dumpPermissionRule = GrantPermissionRule.grant(Manifest.permission.DUMP); + + @Rule + public GrantPermissionRule logPermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_LOGS); + + @Rule + public GrantPermissionRule usagePermissionRule = GrantPermissionRule.grant(Manifest.permission.PACKAGE_USAGE_STATS); + + @Rule + public EnablePostTestDumpsys enablePostTestDumpsys = new EnablePostTestDumpsys(); + + @Rule + public EnableLogcatDump enableLogcatDump = new EnableLogcatDump(); + @Test public void runPerformanceTest() { validateTestSetup(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/PerfTestingUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/PerfTestingUtils.java new file mode 100644 index 0000000000..543ae5e668 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/PerfTestingUtils.java @@ -0,0 +1,76 @@ +package com.mapbox.mapboxsdk.testapp.utils; + +import android.content.Context; +import android.os.Environment; +import android.support.test.InstrumentationRegistry; + +import java.io.File; + +public class PerfTestingUtils { + /** + * Retrieve the directory where test files should be written to. + * This directory is local to the application and removed when the app is uninstalled. + */ + public static File getTestDir(String className, String testName) { + + File rootDir = Environment.getExternalStorageDirectory();//getAppContext().getFilesDir(); + + // Create a test data subdirectory. + File testFileDir = new File(rootDir, "testdata"); + if (getTranslatedTestName(className, testName) != null) { + testFileDir = new File(testFileDir, getTranslatedTestName(className, testName)); + } + + if (!testFileDir.exists()) { + if (!testFileDir.mkdirs()) { + throw new RuntimeException("Unable to create test file directory."); + } + } + return testFileDir; + + } + + /** + * Retrieve the app-under-test's {@code Context}. + */ + public static Context getAppContext() { + return InstrumentationRegistry.getInstrumentation().getTargetContext(); + } + + /** + * Retrieve a file handle that is within the testing directory where tests should be written to. + */ + public static File getTestRunFile(String filename) { + return new File(getTestDir(null, null), filename); + } + + /** + * Retrieve the test run directory where tests should be written to. + */ + public static File getTestRunDir() { + return getTestDir(null, null); + } + + private static String getTranslatedTestName(String className, String testName) { + if (className == null || testName == null) { + return null; + } + String base = className + "_" + testName; + + // Shorten the common strings so "com.google.android" comes out as "c.g.a" for brevity. + base = base.replace("com", "c") + .replace("google", "g") + .replace("android", "a") + .replace("perfmatters", "pm") + .replace("automatingperformancetesting", "apt"); + return base; + + } + + /** + * Retrieve a file handle that is within the testing directory where tests should be written to. + */ + public static File getTestFile(String className, String testName, String filename) { + return new File(getTestDir(className, testName), filename); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableLogcatDump.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableLogcatDump.java new file mode 100644 index 0000000000..9a241a337b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableLogcatDump.java @@ -0,0 +1,73 @@ +package com.mapbox.mapboxsdk.testrule; + +import android.os.Build; +import android.os.Trace; +import android.util.Log; + +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import static com.mapbox.mapboxsdk.testapp.utils.PerfTestingUtils.getTestFile; + +public class EnableLogcatDump extends ExternalResource { + + private static final String LOG_TAG = "EnableLogcatDump"; + + private String mTestName; + + private String mTestClass; + + @Override + public Statement apply(Statement base, Description description) { + mTestName = description.getMethodName(); + mTestClass = description.getClassName(); + return super.apply(base, description); + } + + /** + * Clear logcat buffer prior to test run. + */ + public void before() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command("logcat", "-c"); + processBuilder.redirectErrorStream(); + Process process = processBuilder.start(); + process.waitFor(); + if (process.exitValue() != 0) { + Log.e(LOG_TAG, "Error while clearing logcat, exitValue=" + process.exitValue()); + } + } + } + + /** + * Extract logcat buffer to a file ater test run. + */ + public void after() { + try { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + Trace.beginSection("Taking logcat"); + } + ProcessBuilder processBuilder = new ProcessBuilder(); + + processBuilder.command("logcat", "-d", + "-f", getTestFile(mTestClass, mTestName, "logcat.log") + .getAbsolutePath()); + processBuilder.redirectErrorStream(); + Process process = processBuilder.start(); + process.waitFor(); + if (process.exitValue() != 0) { + Log.e(LOG_TAG, "Error exit value while extracting logcat, exitValue=" + + process.exitValue()); + } + } catch (Exception ignored) { + Log.e(LOG_TAG, "Error while extracting logcat", ignored); + } finally { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + Trace.endSection(); + } + } + } +} +///data/user/0/com.mapbox.mapboxsdk.testapp/files/testdata/c.mapbox.mapboxsdk.maps.PerformanceTest_runPerformanceTest/logcat.log \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePerTestTraceFile.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePerTestTraceFile.java new file mode 100644 index 0000000000..969e53d6e9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePerTestTraceFile.java @@ -0,0 +1,66 @@ +package com.mapbox.mapboxsdk.testrule; + +import com.mapbox.mapboxsdk.testapp.BuildConfig; + +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * This rule enables pulling an atrace file that can be run through systrace.py + * + * This JUnit rule requires the test be running on a device that has been rooted. If you don't + * have a device with root you should prefer the systrace be pulled at the system level. + * + * To enable, add this rule to your test class. + * + *
+ * @Rule
+ * public EnablePerTestTraceFile mEnablePerTestTraceFile = new EnablePerTestTraceFile();
+ * 
+ */ +public class EnablePerTestTraceFile extends ExternalResource { + + private String mTestName; + private boolean aTraceInUse = false; + + @Override + public Statement apply(Statement base, Description description) { + mTestName = description.getMethodName(); + return super.apply(base, description); + } + + @Override + public void before() { + try { + ProcessBuilder builder = new ProcessBuilder(); + builder.command("atrace", "--async_start", "-a", + // NOTE: Using the android app BuildConfig specifically. + BuildConfig.APPLICATION_ID); + Process process = builder.start(); + process.waitFor(); + if (process.exitValue() == 0) { + aTraceInUse = true; + } + } catch (Exception ignored) { + // Not much we can do if atrace isn't enabled on the device. + } + } + + @Override + public void after() { + if (aTraceInUse) { + try { + ProcessBuilder builder = new ProcessBuilder(); + builder.command("atrace", "--async_stop", "-a", + // NOTE: Using the android app BuildConfig specifically. + BuildConfig.APPLICATION_ID); + Process process = builder.start(); + process.waitFor(); + } catch (Exception ignored) { + // Not much we can do if atrace isn't enabled on the device. + } + } + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePostTestDumpsys.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePostTestDumpsys.java new file mode 100644 index 0000000000..1802193c27 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnablePostTestDumpsys.java @@ -0,0 +1,105 @@ +package com.mapbox.mapboxsdk.testrule; + +import android.os.Trace; + +import com.mapbox.mapboxsdk.testapp.BuildConfig; + +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import static com.mapbox.mapboxsdk.testapp.utils.PerfTestingUtils.getTestFile; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class EnablePostTestDumpsys extends ExternalResource { + private Logger logger = Logger.getLogger(EnablePostTestDumpsys.class.getName()); + + private String mTestName; + private String mTestClass; + + @Override + public Statement apply(Statement base, Description description) { + mTestName = description.getMethodName(); + mTestClass = description.getClassName(); + return super.apply(base, description); + } + + @Override + public void before() { + try { + Runtime.getRuntime().exec("pm grant com.mapbox.mapboxsdk.testapp android.permission.DUMP"); + Runtime.getRuntime().exec("pm grant com.mapbox.mapboxsdk.testapp android.permission.READ_LOGS"); + Runtime.getRuntime().exec("pm grant com.mapbox.mapboxsdk.testapp android.permission.PACKAGE_USAGE_STATS"); + ProcessBuilder builder = new ProcessBuilder(); + builder.command("dumpsys", "gfxinfo", "--reset", + // NOTE: Using the android app BuildConfig specifically. + BuildConfig.APPLICATION_ID); + Process process = builder.start(); + process.waitFor(); + } catch (Exception exception) { + logger.log(Level.SEVERE, "Unable to reset dumpsys", exception); + } + } + + @Override + protected void after() { + if (android.os.Build.VERSION.SDK_INT >= 23) { + FileWriter fileWriter = null; + BufferedReader bufferedReader = null; + try { + Trace.beginSection("Taking Dumpsys"); + ProcessBuilder processBuilder = new ProcessBuilder(); + + // TODO: If less than API level 23 we should remove framestats. + processBuilder.command("dumpsys", "gfxinfo", + // NOTE: Using the android app BuildConfig specifically. + BuildConfig.APPLICATION_ID); + + /*processBuilder.command("dumpsys", "package", + // NOTE: Using the android app BuildConfig specifically. + BuildConfig.APPLICATION_ID, "|", "grep", "permission.DUMP");*/ + processBuilder.redirectErrorStream(true); + Process process = processBuilder.start(); + fileWriter = new FileWriter(getTestFile(mTestClass, mTestName, "gfxinfo.dumpsys" + + ".log")); + bufferedReader = new BufferedReader( + new InputStreamReader(process.getInputStream())); + String line; + while ((line = bufferedReader.readLine()) != null) { + fileWriter.append(line); + fileWriter.append(System.lineSeparator()); + } + process.waitFor(); + if (process.exitValue() != 0) { + throw new Exception("Error while taking dumpsys, exitCode=" + + process.exitValue()); + } + } catch (Exception exception) { + logger.log(Level.SEVERE, "Unable to take a dumpsys", exception); + } finally { + if (fileWriter != null) { + try { + fileWriter.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + Trace.endSection(); + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableTestTracing.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableTestTracing.java new file mode 100644 index 0000000000..aca398271d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testrule/EnableTestTracing.java @@ -0,0 +1,44 @@ +package com.mapbox.mapboxsdk.testrule; + +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). + * + *
+ * @Rule
+ * public EnableTestTracing mEnableTestTracing = new EnableTestTracing();
+ * 
+ */ +public class EnableTestTracing extends ExternalResource { + + private String mTestName; + + @Override + public Statement apply(Statement base, Description description) { + mTestName = description.getMethodName(); + return super.apply(base, description); + } + + @Override + public void before() { + if (android.os.Build.VERSION.SDK_INT >= 18) { + Trace.beginSection(mTestName); + } + } + + @Override + public void after() { + if (android.os.Build.VERSION.SDK_INT >= 18) { + Trace.endSection(); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index fb1d0ef8a2..6057d21de4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -3,6 +3,9 @@ package="com.mapbox.mapboxsdk.testapp"> + + + features1 = new ArrayList<>(); + features1.add(Feature.fromGeometry(Point.fromLngLat(10, 50))); + + List features2 = new ArrayList<>(); + features2.add(Feature.fromGeometry(Point.fromLngLat(11, 50))); + + FeatureCollection featureCollection1 = FeatureCollection.fromFeatures(features1); + FeatureCollection featureCollection2 = FeatureCollection.fromFeatures(features2); + + GeoJsonSource source1 = new GeoJsonSource("source1", featureCollection1); + GeoJsonSource source2 = new GeoJsonSource("source2", featureCollection2); + mapboxMap.addSource(source1); + mapboxMap.addSource(source2); + + Layer layer1 = new SymbolLayer("layer1", "source1").withProperties(PropertyFactory.iconImage("restaurant-15"), PropertyFactory.iconAllowOverlap(true), PropertyFactory.iconAnchor(Expression.literal(Property.ICON_ANCHOR_CENTER))); + Layer layer2 = new SymbolLayer("layer2", "source2").withProperties(PropertyFactory.iconImage("restaurant-15"), PropertyFactory.iconAllowOverlap(true), PropertyFactory.iconAnchor(Expression.literal(Property.ICON_ANCHOR_CENTER))); + mapboxMap.addLayer(layer1); + mapboxMap.addLayer(layer2); + + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + featureCollection2.features().addAll(featureCollection1.features()); + featureCollection1.features().clear(); + source1.setGeoJson(featureCollection1); + source2.setGeoJson(featureCollection2); + } + }, 5000); + /*Expression.get("imageAttribute"); + mapboxMap.addOnCameraIdleListener(() -> + Timber.e("Camera IDLE: %s, zoom: %f", mapboxMap.getCameraPosition().target, mapboxMap.getCameraPosition().zoom)); + + //crossing IDL + LatLngBounds latLngBounds = LatLngBounds.from(16.5, -172.8, -35.127709, 172.6); + LatLng ne = new LatLng(16.5, -172.8); + LatLng sw = new LatLng(-35.127709, 172.6); + + if (ne.getLongitude() < sw.getLongitude()) { + if (ne.getLongitude() < 0 && sw.getLongitude() > 0) { + sw.setLongitude(sw.getLongitude() - 360); + } else if (ne.getLongitude() > 0 && sw.getLongitude() < 0) { + sw.setLongitude(sw.getLongitude() + 360); + } + } + + LatLngBounds betterBounds = new LatLngBounds.Builder().include(ne).include(sw).build(); + CameraPosition cameraPosition = mapboxMap.getCameraForLatLngBounds(betterBounds, new int[] {0, 0, 0, 0}); + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); + //mapboxMap.setLatLngBoundsForCameraTarget(latLngBounds); + + //not crossing IDL + new Handler().postDelayed(() -> { + LatLngBounds latLngBounds1 = LatLngBounds.from(16.5, -162.8, -35.127709, -177.4); +// mapboxMap.setLatLngBoundsForCameraTarget(latLngBounds1); + CameraPosition cameraPosition1 = mapboxMap.getCameraForLatLngBounds(latLngBounds1, new int[] {0, 0, 0, 0}); + //mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition1)); + }, 2500);*/ + } + }); } @Override -- cgit v1.2.1