jenkins-bot has submitted this change and it was merged. Change subject: Hygiene: Add page load performance test ......................................................................
Hygiene: Add page load performance test We can't improve what we don't measure. I'm adding some performance tests to see how long loading a few pages on testwiki takes. It's good to have those tests added before we significantly change the page load code further. For serious testing increase the NUM_RUNS to a higher value. Also to compare apples to apples it's imperative to test on the same devices, ideally with the same network condition. (Ok, the latter is not really exactly controlled. But as long as we're talking about a stable broadband connection it should be good enough. The alternative would be mocking responses from a Mock MediaWiki server and injecting code to use that instead of the real servers.) Introduced a callback for page load completion which is currently only used for this performance test but could be used for other things in the future. Renamed another test to make it clearer that it's testing SectionFetchTask directly. Bug: T109246 Change-Id: Ie5f8494da952cfdaa753a5e4e7a9f826599a1409 --- R app/src/androidTest/java/org/wikipedia/test/SectionsFetchTaskTests.java A app/src/androidTest/java/org/wikipedia/test/perf/MeasurementController.java A app/src/androidTest/java/org/wikipedia/test/perf/PageLoadPerformanceTests.java M app/src/main/java/org/wikipedia/page/PageFragment.java A app/src/main/java/org/wikipedia/page/PageLoadCallbacks.java 5 files changed, 203 insertions(+), 2 deletions(-) Approvals: Sniedzielski: Looks good to me, approved Niedzielski: Looks good to me, but someone else must approve jenkins-bot: Verified diff --git a/app/src/androidTest/java/org/wikipedia/test/PageFetchTaskTests.java b/app/src/androidTest/java/org/wikipedia/test/SectionsFetchTaskTests.java similarity index 94% rename from app/src/androidTest/java/org/wikipedia/test/PageFetchTaskTests.java rename to app/src/androidTest/java/org/wikipedia/test/SectionsFetchTaskTests.java index 717dfa0..504cbe8 100644 --- a/app/src/androidTest/java/org/wikipedia/test/PageFetchTaskTests.java +++ b/app/src/androidTest/java/org/wikipedia/test/SectionsFetchTaskTests.java @@ -13,10 +13,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -public class PageFetchTaskTests extends ActivityUnitTestCase<TestDummyActivity> { +public class SectionsFetchTaskTests extends ActivityUnitTestCase<TestDummyActivity> { private static final int TASK_COMPLETION_TIMEOUT = 20000; - public PageFetchTaskTests() { + public SectionsFetchTaskTests() { super(TestDummyActivity.class); } diff --git a/app/src/androidTest/java/org/wikipedia/test/perf/MeasurementController.java b/app/src/androidTest/java/org/wikipedia/test/perf/MeasurementController.java new file mode 100644 index 0000000..f8ccbaf --- /dev/null +++ b/app/src/androidTest/java/org/wikipedia/test/perf/MeasurementController.java @@ -0,0 +1,94 @@ +package org.wikipedia.test.perf; + +import org.wikipedia.util.log.L; + +import android.os.SystemClock; +import android.support.v4.util.ArrayMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * A simple performance test measurement collection mechanism for Android. + */ +public class MeasurementController { + private final Map<String, MeasurementSeries> seriesMap = new ArrayMap<>(); + + public void start(String key) { + MeasurementSeries measurementSeries = seriesMap.get(key); + if (measurementSeries == null) { + measurementSeries = new MeasurementSeries(); + seriesMap.put(key, measurementSeries); + } + if (measurementSeries.currentStart > 0) { + L.w("Overwriting currentStart of " + measurementSeries.currentStart); + } + L.v("Start(" + key + ")"); + measurementSeries.currentStart = SystemClock.elapsedRealtime(); + } + + public void stop(String key) { + MeasurementSeries measurementSeries = seriesMap.get(key); + if (measurementSeries == null) { + throw new IllegalStateException("Stop called without start"); + } + + long duration = SystemClock.elapsedRealtime() - measurementSeries.currentStart; + measurementSeries.currentStart = 0L; + measurementSeries.measurements.add(duration); + + L.v("Duration(" + key + ") = " + MeasurementSeries.toMillisecondString(duration)); + } + + public void analyzeAll() { + L.i("---"); + for (String key : seriesMap.keySet()) { + analyze(key); + } + L.i("---"); + } + + private void analyze(String key) { + L.i(key + ": " + seriesMap.get(key).analyze()); + } + + /** + * One series of similar tests which can be aggregated + */ + public static class MeasurementSeries { + private List<Long> measurements = new ArrayList<>(); + private long currentStart = 0L; + + public String analyze() { + long min = Long.MAX_VALUE; + long max = Long.MIN_VALUE; + long sum = 0L; + + if (measurements.isEmpty()) { + return "No measurements to analyze"; + } + + for (long current : measurements) { + if (current < min) { + min = current; + } + if (current > max) { + max = current; + } + sum += current; + } + + String res = toMillisecondString(sum / measurements.size()); + res += "(n: " + measurements.size() + + "; min: " + toMillisecondString(min) + + "; max: " + toMillisecondString(max) + + ")"; + return res; + } + + static String toMillisecondString(long value) { + return value + "ms"; + } + } +} diff --git a/app/src/androidTest/java/org/wikipedia/test/perf/PageLoadPerformanceTests.java b/app/src/androidTest/java/org/wikipedia/test/perf/PageLoadPerformanceTests.java new file mode 100644 index 0000000..591fc34 --- /dev/null +++ b/app/src/androidTest/java/org/wikipedia/test/perf/PageLoadPerformanceTests.java @@ -0,0 +1,85 @@ +package org.wikipedia.test.perf; + +import org.wikipedia.Site; +import org.wikipedia.history.HistoryEntry; +import org.wikipedia.page.PageActivity; +import org.wikipedia.page.PageFragment; +import org.wikipedia.page.PageLoadCallbacks; +import org.wikipedia.page.PageTitle; + +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test performance of page loading. Update the NUM_RUNS for better statistical significance. + */ +@LargeTest +public class PageLoadPerformanceTests extends ActivityInstrumentationTestCase2<PageActivity> { + private static final int TASK_COMPLETION_TIMEOUT = 30000; + private static final Site SITE = new Site("test.wikipedia.org"); + private static final int NUM_RUNS = 1; //50; + private PageActivity activity; + private CountDownLatch completionLatch; + private PageFragment fragment; + private String title; + private MeasurementController measurement = new MeasurementController(); + + private PageLoadCallbacks callback = new PageLoadCallbacks() { + @Override + public void onLoadComplete() { + measurement.stop(title); + completionLatch.countDown(); + } + }; + + public PageLoadPerformanceTests() { + super(PageActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + activity = getActivity(); + } + + public void testLoadPages() throws Throwable { + testLoadPage("Test_page_for_app_testing/Section1"); + testLoadPage("A_long_page"); + testLoadPage("Barack_Obama"); // much longer than previous pages, has a lead image + + measurement.analyzeAll(); + } + + private void testLoadPage(String myTitle) throws Throwable { + title = myTitle; + for (int i = 0; i < NUM_RUNS; i++) { + loadPageUi(); + } + } + + private void loadPageUi() throws Throwable { + completionLatch = new CountDownLatch(1); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + fragment = (PageFragment) activity.getTopFragment(); + fragment.setPageLoadCallbacks(callback); + + measurement.start(title); + loadPage(); + } + }); + assertTrue(completionLatch.await(TASK_COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS)); + } + + private void loadPage() { + PageTitle pageTitle = new PageTitle(null, title, SITE); + fragment.displayNewPage(pageTitle, + new HistoryEntry(pageTitle, HistoryEntry.SOURCE_RANDOM), + false, + false); + } +} diff --git a/app/src/main/java/org/wikipedia/page/PageFragment.java b/app/src/main/java/org/wikipedia/page/PageFragment.java index 1601878..fdbde74 100755 --- a/app/src/main/java/org/wikipedia/page/PageFragment.java +++ b/app/src/main/java/org/wikipedia/page/PageFragment.java @@ -48,6 +48,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; @@ -168,6 +169,9 @@ toggleToC(TOC_ACTION_TOGGLE); } }; + + @Nullable + private PageLoadCallbacks pageLoadCallbacks; public ObservableWebView getWebView() { return webView; @@ -826,6 +830,10 @@ checkAndShowSelectTextOnboarding(); updateNavDrawerSelection(); + + if (pageLoadCallbacks != null) { + pageLoadCallbacks.onLoadComplete(); + } } public PageTitle adjustPageTitleFromMobileview(PageTitle title, JSONObject mobileView) @@ -1064,6 +1072,11 @@ return (PageActivity) getActivity(); } + @VisibleForTesting + public void setPageLoadCallbacks(@Nullable PageLoadCallbacks pageLoadCallbacks) { + this.pageLoadCallbacks = pageLoadCallbacks; + } + private class LongPressHandler extends PageActivityLongPressHandler implements PageLongPressHandler.WebViewContextMenuListener { public LongPressHandler(@NonNull PageActivity activity) { diff --git a/app/src/main/java/org/wikipedia/page/PageLoadCallbacks.java b/app/src/main/java/org/wikipedia/page/PageLoadCallbacks.java new file mode 100644 index 0000000..af440eb --- /dev/null +++ b/app/src/main/java/org/wikipedia/page/PageLoadCallbacks.java @@ -0,0 +1,9 @@ +package org.wikipedia.page; + +/** + * Callback methods for page load state feedback + */ +public interface PageLoadCallbacks { + /** Called when page has finished loading */ + void onLoadComplete(); +} -- To view, visit https://gerrit.wikimedia.org/r/232681 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ie5f8494da952cfdaa753a5e4e7a9f826599a1409 Gerrit-PatchSet: 7 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: BearND <bsitzm...@wikimedia.org> Gerrit-Reviewer: BearND <bsitzm...@wikimedia.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: Dbrant <dbr...@wikimedia.org> Gerrit-Reviewer: Mholloway <mhollo...@wikimedia.org> Gerrit-Reviewer: Niedzielski <sniedziel...@wikimedia.org> Gerrit-Reviewer: Ori.livneh <o...@wikimedia.org> Gerrit-Reviewer: Sniedzielski <sniedziel...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits