/* * Copyright (C) 2011 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.test.tilebenchmark; import com.test.tilebenchmark.ProfileActivity.ProfileCallback; import java.io.File; import java.util.HashMap; import java.util.Map; import android.content.res.Resources; import android.os.Bundle; import android.os.Environment; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import android.webkit.WebSettings; import android.widget.Spinner; public class PerformanceTest extends ActivityInstrumentationTestCase2<ProfileActivity> { public static class AnimStat { double aggVal = 0; double aggSqrVal = 0; double count = 0; } private class StatAggregator extends PlaybackGraphs { private HashMap<String, Double> mDataMap = new HashMap<String, Double>(); private HashMap<String, AnimStat> mAnimDataMap = new HashMap<String, AnimStat>(); private int mCount = 0; public void aggregate() { boolean inAnimTests = mAnimTests != null; Resources resources = mWeb.getResources(); String animFramerateString = resources.getString(R.string.animation_framerate); for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { String name = e.getKey(); if (inAnimTests) { if (name.equals(animFramerateString)) { // in animation testing phase, record animation framerate and aggregate // stats, differentiating on values of mAnimTestNr and mDoubleBuffering String fullName = ANIM_TEST_NAMES[mAnimTestNr] + " " + name; fullName += mDoubleBuffering ? " tiled" : " webkit"; if (!mAnimDataMap.containsKey(fullName)) { mAnimDataMap.put(fullName, new AnimStat()); } AnimStat statVals = mAnimDataMap.get(fullName); statVals.aggVal += e.getValue(); statVals.aggSqrVal += e.getValue() * e.getValue(); statVals.count += 1; } } else { double aggVal = mDataMap.containsKey(name) ? mDataMap.get(name) : 0; mDataMap.put(name, aggVal + e.getValue()); } } if (inAnimTests) { return; } mCount++; for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { for (int statIndex = 0; statIndex < Stats.length; statIndex++) { String metricLabel = resources.getString( Metrics[metricIndex].getLabelId()); String statLabel = resources.getString( Stats[statIndex].getLabelId()); String label = metricLabel + " " + statLabel; double aggVal = mDataMap.containsKey(label) ? mDataMap .get(label) : 0; aggVal += mStats[metricIndex][statIndex]; mDataMap.put(label, aggVal); } } } // build the final bundle of results public Bundle getBundle() { Bundle b = new Bundle(); int count = (0 == mCount) ? Integer.MAX_VALUE : mCount; for (Map.Entry<String, Double> e : mDataMap.entrySet()) { b.putDouble(e.getKey(), e.getValue() / count); } for (Map.Entry<String, AnimStat> e : mAnimDataMap.entrySet()) { String statName = e.getKey(); AnimStat statVals = e.getValue(); double avg = statVals.aggVal/statVals.count; double stdDev = Math.sqrt((statVals.aggSqrVal / statVals.count) - avg * avg); b.putDouble(statName, avg); b.putDouble(statName + " STD DEV", stdDev); } return b; } } ProfileActivity mActivity; ProfiledWebView mWeb; Spinner mMovementSpinner; StatAggregator mStats; private static final String LOGTAG = "PerformanceTest"; private static final String TEST_LOCATION = "webkit/page_cycler"; private static final String URL_PREFIX = "file://"; private static final String URL_POSTFIX = "/index.html?skip=true"; private static final int MAX_ITERATIONS = 4; private static final String SCROLL_TEST_DIRS[] = { "alexa25_2011" }; private static final String ANIM_TEST_DIRS[] = { "dhtml" }; public PerformanceTest() { super(ProfileActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); mActivity = getActivity(); mWeb = (ProfiledWebView) mActivity.findViewById(R.id.web); mMovementSpinner = (Spinner) mActivity.findViewById(R.id.movement); mStats = new StatAggregator(); // use mStats as a condition variable between the UI thread and // this(the testing) thread mActivity.setCallback(new ProfileCallback() { @Override public void profileCallback(RunData data) { mStats.setData(data); synchronized (mStats) { mStats.notify(); } } }); } private boolean loadUrl(final String url) { try { Log.d(LOGTAG, "test starting for url " + url); mActivity.runOnUiThread(new Runnable() { @Override public void run() { mWeb.loadUrl(url); } }); synchronized (mStats) { mStats.wait(); } mStats.aggregate(); } catch (InterruptedException e) { e.printStackTrace(); return false; } return true; } private boolean validTest(String nextTest) { // if testing animations, test must be in mAnimTests if (mAnimTests == null) return true; for (String test : mAnimTests) { if (test.equals(nextTest)) { return true; } } return false; } private boolean runIteration(String[] testDirs) { File sdFile = Environment.getExternalStorageDirectory(); for (String testDirName : testDirs) { File testDir = new File(sdFile, TEST_LOCATION + "/" + testDirName); Log.d(LOGTAG, "Testing dir: '" + testDir.getAbsolutePath() + "', exists=" + testDir.exists()); for (File siteDir : testDir.listFiles()) { if (!siteDir.isDirectory() || !validTest(siteDir.getName())) { continue; } if (!loadUrl(URL_PREFIX + siteDir.getAbsolutePath() + URL_POSTFIX)) { return false; } } } return true; } private boolean runTestDirs(String[] testDirs) { for (int i = 0; i < MAX_ITERATIONS; i++) if (!runIteration(testDirs)) { return false; } return true; } private void pushDoubleBuffering() { getInstrumentation().runOnMainSync(new Runnable() { public void run() { mWeb.setDoubleBuffering(mDoubleBuffering); } }); } private void setScrollingTestingMode(final boolean scrolled) { getInstrumentation().runOnMainSync(new Runnable() { public void run() { mMovementSpinner.setSelection(scrolled ? 0 : 2); } }); } private String[] mAnimTests = null; private int mAnimTestNr = -1; private boolean mDoubleBuffering = true; private static final String[] ANIM_TEST_NAMES = { "slow", "fast" }; private static final String[][] ANIM_TESTS = { {"scrolling", "replaceimages", "layers5", "layers1"}, {"slidingballs", "meter", "slidein", "fadespacing", "colorfade", "mozilla", "movingtext", "diagball", "zoom", "imageslide"}, }; private boolean checkMedia() { String state = Environment.getExternalStorageState(); if (!Environment.MEDIA_MOUNTED.equals(state) && !Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { Log.d(LOGTAG, "ARG Can't access sd card!"); // Can't read the SD card, fail and die! getInstrumentation().sendStatus(1, null); return false; } return true; } public void testMetrics() { setScrollingTestingMode(true); if (checkMedia() && runTestDirs(SCROLL_TEST_DIRS)) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } public void testMetricsMinimalMemory() { mActivity.runOnUiThread(new Runnable() { @Override public void run() { mWeb.setUseMinimalMemory(true); } }); setScrollingTestingMode(true); if (checkMedia() && runTestDirs(SCROLL_TEST_DIRS)) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } private boolean runAnimationTests() { for (int doubleBuffer = 0; doubleBuffer <= 1; doubleBuffer++) { mDoubleBuffering = doubleBuffer == 1; pushDoubleBuffering(); for (mAnimTestNr = 0; mAnimTestNr < ANIM_TESTS.length; mAnimTestNr++) { mAnimTests = ANIM_TESTS[mAnimTestNr]; if (!runTestDirs(ANIM_TEST_DIRS)) { return false; } } } return true; } public void testAnimations() { // instead of autoscrolling, load each page until either an timer fires, // or the animation signals complete via javascript setScrollingTestingMode(false); if (checkMedia() && runAnimationTests()) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } }