Index: chrome/android/javatests/src/org/chromium/chrome/browser/PopularUrlsTest.java |
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/PopularUrlsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/PopularUrlsTest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..97fc86736fcab7545cbb617ebcdc4b667a620f4f |
--- /dev/null |
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/PopularUrlsTest.java |
@@ -0,0 +1,426 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout; |
+ |
+import android.os.Environment; |
+import android.text.TextUtils; |
+import android.util.Log; |
+ |
+import org.chromium.base.annotations.SuppressFBWarnings; |
+import org.chromium.base.test.util.Manual; |
+import org.chromium.chrome.test.ChromeActivityTestCaseBase; |
+import org.chromium.content.browser.test.util.CallbackHelper; |
+import org.chromium.content_public.browser.LoadUrlParams; |
+import org.chromium.ui.base.PageTransition; |
+ |
+import java.io.BufferedReader; |
+import java.io.File; |
+import java.io.FileNotFoundException; |
+import java.io.FileReader; |
+import java.io.FileWriter; |
+import java.io.IOException; |
+import java.io.OutputStreamWriter; |
+import java.util.Iterator; |
+import java.util.LinkedList; |
+import java.util.List; |
+import java.util.concurrent.TimeUnit; |
+import java.util.concurrent.TimeoutException; |
+ |
+/** |
+ * Popular URL tests (ported from {@link com.android.browser.PopularUrlsTest}). |
+ * <p> |
+ * These tests read popular URLs from /sdcard/popular_urls.txt, open them one by one and verify |
+ * page load. When aborted, they save the last opened URL in /sdcard/test_status.txt, so that they |
+ * can continue opening the next URL when they are restarted. |
+ */ |
+public class PopularUrlsTest extends ChromeActivityTestCaseBase<ChromeActivity> { |
+ |
+ private static final String TAG = "PopularUrlsTest"; |
+ private static final String NEW_LINE = System.getProperty("line.separator"); |
+ private static final File INPUT_FILE = |
+ new File(Environment.getExternalStorageDirectory(), "popular_urls.txt"); |
+ private static final File OUTPUT_FILE = |
+ new File(Environment.getExternalStorageDirectory(), "test_output.txt"); |
+ private static final File STATUS_FILE = |
+ new File(Environment.getExternalStorageDirectory(), "test_status.txt"); |
+ private static final File FAILURE_FILE = |
+ new File(Environment.getExternalStorageDirectory(), "failures.txt"); |
+ private static final File WAIT_FLAG_FILE = |
+ new File(Environment.getExternalStorageDirectory(), "url-test-short-wait"); |
+ private static final int PERF_LOOPCOUNT = 10; |
+ private static final int STABILITY_LOOPCOUNT = 1; |
+ private static final int SHORT_WAIT_TIMEOUT = 1000; |
+ |
+ private RunStatus mStatus; |
+ private boolean mFailed; |
+ private boolean mDoShortWait; |
+ |
+ public PopularUrlsTest() { |
+ super(ChromeActivity.class); |
+ } |
+ |
+ @Override |
+ protected void setUp() throws Exception { |
+ mSkipCheckHttpServer = true; |
+ mStatus = new RunStatus(STATUS_FILE); |
+ mFailed = false; |
+ mDoShortWait = checkDoShortWait(); |
+ super.setUp(); |
+ } |
+ |
+ @Override |
+ protected void tearDown() throws Exception { |
+ if (mStatus != null) { |
+ mStatus.cleanUp(); |
+ } |
+ super.tearDown(); |
+ } |
+ |
+ @Override |
+ public void startMainActivity() throws InterruptedException { |
+ startMainActivityFromLauncher(); |
+ } |
+ |
+ private BufferedReader getInputStream(File inputFile) throws FileNotFoundException { |
+ FileReader fileReader = new FileReader(inputFile); |
+ BufferedReader bufferedReader = new BufferedReader(fileReader); |
+ |
+ return bufferedReader; |
+ } |
+ |
+ private OutputStreamWriter getOutputStream(File outputFile) throws IOException { |
+ return new FileWriter(outputFile, mStatus.getIsRecovery()); |
+ } |
+ |
+ private void logToStream(String str, OutputStreamWriter writer) throws IOException { |
+ if (writer != null) { |
+ writer.write(str); |
+ writer.flush(); |
+ } |
+ } |
+ |
+ private boolean checkDoShortWait() { |
+ return WAIT_FLAG_FILE.isFile() && WAIT_FLAG_FILE.exists(); |
+ } |
+ |
+ private static class RunStatus { |
+ private File mFile; |
+ private int mIteration; |
+ private int mPage; |
+ private String mUrl; |
+ private boolean mIsRecovery; |
+ private boolean mAllClear; |
+ |
+ public RunStatus(File file) throws IOException { |
+ mFile = file; |
+ FileReader input = null; |
+ BufferedReader reader = null; |
+ mIsRecovery = false; |
+ mAllClear = false; |
+ mIteration = 0; |
+ mPage = 0; |
+ try { |
+ input = new FileReader(mFile); |
+ mIsRecovery = true; |
+ reader = new BufferedReader(input); |
+ String line = reader.readLine(); |
+ if (line == null) { |
+ return; |
+ } |
+ mIteration = Integer.parseInt(line); |
+ line = reader.readLine(); |
+ if (line == null) { |
+ return; |
+ } |
+ mPage = Integer.parseInt(line); |
+ } catch (FileNotFoundException ex) { |
+ return; |
+ } catch (NumberFormatException nfe) { |
+ Log.wtf(TAG, "Unexpected data in status file. Will run for all URLs."); |
+ return; |
+ } finally { |
+ try { |
+ if (reader != null) { |
+ reader.close(); |
+ } |
+ } finally { |
+ if (input != null) { |
+ input.close(); |
+ } |
+ } |
+ } |
+ } |
+ |
+ @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") |
+ public void write() throws IOException { |
+ FileWriter output = null; |
+ if (mFile.exists()) { |
+ mFile.delete(); |
+ } |
+ try { |
+ output = new FileWriter(mFile); |
+ output.write(mIteration + NEW_LINE); |
+ output.write(mPage + NEW_LINE); |
+ output.write(mUrl + NEW_LINE); |
+ } finally { |
+ if (output != null) { |
+ output.close(); |
+ } |
+ } |
+ } |
+ |
+ @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") |
+ public void cleanUp() { |
+ // Only perform cleanup when mAllClear flag is set, i.e. |
+ // when the test was not interrupted by a Java crash. |
+ if (mFile.exists() && mAllClear) { |
+ mFile.delete(); |
+ } |
+ } |
+ |
+ public void resetPage() { |
+ mPage = 0; |
+ } |
+ |
+ public void incrementPage() { |
+ ++mPage; |
+ mAllClear = true; |
+ } |
+ |
+ public void incrementIteration() { |
+ ++mIteration; |
+ } |
+ |
+ public int getPage() { |
+ return mPage; |
+ } |
+ |
+ public int getIteration() { |
+ return mIteration; |
+ } |
+ |
+ public boolean getIsRecovery() { |
+ return mIsRecovery; |
+ } |
+ |
+ public void setUrl(String url) { |
+ mUrl = url; |
+ mAllClear = false; |
+ } |
+ } |
+ |
+ /** |
+ * Navigates to a URL directly without going through the UrlBar. This bypasses the page |
+ * preloading mechanism of the UrlBar. |
+ * @param url the page URL |
+ * @param failureWriter the writer where failures/crashes/timeouts are logged. |
+ * @throws IOException unable to read from input or write to writer. |
+ * @throws InterruptedException the thread was interrupted waiting for the page to load. |
+ */ |
+ public void loadUrl(final String url, OutputStreamWriter failureWriter) |
+ throws InterruptedException, IOException { |
+ Tab tab = getActivity().getActivityTab(); |
+ final CallbackHelper loadedCallback = new CallbackHelper(); |
+ final CallbackHelper failedCallback = new CallbackHelper(); |
+ final CallbackHelper crashedCallback = new CallbackHelper(); |
+ |
+ tab.addObserver(new EmptyTabObserver() { |
+ @Override |
+ public void onPageLoadFinished(Tab tab) { |
+ loadedCallback.notifyCalled(); |
+ } |
+ |
+ @Override |
+ public void onPageLoadFailed(Tab tab, int errorCode) { |
+ failedCallback.notifyCalled(); |
+ } |
+ |
+ @Override |
+ public void onCrash(Tab tab, boolean sadTabShown) { |
+ crashedCallback.notifyCalled(); |
+ } |
+ }); |
+ |
+ getInstrumentation().runOnMainSync(new Runnable() { |
+ @Override |
+ public void run() { |
+ Tab tab = getActivity().getActivityTab(); |
+ int pageTransition = PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR; |
+ tab.loadUrl(new LoadUrlParams(url, pageTransition)); |
+ } |
+ }); |
+ // There are a combination of events ordering in a failure case. |
+ // There might be TAB_CRASHED with or without PAGE_LOAD_FINISHED preceding it. |
+ // It is possible to get PAGE_LOAD_FINISHED followed by PAGE_LOAD_FAILED due to redirects. |
+ boolean timedout = false; |
+ try { |
+ loadedCallback.waitForCallback(0, 1, 2, TimeUnit.MINUTES); |
+ } catch (TimeoutException ex) { |
+ timedout = true; |
+ } |
+ |
+ boolean failed = true; |
+ boolean crashed = true; |
+ if (mDoShortWait) { |
+ try { |
+ failedCallback.waitForCallback(0, 1, SHORT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS); |
+ } catch (TimeoutException ex) { |
+ failed = false; |
+ } |
+ try { |
+ crashedCallback.waitForCallback(0, 1, SHORT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS); |
+ } catch (TimeoutException ex) { |
+ crashed = false; |
+ } |
+ } else { |
+ try { |
+ failedCallback.waitForCallback( |
+ 0, 1, scaleTimeout(100 * 1000), TimeUnit.MILLISECONDS); |
+ } catch (TimeoutException ex) { |
+ failed = false; |
+ } |
+ try { |
+ crashedCallback.waitForCallback( |
+ 0, 1, scaleTimeout(100 * 1000), TimeUnit.MILLISECONDS); |
+ } catch (TimeoutException ex) { |
+ crashed = false; |
+ } |
+ } |
+ if (crashed) { |
+ logToStream(url + " crashed!" + NEW_LINE, failureWriter); |
+ mFailed = true; |
+ } |
+ if (failed) { |
+ logToStream(url + " failed to load!" + NEW_LINE, failureWriter); |
+ mFailed = true; |
+ } |
+ if (timedout) { |
+ logToStream(url + " timed out!" + NEW_LINE, failureWriter); |
+ mFailed = true; |
+ } |
+ // Try to stop page load. |
+ getInstrumentation().runOnMainSync(new Runnable() { |
+ @Override |
+ public void run() { |
+ getActivity().getActivityTab().stopLoading(); |
+ } |
+ }); |
+ getInstrumentation().waitForIdleSync(); |
+ } |
+ |
+ /** |
+ * Loops over a list of URLs, points the browser to each one, and records the time elapsed. |
+ * |
+ * @param input the reader from which to get the URLs. |
+ * @param outputWriter the writer to which to output the results. |
+ * @param failureWriter the writer where failures/crashes/timeouts are logged. |
+ * @param clearCache determines whether the cache is cleared before loading each page. |
+ * @param loopCount the number of times to loop through the list of pages. |
+ * @throws IOException unable to read from input or write to writer. |
+ * @throws InterruptedException the thread was interrupted waiting for the page to load. |
+ */ |
+ private void loopUrls(BufferedReader input, OutputStreamWriter outputWriter, |
+ OutputStreamWriter failureWriter, boolean clearCache, int loopCount) |
+ throws IOException, InterruptedException { |
+ List<String> pages = new LinkedList<String>(); |
+ |
+ String page; |
+ while (null != (page = input.readLine())) { |
+ if (!TextUtils.isEmpty(page)) { |
+ pages.add(page); |
+ } |
+ } |
+ |
+ Iterator<String> iterator = pages.iterator(); |
+ for (int i = 0; i < mStatus.getPage(); ++i) { |
+ iterator.next(); |
+ } |
+ |
+ if (mStatus.getIsRecovery()) { |
+ Log.e(TAG, "Recovering after crash: " + iterator.next()); |
+ mStatus.incrementPage(); |
+ } |
+ |
+ while (mStatus.getIteration() < loopCount) { |
+ if (clearCache) { |
+ // TODO(jingzhao): Clear cache before loading the URL. |
+ } |
+ while (iterator.hasNext()) { |
+ page = iterator.next(); |
+ mStatus.setUrl(page); |
+ mStatus.write(); |
+ Log.i(TAG, "Start: " + page); |
+ |
+ long startTime = System.currentTimeMillis(); |
+ loadUrl(page, failureWriter); |
+ long stopTime = System.currentTimeMillis(); |
+ |
+ String currentUrl = getActivity().getActivityTab().getUrl(); |
+ Log.i(TAG, "Finish: " + currentUrl); |
+ logToStream(page + "|" + (stopTime - startTime) + NEW_LINE, outputWriter); |
+ mStatus.incrementPage(); |
+ } |
+ mStatus.incrementIteration(); |
+ mStatus.resetPage(); |
+ iterator = pages.iterator(); |
+ } |
+ } |
+ |
+ /** |
+ * Navigate to all the pages listed in the input. |
+ * @param perf Whether this is a performance test or stability test. |
+ * @throws IOException |
+ * @throws InterruptedException |
+ */ |
+ public void loadPages(boolean perf) throws IOException, InterruptedException { |
+ OutputStreamWriter outputWriter = null; |
+ if (perf) { |
+ outputWriter = getOutputStream(OUTPUT_FILE); |
+ } |
+ OutputStreamWriter failureWriter = getOutputStream(FAILURE_FILE); |
+ try { |
+ BufferedReader bufferedReader = getInputStream(INPUT_FILE); |
+ int loopCount = perf ? PERF_LOOPCOUNT : STABILITY_LOOPCOUNT; |
+ try { |
+ loopUrls(bufferedReader, outputWriter, failureWriter, true, loopCount); |
+ assertFalse( |
+ String.format("Failed to load all pages. Take a look at %s", FAILURE_FILE), |
+ mFailed); |
+ } finally { |
+ if (bufferedReader != null) { |
+ bufferedReader.close(); |
+ } |
+ } |
+ } catch (FileNotFoundException fnfe) { |
+ Log.e(TAG, fnfe.getMessage(), fnfe); |
+ fail(String.format("URL file %s is not found.", INPUT_FILE)); |
+ } finally { |
+ if (outputWriter != null) { |
+ outputWriter.close(); |
+ } |
+ if (failureWriter != null) { |
+ failureWriter.close(); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Repeats loading all URLs by PERF_LOOPCOUNT times, and records the time each load takes. |
+ */ |
+ @Manual |
+ public void testLoadPerformance() throws IOException, InterruptedException { |
+ loadPages(true); |
+ } |
+ |
+ /** |
+ * Loads all URLs. |
+ */ |
+ @Manual |
+ public void testStability() throws IOException, InterruptedException { |
+ loadPages(false); |
+ } |
+} |