1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.browser; 18 19 import android.content.Intent; 20 import android.content.pm.PackageManager; 21 import android.cts.util.WatchDog; 22 import android.net.Uri; 23 import android.provider.Browser; 24 import android.util.Log; 25 import android.webkit.cts.CtsTestServer; 26 27 import android.cts.util.CtsAndroidTestCase; 28 import com.android.cts.util.ResultType; 29 import com.android.cts.util.ResultUnit; 30 import com.android.cts.util.Stat; 31 import com.android.cts.util.TimeoutReq; 32 33 import java.net.URLDecoder; 34 import java.util.LinkedHashMap; 35 import java.util.Map; 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.TimeUnit; 38 import java.util.regex.Matcher; 39 import java.util.regex.Pattern; 40 41 import org.apache.http.HttpRequest; 42 import org.apache.http.HttpResponse; 43 import org.apache.http.RequestLine; 44 /** 45 * Browser benchmarking. 46 * It launches an activity with URL and wait for POST from the client. 47 */ 48 public class BrowserBenchTest extends CtsAndroidTestCase { 49 private static final String TAG = BrowserBenchTest.class.getSimpleName(); 50 private static final boolean DEBUG = false; 51 private static final String OCTANE_START_FILE = "octane/index.html"; 52 private static final String ROBOHORNET_START_FILE = "robohornet/robohornet.html"; 53 private static final String HOST_COMPLETION_BROADCAST = "com.android.cts.browser.completion"; 54 // time-out for watch-dog. POST should happen within this time. 55 private static long BROWSER_POST_TIMEOUT_IN_MS = 10 * 60 * 1000L; 56 // watch-dog will time-out first. So make it long enough. 57 private static long BROWSER_COMPLETION_TIMEOUT_IN_MS = 60 * 60 * 1000L; 58 private static final String HTTP_USER_AGENT = "User-Agent"; 59 private CtsTestServer mWebServer; 60 // used for final score 61 private ResultType mTypeNonFinal = ResultType.NEUTRAL; 62 private ResultUnit mUnitNonFinal = ResultUnit.NONE; 63 // used for all other scores 64 private ResultType mTypeFinal = ResultType.NEUTRAL; 65 private ResultUnit mUnitFinal = ResultUnit.SCORE; 66 private WatchDog mWatchDog; 67 private CountDownLatch mLatch; 68 // can be changed by each test before starting 69 private volatile int mNumberRepeat; 70 /** tells how many tests have run up to now */ 71 private volatile int mRunIndex; 72 /** stores results for each runs. last entry will be the final score. */ 73 private LinkedHashMap<String, double[]> mResultsMap; 74 private PackageManager mPackageManager; 75 76 @Override setUp()77 protected void setUp() throws Exception { 78 super.setUp(); 79 mPackageManager = getInstrumentation().getContext().getPackageManager(); 80 mWebServer = new CtsTestServer(getContext()) { 81 @Override 82 protected HttpResponse onPost(HttpRequest request) throws Exception { 83 // post uri will look like "cts_report.html?final=1&score=10.1&message=hello" 84 RequestLine requestLine = request.getRequestLine(); 85 String uriString = URLDecoder.decode(requestLine.getUri(), "UTF-8"); 86 if (DEBUG) { 87 Log.i(TAG, "uri:" + uriString); 88 } 89 String resultRe = 90 ".*cts_report.html\\?final=([\\d])&score=([\\d]+\\.?[\\d]*)&message=([\\w][\\w ]*)"; 91 Pattern resultPattern = Pattern.compile(resultRe); 92 Matcher matchResult = resultPattern.matcher(uriString); 93 if (matchResult.find()) { 94 int isFinal = Integer.parseInt(matchResult.group(1)); 95 double score = Double.parseDouble(matchResult.group(2)); 96 String message = matchResult.group(3); 97 Log.i(TAG, message + ":" + score); 98 if (!mResultsMap.containsKey(message)) { 99 mResultsMap.put(message, new double[mNumberRepeat]); 100 } 101 double[] scores = mResultsMap.get(message); 102 scores[mRunIndex] = score; 103 if (isFinal == 1) { 104 String userAgent = request.getFirstHeader(HTTP_USER_AGENT).getValue(); 105 getReportLog().printValue(HTTP_USER_AGENT + "=" + userAgent, 0, 106 ResultType.NEUTRAL, ResultUnit.NONE); 107 mLatch.countDown(); 108 } 109 mWatchDog.reset(); 110 } 111 return null; // default response is OK as it will be ignored by client anyway. 112 } 113 }; 114 mResultsMap = new LinkedHashMap<String, double[]>(); 115 mWatchDog = new WatchDog(BROWSER_POST_TIMEOUT_IN_MS); 116 mWatchDog.start(); 117 } 118 119 @Override tearDown()120 protected void tearDown() throws Exception { 121 mWatchDog.stop(); 122 mWebServer.shutdown(); 123 mWebServer = null; 124 mResultsMap = null; 125 super.tearDown(); 126 } 127 128 @TimeoutReq(minutes = 60) testOctane()129 public void testOctane() throws InterruptedException { 130 if (!isBrowserSupported()) { 131 Log.i(TAG, "Skipping test for device with no supported browser"); 132 return; 133 } 134 String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1"; 135 final int kRepeat = 5; 136 doTest(url, ResultType.LOWER_BETTER, ResultUnit.MS, 137 ResultType.HIGHER_BETTER, ResultUnit.SCORE, kRepeat); 138 } 139 doTest(String url, ResultType typeNonFinal, ResultUnit unitNonFinal, ResultType typeFinal, ResultUnit unitFinal, int numberRepeat)140 private void doTest(String url, ResultType typeNonFinal, ResultUnit unitNonFinal, 141 ResultType typeFinal, ResultUnit unitFinal, int numberRepeat) 142 throws InterruptedException { 143 mTypeNonFinal = typeNonFinal; 144 mUnitNonFinal = unitNonFinal; 145 mTypeFinal = typeFinal; 146 mUnitFinal = unitFinal; 147 mNumberRepeat = numberRepeat; 148 Uri uri = Uri.parse(url); 149 for (mRunIndex = 0; mRunIndex < numberRepeat; mRunIndex++) { 150 Log.i(TAG, mRunIndex + "-th round"); 151 mLatch = new CountDownLatch(1); 152 Intent intent = new Intent(Intent.ACTION_VIEW, uri); 153 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 154 // force using only one window or tab 155 intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); 156 getContext().startActivity(intent); 157 boolean ok = mLatch.await(BROWSER_COMPLETION_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); 158 assertTrue("timed-out", ok); 159 } 160 // it is somewhat awkward to handle the last one specially with Map 161 int numberEntries = mResultsMap.size(); 162 int numberToProcess = 1; 163 for (Map.Entry<String, double[]> entry : mResultsMap.entrySet()) { 164 String message = entry.getKey(); 165 double[] scores = entry.getValue(); 166 if (numberToProcess == numberEntries) { // final score 167 // store the whole results first 168 getReportLog().printArray(message, scores, mTypeFinal, mUnitFinal); 169 getReportLog().printSummary(message, Stat.getAverage(scores), mTypeFinal, 170 mUnitFinal); 171 } else { // interim results 172 getReportLog().printArray(message, scores, mTypeNonFinal, mUnitNonFinal); 173 } 174 numberToProcess++; 175 } 176 } 177 178 /** 179 * @return true iff this device is has a working browser. 180 */ isBrowserSupported()181 private boolean isBrowserSupported() { 182 return !(mPackageManager.hasSystemFeature("android.hardware.type.television") 183 || mPackageManager.hasSystemFeature("android.software.leanback") 184 || mPackageManager.hasSystemFeature("android.hardware.type.watch")); 185 } 186 } 187