1/* 2 * Copyright (C) 2010 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 17package com.android.browser; 18 19import java.io.BufferedReader; 20import java.io.File; 21import java.io.FileNotFoundException; 22import java.io.FileReader; 23import java.io.FileWriter; 24import java.io.IOException; 25import java.io.OutputStreamWriter; 26import java.util.Iterator; 27import java.util.LinkedList; 28import java.util.List; 29import java.util.concurrent.CountDownLatch; 30import java.util.concurrent.TimeUnit; 31 32import android.app.Instrumentation; 33import android.content.Intent; 34import android.net.Uri; 35import android.net.http.SslError; 36import android.os.Environment; 37import android.test.ActivityInstrumentationTestCase2; 38import android.util.Log; 39import android.webkit.HttpAuthHandler; 40import android.webkit.JsPromptResult; 41import android.webkit.JsResult; 42import android.webkit.SslErrorHandler; 43import android.webkit.WebView; 44 45/** 46 * 47 * Iterates over a list of URLs from a file and outputs the time to load each. 48 */ 49public class PopularUrlsTest extends ActivityInstrumentationTestCase2<BrowserActivity> { 50 51 private final static String TAG = "PopularUrlsTest"; 52 private final static String newLine = System.getProperty("line.separator"); 53 private final static String sInputFile = "popular_urls.txt"; 54 private final static String sOutputFile = "test_output.txt"; 55 private final static String sStatusFile = "test_status.txt"; 56 private final static File sExternalStorage = Environment.getExternalStorageDirectory(); 57 58 private final static int PERF_LOOPCOUNT = 10; 59 private final static int STABILITY_LOOPCOUNT = 1; 60 private final static int PAGE_LOAD_TIMEOUT = 120000; // 2 minutes 61 62 private BrowserActivity mActivity = null; 63 private Instrumentation mInst = null; 64 private CountDownLatch mLatch = new CountDownLatch(1); 65 private RunStatus mStatus; 66 67 public PopularUrlsTest() { 68 super(BrowserActivity.class); 69 } 70 71 @Override 72 protected void setUp() throws Exception { 73 super.setUp(); 74 75 mActivity = getActivity(); 76 mInst = getInstrumentation(); 77 mInst.waitForIdleSync(); 78 79 mStatus = RunStatus.load(); 80 } 81 82 @Override 83 protected void tearDown() throws Exception { 84 if (mStatus != null) { 85 mStatus.cleanUp(); 86 } 87 88 super.tearDown(); 89 } 90 91 static BufferedReader getInputStream() throws FileNotFoundException { 92 return getInputStream(sInputFile); 93 } 94 95 static BufferedReader getInputStream(String inputFile) throws FileNotFoundException { 96 String path = sExternalStorage + File.separator + inputFile; 97 FileReader fileReader = new FileReader(path); 98 BufferedReader bufferedReader = new BufferedReader(fileReader); 99 100 return bufferedReader; 101 } 102 103 OutputStreamWriter getOutputStream() throws IOException { 104 return getOutputStream(sOutputFile); 105 } 106 107 OutputStreamWriter getOutputStream(String outputFile) throws IOException { 108 String path = sExternalStorage + File.separator + outputFile; 109 110 File file = new File(path); 111 112 return new FileWriter(file, mStatus.getIsRecovery()); 113 } 114 115 /** 116 * Gets the browser ready for testing by starting the application 117 * and wrapping the WebView's helper clients. 118 */ 119 void setUpBrowser() { 120 Tab tab = mActivity.getTabControl().getCurrentTab(); 121 WebView webView = tab.getWebView(); 122 123 webView.setWebChromeClient(new TestWebChromeClient(webView.getWebChromeClient()) { 124 125 /** 126 * Reset the latch whenever page progress reaches 100%. 127 */ 128 @Override 129 public void onProgressChanged(WebView view, int newProgress) { 130 super.onProgressChanged(view, newProgress); 131 if (newProgress >= 100) { 132 resetLatch(); 133 } 134 } 135 136 /** 137 * Dismisses and logs Javascript alerts. 138 */ 139 @Override 140 public boolean onJsAlert(WebView view, String url, String message, 141 JsResult result) { 142 String logMsg = String.format("JS Alert '%s' received from %s", message, url); 143 Log.w(TAG, logMsg); 144 result.confirm(); 145 146 return true; 147 } 148 149 /** 150 * Confirms and logs Javascript alerts. 151 */ 152 @Override 153 public boolean onJsConfirm(WebView view, String url, String message, 154 JsResult result) { 155 String logMsg = String.format("JS Confirmation '%s' received from %s", 156 message, url); 157 Log.w(TAG, logMsg); 158 result.confirm(); 159 160 return true; 161 } 162 163 /** 164 * Confirms and logs Javascript alerts, providing the default value. 165 */ 166 @Override 167 public boolean onJsPrompt(WebView view, String url, String message, 168 String defaultValue, JsPromptResult result) { 169 String logMsg = String.format("JS Prompt '%s' received from %s; " + 170 "Giving default value '%s'", message, url, defaultValue); 171 Log.w(TAG, logMsg); 172 result.confirm(defaultValue); 173 174 return true; 175 } 176 }); 177 178 webView.setWebViewClient(new TestWebViewClient(webView.getWebViewClient()) { 179 180 /** 181 * Bypasses and logs errors. 182 */ 183 @Override 184 public void onReceivedError(WebView view, int errorCode, 185 String description, String failingUrl) { 186 String message = String.format("Error '%s' (%d) loading url: %s", 187 description, errorCode, failingUrl); 188 Log.w(TAG, message); 189 } 190 191 /** 192 * Ignores and logs SSL errors. 193 */ 194 @Override 195 public void onReceivedSslError(WebView view, SslErrorHandler handler, 196 SslError error) { 197 Log.w(TAG, "SSL error: " + error); 198 handler.proceed(); 199 } 200 201 /** 202 * Ignores http auth with dummy username and password 203 */ 204 @Override 205 public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, 206 String host, String realm) { 207 handler.proceed("user", "passwd"); 208 } 209 }); 210 } 211 212 void resetLatch() { 213 CountDownLatch temp = mLatch; 214 mLatch = new CountDownLatch(1); 215 if (temp != null) { 216 // Notify existing latch that it's done. 217 while (temp.getCount() > 0) { 218 temp.countDown(); 219 } 220 } 221 } 222 223 void waitForLoad() throws InterruptedException { 224 boolean timedout = !mLatch.await(PAGE_LOAD_TIMEOUT, TimeUnit.MILLISECONDS); 225 if (timedout) { 226 Log.w(TAG, "page timeout. trying to stop."); 227 // try to stop page load 228 mInst.runOnMainSync(new Runnable(){ 229 public void run() { 230 mActivity.getTabControl().getCurrentTab().getWebView().stopLoading(); 231 } 232 }); 233 // try to wait for count down latch again 234 timedout = !mLatch.await(5000, TimeUnit.MILLISECONDS); 235 if (timedout) { 236 Log.e(TAG, "failed to stop the timedout site in 5s"); 237 } 238 } 239 } 240 241 private static class RunStatus { 242 private File mFile; 243 private int iteration; 244 private int page; 245 private String url; 246 private boolean isRecovery; 247 248 private RunStatus(String file) throws IOException { 249 mFile = new File(file); 250 FileReader input = null; 251 BufferedReader reader = null; 252 try { 253 input = new FileReader(mFile); 254 isRecovery = true; 255 reader = new BufferedReader(input); 256 iteration = Integer.parseInt(reader.readLine()); 257 page = Integer.parseInt(reader.readLine()); 258 } catch (FileNotFoundException ex) { 259 isRecovery = false; 260 iteration = 0; 261 page = 0; 262 } finally { 263 try { 264 if (reader != null) { 265 reader.close(); 266 } 267 } finally { 268 if (input != null) { 269 input.close(); 270 } 271 } 272 } 273 } 274 275 public static RunStatus load() throws IOException { 276 return load(sStatusFile); 277 } 278 279 public static RunStatus load(String file) throws IOException { 280 return new RunStatus(sExternalStorage + File.separator + file); 281 } 282 283 public void write() throws IOException { 284 FileWriter output = null; 285 OutputStreamWriter writer = null; 286 if (mFile.exists()) { 287 mFile.delete(); 288 } 289 try { 290 output = new FileWriter(mFile); 291 output.write(iteration + newLine); 292 output.write(page + newLine); 293 output.write(url + newLine); 294 } finally { 295 try { 296 if (writer != null) { 297 writer.close(); 298 } 299 } finally { 300 if (output != null) { 301 output.close(); 302 } 303 } 304 } 305 } 306 307 public void cleanUp() { 308 if (mFile.exists()) { 309 mFile.delete(); 310 } 311 } 312 313 public void resetPage() { 314 page = 0; 315 } 316 317 public void incrementPage() { 318 ++page; 319 } 320 321 public void incrementIteration() { 322 ++iteration; 323 } 324 325 public int getPage() { 326 return page; 327 } 328 329 public int getIteration() { 330 return iteration; 331 } 332 333 public boolean getIsRecovery() { 334 return isRecovery; 335 } 336 337 public void setUrl(String url) { 338 this.url = url; 339 } 340 } 341 342 /** 343 * Loops over a list of URLs, points the browser to each one, and records the time elapsed. 344 * 345 * @param input the reader from which to get the URLs. 346 * @param writer the writer to which to output the results. 347 * @param clearCache determines whether the cache is cleared before loading each page 348 * @param loopCount the number of times to loop through the list of pages 349 * @throws IOException unable to read from input or write to writer. 350 * @throws InterruptedException the thread was interrupted waiting for the page to load. 351 */ 352 void loopUrls(BufferedReader input, OutputStreamWriter writer, 353 boolean clearCache, int loopCount) 354 throws IOException, InterruptedException { 355 Tab tab = mActivity.getTabControl().getCurrentTab(); 356 WebView webView = tab.getWebView(); 357 358 List<String> pages = new LinkedList<String>(); 359 360 String page; 361 while (null != (page = input.readLine())) { 362 pages.add(page); 363 } 364 365 Iterator<String> iterator = pages.iterator(); 366 for (int i = 0; i < mStatus.getPage(); ++i) { 367 iterator.next(); 368 } 369 370 if (mStatus.getIsRecovery()) { 371 Log.e(TAG, "Recovering after crash: " + iterator.next()); 372 } 373 374 while (mStatus.getIteration() < loopCount) { 375 while(iterator.hasNext()) { 376 page = iterator.next(); 377 mStatus.setUrl(page); 378 mStatus.write(); 379 Log.i(TAG, "start: " + page); 380 Uri uri = Uri.parse(page); 381 if (clearCache) { 382 webView.clearCache(true); 383 } 384 final Intent intent = new Intent(Intent.ACTION_VIEW, uri); 385 386 long startTime = System.currentTimeMillis(); 387 mInst.runOnMainSync(new Runnable() { 388 389 public void run() { 390 mActivity.onNewIntent(intent); 391 } 392 393 }); 394 waitForLoad(); 395 long stopTime = System.currentTimeMillis(); 396 397 String url = webView.getUrl(); 398 Log.i(TAG, "finish: " + url); 399 400 if (writer != null) { 401 writer.write(page + "|" + (stopTime - startTime) + newLine); 402 writer.flush(); 403 } 404 405 mStatus.incrementPage(); 406 } 407 mStatus.incrementIteration(); 408 mStatus.resetPage(); 409 iterator = pages.iterator(); 410 } 411 } 412 413 public void testLoadPerformance() throws IOException, InterruptedException { 414 setUpBrowser(); 415 416 OutputStreamWriter writer = getOutputStream(); 417 try { 418 BufferedReader bufferedReader = getInputStream(); 419 try { 420 loopUrls(bufferedReader, writer, true, PERF_LOOPCOUNT); 421 } finally { 422 if (bufferedReader != null) { 423 bufferedReader.close(); 424 } 425 } 426 } finally { 427 if (writer != null) { 428 writer.close(); 429 } 430 } 431 } 432 433 public void testStability() throws IOException, InterruptedException { 434 setUpBrowser(); 435 436 BufferedReader bufferedReader = getInputStream(); 437 try { 438 loopUrls(bufferedReader, null, true, STABILITY_LOOPCOUNT); 439 } finally { 440 if (bufferedReader != null) { 441 bufferedReader.close(); 442 } 443 } 444 } 445} 446