• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.android_webview.test;
6 
7 import android.app.Instrumentation;
8 import android.content.Context;
9 import android.test.ActivityInstrumentationTestCase2;
10 import android.util.Log;
11 
12 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
13 
14 import org.chromium.android_webview.AwBrowserContext;
15 import org.chromium.android_webview.AwBrowserProcess;
16 import org.chromium.android_webview.AwContents;
17 import org.chromium.android_webview.AwContentsClient;
18 import org.chromium.android_webview.AwSettings;
19 import org.chromium.android_webview.test.util.JSUtils;
20 import org.chromium.base.test.util.InMemorySharedPreferences;
21 import org.chromium.content.browser.ContentSettings;
22 import org.chromium.content.browser.LoadUrlParams;
23 import org.chromium.content.browser.test.util.CallbackHelper;
24 import org.chromium.content.browser.test.util.Criteria;
25 import org.chromium.content.browser.test.util.CriteriaHelper;
26 
27 import java.util.Map;
28 import java.util.concurrent.Callable;
29 import java.util.concurrent.FutureTask;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.atomic.AtomicReference;
32 
33 /**
34  * A base class for android_webview tests.
35  */
36 public class AwTestBase
37         extends ActivityInstrumentationTestCase2<AwTestRunnerActivity> {
38     public static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
39     public static final int CHECK_INTERVAL = 100;
40     private static final String TAG = "AwTestBase";
41 
AwTestBase()42     public AwTestBase() {
43         super(AwTestRunnerActivity.class);
44     }
45 
46     @Override
setUp()47     protected void setUp() throws Exception {
48         super.setUp();
49         if (needsBrowserProcessStarted()) {
50             final Context context = getActivity();
51             getInstrumentation().runOnMainSync(new Runnable() {
52                 @Override
53                 public void run() {
54                     AwBrowserProcess.start(context);
55                 }
56             });
57         }
58     }
59 
60     /* Override this to return false if the test doesn't want the browser startup sequence to
61      * be run automatically.
62      */
needsBrowserProcessStarted()63     protected boolean needsBrowserProcessStarted() {
64         return true;
65     }
66 
67     /**
68      * Runs a {@link Callable} on the main thread, blocking until it is
69      * complete, and returns the result. Calls
70      * {@link Instrumentation#waitForIdleSync()} first to help avoid certain
71      * race conditions.
72      *
73      * @param <R> Type of result to return
74      */
runTestOnUiThreadAndGetResult(Callable<R> callable)75     public <R> R runTestOnUiThreadAndGetResult(Callable<R> callable)
76             throws Exception {
77         FutureTask<R> task = new FutureTask<R>(callable);
78         getInstrumentation().waitForIdleSync();
79         getInstrumentation().runOnMainSync(task);
80         return task.get();
81     }
82 
enableJavaScriptOnUiThread(final AwContents awContents)83     public void enableJavaScriptOnUiThread(final AwContents awContents) {
84         getInstrumentation().runOnMainSync(new Runnable() {
85             @Override
86             public void run() {
87                 awContents.getSettings().setJavaScriptEnabled(true);
88             }
89         });
90     }
91 
setNetworkAvailableOnUiThread(final AwContents awContents, final boolean networkUp)92     public void setNetworkAvailableOnUiThread(final AwContents awContents,
93             final boolean networkUp) {
94         getInstrumentation().runOnMainSync(new Runnable() {
95             @Override
96             public void run() {
97                 awContents.setNetworkAvailable(networkUp);
98             }
99         });
100     }
101 
102     /**
103      * Loads url on the UI thread and blocks until onPageFinished is called.
104      */
loadUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper, final String url)105     public void loadUrlSync(final AwContents awContents,
106                                CallbackHelper onPageFinishedHelper,
107                                final String url) throws Exception {
108         loadUrlSync(awContents, onPageFinishedHelper, url, null);
109     }
110 
loadUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper, final String url, final Map<String, String> extraHeaders)111     public void loadUrlSync(final AwContents awContents,
112                                CallbackHelper onPageFinishedHelper,
113                                final String url,
114                                final Map<String, String> extraHeaders) throws Exception {
115         int currentCallCount = onPageFinishedHelper.getCallCount();
116         loadUrlAsync(awContents, url, extraHeaders);
117         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
118                 TimeUnit.MILLISECONDS);
119     }
120 
loadUrlSyncAndExpectError(final AwContents awContents, CallbackHelper onPageFinishedHelper, CallbackHelper onReceivedErrorHelper, final String url)121     public void loadUrlSyncAndExpectError(final AwContents awContents,
122             CallbackHelper onPageFinishedHelper,
123             CallbackHelper onReceivedErrorHelper,
124             final String url) throws Exception {
125         int onErrorCallCount = onReceivedErrorHelper.getCallCount();
126         int onFinishedCallCount = onPageFinishedHelper.getCallCount();
127         loadUrlAsync(awContents, url);
128         onReceivedErrorHelper.waitForCallback(onErrorCallCount, 1, WAIT_TIMEOUT_MS,
129                 TimeUnit.MILLISECONDS);
130         onPageFinishedHelper.waitForCallback(onFinishedCallCount, 1, WAIT_TIMEOUT_MS,
131                 TimeUnit.MILLISECONDS);
132     }
133 
134     /**
135      * Loads url on the UI thread but does not block.
136      */
loadUrlAsync(final AwContents awContents, final String url)137     public void loadUrlAsync(final AwContents awContents,
138                                 final String url) throws Exception {
139         loadUrlAsync(awContents, url, null);
140     }
141 
loadUrlAsync(final AwContents awContents, final String url, final Map<String, String> extraHeaders)142     public void loadUrlAsync(final AwContents awContents,
143                                 final String url,
144                                 final Map<String, String> extraHeaders) {
145         getInstrumentation().runOnMainSync(new Runnable() {
146             @Override
147             public void run() {
148                 LoadUrlParams params = new LoadUrlParams(url);
149                 params.setExtraHeaders(extraHeaders);
150                 awContents.loadUrl(params);
151             }
152         });
153     }
154 
155     /**
156      * Posts url on the UI thread and blocks until onPageFinished is called.
157      */
postUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper, final String url, byte[] postData)158     public void postUrlSync(final AwContents awContents,
159             CallbackHelper onPageFinishedHelper, final String url,
160             byte[] postData) throws Exception {
161         int currentCallCount = onPageFinishedHelper.getCallCount();
162         postUrlAsync(awContents, url, postData);
163         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
164                 TimeUnit.MILLISECONDS);
165     }
166 
167     /**
168      * Loads url on the UI thread but does not block.
169      */
postUrlAsync(final AwContents awContents, final String url, byte[] postData)170     public void postUrlAsync(final AwContents awContents,
171             final String url, byte[] postData) throws Exception {
172         class PostUrl implements Runnable {
173             byte[] mPostData;
174             public PostUrl(byte[] postData) {
175                 mPostData = postData;
176             }
177             @Override
178             public void run() {
179                 awContents.loadUrl(LoadUrlParams.createLoadHttpPostParams(url,
180                         mPostData));
181             }
182         }
183         getInstrumentation().runOnMainSync(new PostUrl(postData));
184     }
185 
186     /**
187      * Loads data on the UI thread and blocks until onPageFinished is called.
188      */
loadDataSync(final AwContents awContents, CallbackHelper onPageFinishedHelper, final String data, final String mimeType, final boolean isBase64Encoded)189     public void loadDataSync(final AwContents awContents,
190                                 CallbackHelper onPageFinishedHelper,
191                                 final String data, final String mimeType,
192                                 final boolean isBase64Encoded) throws Exception {
193         int currentCallCount = onPageFinishedHelper.getCallCount();
194         loadDataAsync(awContents, data, mimeType, isBase64Encoded);
195         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
196                 TimeUnit.MILLISECONDS);
197     }
198 
loadDataSyncWithCharset(final AwContents awContents, CallbackHelper onPageFinishedHelper, final String data, final String mimeType, final boolean isBase64Encoded, final String charset)199     public void loadDataSyncWithCharset(final AwContents awContents,
200                                            CallbackHelper onPageFinishedHelper,
201                                            final String data, final String mimeType,
202                                            final boolean isBase64Encoded, final String charset)
203             throws Exception {
204         int currentCallCount = onPageFinishedHelper.getCallCount();
205         getInstrumentation().runOnMainSync(new Runnable() {
206             @Override
207             public void run() {
208                 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
209                         data, mimeType, isBase64Encoded, charset));
210             }
211         });
212         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
213                 TimeUnit.MILLISECONDS);
214     }
215 
216     /**
217      * Loads data on the UI thread but does not block.
218      */
loadDataAsync(final AwContents awContents, final String data, final String mimeType, final boolean isBase64Encoded)219     public void loadDataAsync(final AwContents awContents, final String data,
220                                  final String mimeType, final boolean isBase64Encoded)
221             throws Exception {
222         getInstrumentation().runOnMainSync(new Runnable() {
223             @Override
224             public void run() {
225                 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
226                         data, mimeType, isBase64Encoded));
227             }
228         });
229     }
230 
loadDataWithBaseUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper, final String data, final String mimeType, final boolean isBase64Encoded, final String baseUrl, final String historyUrl)231     public void loadDataWithBaseUrlSync(final AwContents awContents,
232             CallbackHelper onPageFinishedHelper, final String data, final String mimeType,
233             final boolean isBase64Encoded, final String baseUrl,
234             final String historyUrl) throws Throwable {
235         int currentCallCount = onPageFinishedHelper.getCallCount();
236         loadDataWithBaseUrlAsync(awContents, data, mimeType, isBase64Encoded, baseUrl, historyUrl);
237         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
238                 TimeUnit.MILLISECONDS);
239     }
240 
loadDataWithBaseUrlAsync(final AwContents awContents, final String data, final String mimeType, final boolean isBase64Encoded, final String baseUrl, final String historyUrl)241     public void loadDataWithBaseUrlAsync(final AwContents awContents,
242             final String data, final String mimeType, final boolean isBase64Encoded,
243             final String baseUrl, final String historyUrl) throws Throwable {
244         runTestOnUiThread(new Runnable() {
245             @Override
246             public void run() {
247                 awContents.loadUrl(LoadUrlParams.createLoadDataParamsWithBaseUrl(
248                         data, mimeType, isBase64Encoded, baseUrl, historyUrl));
249             }
250         });
251     }
252 
253     /**
254      * Reloads the current page synchronously.
255      */
reloadSync(final AwContents awContents, CallbackHelper onPageFinishedHelper)256     public void reloadSync(final AwContents awContents,
257                               CallbackHelper onPageFinishedHelper) throws Exception {
258         int currentCallCount = onPageFinishedHelper.getCallCount();
259         getInstrumentation().runOnMainSync(new Runnable() {
260             @Override
261             public void run() {
262                 awContents.getContentViewCore().reload(true);
263             }
264         });
265         onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
266                 TimeUnit.MILLISECONDS);
267     }
268 
269     /**
270      * Factory class used in creation of test AwContents instances.
271      *
272      * Test cases can provide subclass instances to the createAwTest* methods in order to create an
273      * AwContents instance with injected test dependencies.
274      */
275     public static class TestDependencyFactory extends AwContents.DependencyFactory {
createAwTestContainerView(AwTestRunnerActivity activity)276         public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
277             return new AwTestContainerView(activity);
278         }
createAwSettings(Context context, boolean supportsLegacyQuirks)279         public AwSettings createAwSettings(Context context, boolean supportsLegacyQuirks) {
280             return new AwSettings(context, false, supportsLegacyQuirks);
281         }
282     }
283 
createTestDependencyFactory()284     protected TestDependencyFactory createTestDependencyFactory() {
285         return new TestDependencyFactory();
286     }
287 
createAwTestContainerView( final AwContentsClient awContentsClient)288     public AwTestContainerView createAwTestContainerView(
289             final AwContentsClient awContentsClient) {
290         return createAwTestContainerView(awContentsClient, false);
291     }
292 
createAwTestContainerView( final AwContentsClient awContentsClient, boolean supportsLegacyQuirks)293     public AwTestContainerView createAwTestContainerView(
294             final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
295         AwTestContainerView testContainerView =
296                 createDetachedAwTestContainerView(awContentsClient, supportsLegacyQuirks);
297         getActivity().addView(testContainerView);
298         testContainerView.requestFocus();
299         return testContainerView;
300     }
301 
302     // The browser context needs to be a process-wide singleton.
303     private AwBrowserContext mBrowserContext =
304             new AwBrowserContext(new InMemorySharedPreferences());
305 
createDetachedAwTestContainerView( final AwContentsClient awContentsClient)306     public AwTestContainerView createDetachedAwTestContainerView(
307             final AwContentsClient awContentsClient) {
308         return createDetachedAwTestContainerView(awContentsClient, false);
309     }
310 
createDetachedAwTestContainerView( final AwContentsClient awContentsClient, boolean supportsLegacyQuirks)311     public AwTestContainerView createDetachedAwTestContainerView(
312             final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
313         final TestDependencyFactory testDependencyFactory = createTestDependencyFactory();
314         final AwTestContainerView testContainerView =
315             testDependencyFactory.createAwTestContainerView(getActivity());
316         AwSettings awSettings = testDependencyFactory.createAwSettings(getActivity(),
317                 supportsLegacyQuirks);
318         testContainerView.initialize(new AwContents(
319                 mBrowserContext, testContainerView, testContainerView.getContext(),
320                 testContainerView.getInternalAccessDelegate(),
321                 testContainerView.getNativeGLDelegate(), awContentsClient,
322                 awSettings, testDependencyFactory));
323         return testContainerView;
324     }
325 
createAwTestContainerViewOnMainSync( final AwContentsClient client)326     public AwTestContainerView createAwTestContainerViewOnMainSync(
327             final AwContentsClient client) throws Exception {
328         return createAwTestContainerViewOnMainSync(client, false);
329     }
330 
createAwTestContainerViewOnMainSync( final AwContentsClient client, final boolean supportsLegacyQuirks)331     public AwTestContainerView createAwTestContainerViewOnMainSync(
332             final AwContentsClient client, final boolean supportsLegacyQuirks) throws Exception {
333         final AtomicReference<AwTestContainerView> testContainerView =
334                 new AtomicReference<AwTestContainerView>();
335         getInstrumentation().runOnMainSync(new Runnable() {
336             @Override
337             public void run() {
338                 testContainerView.set(createAwTestContainerView(client, supportsLegacyQuirks));
339             }
340         });
341         return testContainerView.get();
342     }
343 
destroyAwContentsOnMainSync(final AwContents awContents)344     public void destroyAwContentsOnMainSync(final AwContents awContents) {
345         if (awContents == null) return;
346         getInstrumentation().runOnMainSync(new Runnable() {
347             @Override
348             public void run() {
349                 awContents.destroy();
350             }
351         });
352     }
353 
getTitleOnUiThread(final AwContents awContents)354     public String getTitleOnUiThread(final AwContents awContents) throws Exception {
355         return runTestOnUiThreadAndGetResult(new Callable<String>() {
356             @Override
357             public String call() throws Exception {
358                 return awContents.getContentViewCore().getTitle();
359             }
360         });
361     }
362 
363     public ContentSettings getContentSettingsOnUiThread(
364             final AwContents awContents) throws Exception {
365         return runTestOnUiThreadAndGetResult(new Callable<ContentSettings>() {
366             @Override
367             public ContentSettings call() throws Exception {
368                 return awContents.getContentViewCore().getContentSettings();
369             }
370         });
371     }
372 
373     public AwSettings getAwSettingsOnUiThread(
374             final AwContents awContents) throws Exception {
375         return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
376             @Override
377             public AwSettings call() throws Exception {
378                 return awContents.getSettings();
379             }
380         });
381     }
382 
383     /**
384      * Executes the given snippet of JavaScript code within the given ContentView. Returns the
385      * result of its execution in JSON format.
386      */
387     public String executeJavaScriptAndWaitForResult(final AwContents awContents,
388             TestAwContentsClient viewClient, final String code) throws Exception {
389         return JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
390                 viewClient.getOnEvaluateJavaScriptResultHelper(),
391                 code);
392     }
393 
394     /**
395      * Wrapper around CriteriaHelper.pollForCriteria. This uses AwTestBase-specifc timeouts and
396      * treats timeouts and exceptions as test failures automatically.
397      */
398     public static void poll(final Callable<Boolean> callable) throws Exception {
399         assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
400             @Override
401             public boolean isSatisfied() {
402                 try {
403                     return callable.call();
404                 } catch (Throwable e) {
405                     Log.e(TAG, "Exception while polling.", e);
406                     return false;
407                 }
408             }
409         }, WAIT_TIMEOUT_MS, CHECK_INTERVAL));
410     }
411 
412     /**
413      * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI thread.
414      */
415     public void pollOnUiThread(final Callable<Boolean> callable) throws Exception {
416         poll(new Callable<Boolean>() {
417             @Override
418             public Boolean call() throws Exception {
419                 return runTestOnUiThreadAndGetResult(callable);
420             }
421         });
422     }
423 
424     /**
425      * Clears the resource cache. Note that the cache is per-application, so this will clear the
426      * cache for all WebViews used.
427      */
428     public void clearCacheOnUiThread(
429             final AwContents awContents,
430             final boolean includeDiskFiles) throws Exception {
431         getInstrumentation().runOnMainSync(new Runnable() {
432             @Override
433             public void run() {
434                 awContents.clearCache(includeDiskFiles);
435             }
436         });
437     }
438 
439     /**
440      * Returns pure page scale.
441      */
442     public float getScaleOnUiThread(final AwContents awContents) throws Exception {
443         return runTestOnUiThreadAndGetResult(new Callable<Float>() {
444             @Override
445             public Float call() throws Exception {
446                 return awContents.getPageScaleFactor();
447             }
448         });
449     }
450 
451     /**
452      * Returns page scale multiplied by the screen density.
453      */
454     public float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
455         return runTestOnUiThreadAndGetResult(new Callable<Float>() {
456             @Override
457             public Float call() throws Exception {
458                 return awContents.getScale();
459             }
460         });
461     }
462 
463     /**
464      * Returns whether a user can zoom the page in.
465      */
466     public boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
467         return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
468             @Override
469             public Boolean call() throws Exception {
470                 return awContents.canZoomIn();
471             }
472         });
473     }
474 
475     /**
476      * Returns whether a user can zoom the page out.
477      */
478     public boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
479         return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
480             @Override
481             public Boolean call() throws Exception {
482                 return awContents.canZoomOut();
483             }
484         });
485     }
486 
487 }
488