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.
5 package org.chromium.android_webview.test;
7 import android.app.Instrumentation;
8 import android.content.Context;
9 import android.test.ActivityInstrumentationTestCase2;
10 import android.util.Log;
12 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
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.AwLayoutSizer;
19 import org.chromium.android_webview.AwSettings;
20 import org.chromium.android_webview.test.util.JSUtils;
21 import org.chromium.base.test.util.InMemorySharedPreferences;
22 import org.chromium.content.browser.ContentSettings;
23 import org.chromium.content.browser.LoadUrlParams;
24 import org.chromium.content.browser.test.util.CallbackHelper;
25 import org.chromium.content.browser.test.util.Criteria;
26 import org.chromium.content.browser.test.util.CriteriaHelper;
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;
34 * A base class for android_webview tests.
36 public class AwTestBase
37 extends ActivityInstrumentationTestCase2<AwTestRunnerActivity> {
38 protected static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
39 protected static final int CHECK_INTERVAL = 100;
40 private static final String TAG = "AwTestBase";
43 super(AwTestRunnerActivity.class);
47 protected void setUp() throws Exception {
49 if (needsBrowserProcessStarted()) {
50 final Context context = getActivity();
51 getInstrumentation().runOnMainSync(new Runnable() {
54 AwBrowserProcess.start(context);
60 /* Override this to return false if the test doesn't want the browser startup sequence to
61 * be run automatically.
63 protected boolean needsBrowserProcessStarted() {
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
73 * @param <R> Type of result to return
75 public <R> R runTestOnUiThreadAndGetResult(Callable<R> callable)
77 FutureTask<R> task = new FutureTask<R>(callable);
78 getInstrumentation().waitForIdleSync();
79 getInstrumentation().runOnMainSync(task);
83 protected void enableJavaScriptOnUiThread(final AwContents awContents) {
84 getInstrumentation().runOnMainSync(new Runnable() {
87 awContents.getSettings().setJavaScriptEnabled(true);
92 protected void setNetworkAvailableOnUiThread(final AwContents awContents,
93 final boolean networkUp) {
94 getInstrumentation().runOnMainSync(new Runnable() {
97 awContents.setNetworkAvailable(networkUp);
103 * Loads url on the UI thread and blocks until onPageFinished is called.
105 protected void loadUrlSync(final AwContents awContents,
106 CallbackHelper onPageFinishedHelper,
107 final String url) throws Exception {
108 int currentCallCount = onPageFinishedHelper.getCallCount();
109 loadUrlAsync(awContents, url);
110 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
111 TimeUnit.MILLISECONDS);
114 protected void loadUrlSyncAndExpectError(final AwContents awContents,
115 CallbackHelper onPageFinishedHelper,
116 CallbackHelper onReceivedErrorHelper,
117 final String url) throws Exception {
118 int onErrorCallCount = onReceivedErrorHelper.getCallCount();
119 int onFinishedCallCount = onPageFinishedHelper.getCallCount();
120 loadUrlAsync(awContents, url);
121 onReceivedErrorHelper.waitForCallback(onErrorCallCount, 1, WAIT_TIMEOUT_MS,
122 TimeUnit.MILLISECONDS);
123 onPageFinishedHelper.waitForCallback(onFinishedCallCount, 1, WAIT_TIMEOUT_MS,
124 TimeUnit.MILLISECONDS);
128 * Loads url on the UI thread but does not block.
130 protected void loadUrlAsync(final AwContents awContents,
131 final String url) throws Exception {
132 getInstrumentation().runOnMainSync(new Runnable() {
135 awContents.loadUrl(new LoadUrlParams(url));
141 * Posts url on the UI thread and blocks until onPageFinished is called.
143 protected void postUrlSync(final AwContents awContents,
144 CallbackHelper onPageFinishedHelper, final String url,
145 byte[] postData) throws Exception {
146 int currentCallCount = onPageFinishedHelper.getCallCount();
147 postUrlAsync(awContents, url, postData);
148 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
149 TimeUnit.MILLISECONDS);
153 * Loads url on the UI thread but does not block.
155 protected void postUrlAsync(final AwContents awContents,
156 final String url, byte[] postData) throws Exception {
157 class PostUrl implements Runnable {
159 public PostUrl(byte[] postData) {
160 mPostData = postData;
164 awContents.loadUrl(LoadUrlParams.createLoadHttpPostParams(url,
168 getInstrumentation().runOnMainSync(new PostUrl(postData));
172 * Loads data on the UI thread and blocks until onPageFinished is called.
174 protected void loadDataSync(final AwContents awContents,
175 CallbackHelper onPageFinishedHelper,
176 final String data, final String mimeType,
177 final boolean isBase64Encoded) throws Exception {
178 int currentCallCount = onPageFinishedHelper.getCallCount();
179 loadDataAsync(awContents, data, mimeType, isBase64Encoded);
180 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
181 TimeUnit.MILLISECONDS);
184 protected void loadDataSyncWithCharset(final AwContents awContents,
185 CallbackHelper onPageFinishedHelper,
186 final String data, final String mimeType,
187 final boolean isBase64Encoded, final String charset)
189 int currentCallCount = onPageFinishedHelper.getCallCount();
190 getInstrumentation().runOnMainSync(new Runnable() {
193 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
194 data, mimeType, isBase64Encoded, charset));
197 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
198 TimeUnit.MILLISECONDS);
202 * Loads data on the UI thread but does not block.
204 protected void loadDataAsync(final AwContents awContents, final String data,
205 final String mimeType, final boolean isBase64Encoded)
207 getInstrumentation().runOnMainSync(new Runnable() {
210 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
211 data, mimeType, isBase64Encoded));
216 protected void loadDataWithBaseUrlSync(final AwContents awContents,
217 CallbackHelper onPageFinishedHelper, final String data, final String mimeType,
218 final boolean isBase64Encoded, final String baseUrl,
219 final String historyUrl) throws Throwable {
220 int currentCallCount = onPageFinishedHelper.getCallCount();
221 loadDataWithBaseUrlAsync(awContents, data, mimeType, isBase64Encoded, baseUrl, historyUrl);
222 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
223 TimeUnit.MILLISECONDS);
226 protected void loadDataWithBaseUrlAsync(final AwContents awContents,
227 final String data, final String mimeType, final boolean isBase64Encoded,
228 final String baseUrl, final String historyUrl) throws Throwable {
229 runTestOnUiThread(new Runnable() {
232 awContents.loadUrl(LoadUrlParams.createLoadDataParamsWithBaseUrl(
233 data, mimeType, isBase64Encoded, baseUrl, historyUrl));
239 * Reloads the current page synchronously.
241 protected void reloadSync(final AwContents awContents,
242 CallbackHelper onPageFinishedHelper) throws Exception {
243 int currentCallCount = onPageFinishedHelper.getCallCount();
244 getInstrumentation().runOnMainSync(new Runnable() {
247 awContents.getContentViewCore().reload(true);
250 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
251 TimeUnit.MILLISECONDS);
255 * Factory class used in creation of test AwContents instances.
257 * Test cases can provide subclass instances to the createAwTest* methods in order to create an
258 * AwContents instance with injected test dependencies.
260 public static class TestDependencyFactory {
261 public AwLayoutSizer createLayoutSizer() {
262 return new AwLayoutSizer();
264 public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
265 return new AwTestContainerView(activity);
267 public AwSettings createAwSettings(Context context, boolean supportsLegacyQuirks) {
268 return new AwSettings(context, false, supportsLegacyQuirks);
272 protected TestDependencyFactory createTestDependencyFactory() {
273 return new TestDependencyFactory();
276 protected AwTestContainerView createAwTestContainerView(
277 final AwContentsClient awContentsClient) {
278 return createAwTestContainerView(awContentsClient, false);
281 protected AwTestContainerView createAwTestContainerView(
282 final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
283 AwTestContainerView testContainerView =
284 createDetachedAwTestContainerView(awContentsClient, supportsLegacyQuirks);
285 getActivity().addView(testContainerView);
286 testContainerView.requestFocus();
287 return testContainerView;
290 // The browser context needs to be a process-wide singleton.
291 private AwBrowserContext mBrowserContext =
292 new AwBrowserContext(new InMemorySharedPreferences());
294 protected AwTestContainerView createDetachedAwTestContainerView(
295 final AwContentsClient awContentsClient) {
296 return createDetachedAwTestContainerView(awContentsClient, false);
299 protected AwTestContainerView createDetachedAwTestContainerView(
300 final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
301 final TestDependencyFactory testDependencyFactory = createTestDependencyFactory();
302 final AwTestContainerView testContainerView =
303 testDependencyFactory.createAwTestContainerView(getActivity());
304 AwSettings awSettings = testDependencyFactory.createAwSettings(getActivity(),
305 supportsLegacyQuirks);
306 testContainerView.initialize(new AwContents(
307 mBrowserContext, testContainerView, testContainerView.getInternalAccessDelegate(),
308 awContentsClient, awSettings, testDependencyFactory.createLayoutSizer()));
309 AwContents.setShouldDownloadFavicons();
310 return testContainerView;
313 protected AwTestContainerView createAwTestContainerViewOnMainSync(
314 final AwContentsClient client) throws Exception {
315 return createAwTestContainerViewOnMainSync(client, false);
318 protected AwTestContainerView createAwTestContainerViewOnMainSync(
319 final AwContentsClient client, final boolean supportsLegacyQuirks) throws Exception {
320 final AtomicReference<AwTestContainerView> testContainerView =
321 new AtomicReference<AwTestContainerView>();
322 getInstrumentation().runOnMainSync(new Runnable() {
325 testContainerView.set(createAwTestContainerView(client, supportsLegacyQuirks));
328 return testContainerView.get();
331 protected void destroyAwContentsOnMainSync(final AwContents awContents) {
332 if (awContents == null) return;
333 getInstrumentation().runOnMainSync(new Runnable() {
336 awContents.destroy();
341 protected String getTitleOnUiThread(final AwContents awContents) throws Exception {
342 return runTestOnUiThreadAndGetResult(new Callable<String>() {
344 public String call() throws Exception {
345 return awContents.getContentViewCore().getTitle();
350 protected ContentSettings getContentSettingsOnUiThread(
351 final AwContents awContents) throws Exception {
352 return runTestOnUiThreadAndGetResult(new Callable<ContentSettings>() {
354 public ContentSettings call() throws Exception {
355 return awContents.getContentViewCore().getContentSettings();
360 protected AwSettings getAwSettingsOnUiThread(
361 final AwContents awContents) throws Exception {
362 return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
364 public AwSettings call() throws Exception {
365 return awContents.getSettings();
371 * Executes the given snippet of JavaScript code within the given ContentView. Returns the
372 * result of its execution in JSON format.
374 protected String executeJavaScriptAndWaitForResult(final AwContents awContents,
375 TestAwContentsClient viewClient, final String code) throws Exception {
376 return JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
377 viewClient.getOnEvaluateJavaScriptResultHelper(),
382 * Wrapper around CriteriaHelper.pollForCriteria. This uses AwTestBase-specifc timeouts and
383 * treats timeouts and exceptions as test failures automatically.
385 protected static void poll(final Callable<Boolean> callable) throws Exception {
386 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
388 public boolean isSatisfied() {
390 return callable.call();
391 } catch (Throwable e) {
392 Log.e(TAG, "Exception while polling.", e);
396 }, WAIT_TIMEOUT_MS, CHECK_INTERVAL));
400 * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI thread.
402 protected void pollOnUiThread(final Callable<Boolean> callable) throws Exception {
403 poll(new Callable<Boolean>() {
405 public Boolean call() throws Exception {
406 return runTestOnUiThreadAndGetResult(callable);
412 * Clears the resource cache. Note that the cache is per-application, so this will clear the
413 * cache for all WebViews used.
415 protected void clearCacheOnUiThread(
416 final AwContents awContents,
417 final boolean includeDiskFiles) throws Exception {
418 getInstrumentation().runOnMainSync(new Runnable() {
421 awContents.clearCache(includeDiskFiles);
427 * Returns pure page scale.
429 protected float getScaleOnUiThread(final AwContents awContents) throws Exception {
430 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
432 public Float call() throws Exception {
433 return awContents.getPageScaleFactor();
439 * Returns page scale multiplied by the screen density.
441 protected float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
442 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
444 public Float call() throws Exception {
445 return awContents.getScale();
451 * Returns whether a user can zoom the page in.
453 protected boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
454 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
456 public Boolean call() throws Exception {
457 return awContents.canZoomIn();
463 * Returns whether a user can zoom the page out.
465 protected boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
466 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
468 public Boolean call() throws Exception {
469 return awContents.canZoomOut();