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