/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.browser.tests; import com.android.ddmlib.IDevice; import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; import com.android.tradefed.config.Option; import com.android.tradefed.config.Option.Importance; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.invoker.ITestInvocation; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.result.CollectingTestListener; import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.testtype.IDeviceTest; import com.android.tradefed.testtype.IRemoteTest; import java.util.HashMap; import java.util.Map; /** * Base test harness class for browser site load test. * * The class can be configured to run Android Browser stability test with a preconfigured list * of sites pushed into ${EXTERNAL_STORAGE}/popular_urls.txt * */ public class BrowserSiteLoadTest implements IRemoteTest, IDeviceTest { private static final String URLS_FILE_NAME = "popular_urls.txt"; private static final String STATUS_FILE_NAME = "test_status.txt"; private static final String TEST_CLASS_NAME = "com.android.browser.PopularUrlsTest"; private static final String TEST_METHOD_NAME = "testStability"; private static final String TEST_PACKAGE_NAME = "com.android.browser.tests"; private static final String TEST_RUNNER_NAME = "android.test.InstrumentationTestRunner"; private static final int MAX_TIMEOUT_MS = 8 * 60 * 60 * 1000; // max test timeout is 8 hrs private ITestDevice mTestDevice = null; @Option(name = "metrics-name", description = "name used to identify the metrics for reporting", importance = Importance.ALWAYS) private String mMetricsName; @Option(name = "schema-key", description = "the schema key that number of successful loads should be reported under", importance = Importance.ALWAYS) private String mSchemaKey; @Option(name = "test-package", description = "package name of the stability test. defaults to that of AOSP Browser") private String mTestPackage = TEST_PACKAGE_NAME; @Option(name = "test-class", description = "class name of the stability test. defaults to that of AOSP Browser") private String mTestClass = TEST_CLASS_NAME; @Option(name = "test-method", description = "method name of the stability test. defaults to that of AOSP Browser") private String mTestMethod = TEST_METHOD_NAME; @Option(name = "total-sites", description = "expected total number of site loads") private int mTotalSites = 0; private String mUrlsFilePath; private String mStatusFilePath; /** * {@inheritDoc} */ @Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { int failCounter = 0; Map<String, String> finalMetrics = new HashMap<String, String>(); preTestSetup(); // Create and config runner for instrumentation test IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mTestPackage, TEST_RUNNER_NAME, mTestDevice.getIDevice()); runner.setClassName(mTestClass); runner.setMethodName(mTestClass, mTestMethod); // max timeout is the smaller of: 8 hrs or 1 minute per site runner.setMaxtimeToOutputResponse(Math.min(MAX_TIMEOUT_MS, 60 * 1000 * mTotalSites)); CollectingTestListener collectingTestListener = new CollectingTestListener(); failCounter = runStabilityTest(runner, collectingTestListener); finalMetrics.put(mSchemaKey, Integer.toString(mTotalSites - failCounter)); reportMetrics(listener, mMetricsName, finalMetrics); } /** * {@inheritDoc} */ @Override public ITestDevice getDevice() { return mTestDevice; } /** * {@inheritDoc} */ @Override public void setDevice(ITestDevice device) { mTestDevice = device; } /** * Wipes the device's external memory of test collateral from prior runs. * * @throws DeviceNotAvailableException If the device is unavailable or * something happened while deleting files */ private void preTestSetup() throws DeviceNotAvailableException { String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); mUrlsFilePath = String.format("%s/%s", extStore, URLS_FILE_NAME); mStatusFilePath = String.format("%s/%s", extStore, STATUS_FILE_NAME); if (!mTestDevice.doesFileExist(mUrlsFilePath)) { throw new RuntimeException("missing URL list file at: " + mUrlsFilePath); } mTestDevice.executeShellCommand("rm " + mStatusFilePath); } /** * Report run metrics by creating an empty test run to stick them in. * * @param listener The {@link ITestInvocation} of test results * @param runName The test name * @param metrics The {@link Map} that contains metrics for the given test */ private void reportMetrics(ITestInvocationListener listener, String runName, Map<String, String> metrics) { CLog.i(String.format("About to report metrics: loaded=%s", metrics.get(mSchemaKey))); listener.testRunStarted(runName, 0); listener.testRunEnded(0, metrics); } /** * Helper method for iterating through popular_urls.txt and reporting back * how many pages fail to load. * * @param runner The {@link IRemoteAndroidTestRunner} for instrumentation * @param listener The {@link CollectingTestListener} of test results * @return Number of pages that failed to load */ private int runStabilityTest(IRemoteAndroidTestRunner runner, CollectingTestListener listener) throws DeviceNotAvailableException { boolean exitConditionMet = false; int failureCounter = 0; while (!exitConditionMet) { mTestDevice.runInstrumentationTests(runner, listener); // if the status file exists, then the previous instrumentation has crashed if (!mTestDevice.doesFileExist(mStatusFilePath)) { exitConditionMet = true; } else { ++failureCounter; } } return failureCounter; } }