1 // Copyright (c) 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;
11 import org.chromium.android_webview.AwBrowserContext;
12 import org.chromium.android_webview.AwBrowserProcess;
13 import org.chromium.android_webview.AwContents;
14 import org.chromium.android_webview.AwContentsClient;
15 import org.chromium.android_webview.AwLayoutSizer;
16 import org.chromium.android_webview.AwSettings;
17 import org.chromium.android_webview.test.util.JSUtils;
18 import org.chromium.base.test.util.InMemorySharedPreferences;
19 import org.chromium.content.browser.ContentSettings;
20 import org.chromium.content.browser.LoadUrlParams;
21 import org.chromium.content.browser.test.util.CallbackHelper;
22 import org.chromium.content.browser.test.util.Criteria;
23 import org.chromium.content.browser.test.util.CriteriaHelper;
25 import java.util.concurrent.Callable;
26 import java.util.concurrent.FutureTask;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.atomic.AtomicReference;
31 * A base class for android_webview tests.
33 public class AwTestBase
34 extends ActivityInstrumentationTestCase2<AwTestRunnerActivity> {
35 protected static final int WAIT_TIMEOUT_SECONDS = 15;
36 protected static final int CHECK_INTERVAL = 100;
39 super(AwTestRunnerActivity.class);
43 protected void setUp() throws Exception {
45 final Context context = getActivity();
46 getInstrumentation().runOnMainSync(new Runnable() {
49 AwBrowserProcess.start(context);
55 * Runs a {@link Callable} on the main thread, blocking until it is
56 * complete, and returns the result. Calls
57 * {@link Instrumentation#waitForIdleSync()} first to help avoid certain
60 * @param <R> Type of result to return
62 public <R> R runTestOnUiThreadAndGetResult(Callable<R> callable)
64 FutureTask<R> task = new FutureTask<R>(callable);
65 getInstrumentation().waitForIdleSync();
66 getInstrumentation().runOnMainSync(task);
70 protected void enableJavaScriptOnUiThread(final AwContents awContents) {
71 getInstrumentation().runOnMainSync(new Runnable() {
74 awContents.getSettings().setJavaScriptEnabled(true);
80 * Loads url on the UI thread and blocks until onPageFinished is called.
82 protected void loadUrlSync(final AwContents awContents,
83 CallbackHelper onPageFinishedHelper,
84 final String url) throws Exception {
85 int currentCallCount = onPageFinishedHelper.getCallCount();
86 loadUrlAsync(awContents, url);
87 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_SECONDS,
91 protected void loadUrlSyncAndExpectError(final AwContents awContents,
92 CallbackHelper onPageFinishedHelper,
93 CallbackHelper onReceivedErrorHelper,
94 final String url) throws Exception {
95 int onErrorCallCount = onReceivedErrorHelper.getCallCount();
96 int onFinishedCallCount = onPageFinishedHelper.getCallCount();
97 loadUrlAsync(awContents, url);
98 onReceivedErrorHelper.waitForCallback(onErrorCallCount, 1, WAIT_TIMEOUT_SECONDS,
100 onPageFinishedHelper.waitForCallback(onFinishedCallCount, 1, WAIT_TIMEOUT_SECONDS,
105 * Loads url on the UI thread but does not block.
107 protected void loadUrlAsync(final AwContents awContents,
108 final String url) throws Exception {
109 getInstrumentation().runOnMainSync(new Runnable() {
112 awContents.loadUrl(new LoadUrlParams(url));
118 * Posts url on the UI thread and blocks until onPageFinished is called.
120 protected void postUrlSync(final AwContents awContents,
121 CallbackHelper onPageFinishedHelper, final String url,
122 byte[] postData) throws Exception {
123 int currentCallCount = onPageFinishedHelper.getCallCount();
124 postUrlAsync(awContents, url, postData);
125 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_SECONDS,
130 * Loads url on the UI thread but does not block.
132 protected void postUrlAsync(final AwContents awContents,
133 final String url, byte[] postData) throws Exception {
134 class PostUrl implements Runnable {
136 public PostUrl(byte[] postData) {
137 mPostData = postData;
141 awContents.loadUrl(LoadUrlParams.createLoadHttpPostParams(url,
145 getInstrumentation().runOnMainSync(new PostUrl(postData));
149 * Loads data on the UI thread and blocks until onPageFinished is called.
151 protected void loadDataSync(final AwContents awContents,
152 CallbackHelper onPageFinishedHelper,
153 final String data, final String mimeType,
154 final boolean isBase64Encoded) throws Exception {
155 int currentCallCount = onPageFinishedHelper.getCallCount();
156 loadDataAsync(awContents, data, mimeType, isBase64Encoded);
157 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_SECONDS,
161 protected void loadDataSyncWithCharset(final AwContents awContents,
162 CallbackHelper onPageFinishedHelper,
163 final String data, final String mimeType,
164 final boolean isBase64Encoded, final String charset)
166 int currentCallCount = onPageFinishedHelper.getCallCount();
167 getInstrumentation().runOnMainSync(new Runnable() {
170 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
171 data, mimeType, isBase64Encoded, charset));
174 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_SECONDS,
179 * Loads data on the UI thread but does not block.
181 protected void loadDataAsync(final AwContents awContents, final String data,
182 final String mimeType, final boolean isBase64Encoded)
184 getInstrumentation().runOnMainSync(new Runnable() {
187 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
188 data, mimeType, isBase64Encoded));
194 * Factory class used in creation of test AwContents instances.
196 * Test cases can provide subclass instances to the createAwTest* methods in order to create an
197 * AwContents instance with injected test dependencies.
199 public static class TestDependencyFactory {
200 public AwLayoutSizer createLayoutSizer() {
201 return new AwLayoutSizer();
203 public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
204 return new AwTestContainerView(activity);
208 protected TestDependencyFactory createTestDependencyFactory() {
209 return new TestDependencyFactory();
212 protected AwTestContainerView createAwTestContainerView(
213 final AwContentsClient awContentsClient) {
214 return createAwTestContainerView(awContentsClient, true);
217 protected AwTestContainerView createAwTestContainerView(
218 final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
219 AwTestContainerView testContainerView =
220 createDetachedAwTestContainerView(awContentsClient, supportsLegacyQuirks);
221 getActivity().addView(testContainerView);
222 testContainerView.requestFocus();
223 return testContainerView;
226 // The browser context needs to be a process-wide singleton.
227 private AwBrowserContext mBrowserContext =
228 new AwBrowserContext(new InMemorySharedPreferences());
230 protected AwTestContainerView createDetachedAwTestContainerView(
231 final AwContentsClient awContentsClient) {
232 return createDetachedAwTestContainerView(awContentsClient, true);
235 protected AwTestContainerView createDetachedAwTestContainerView(
236 final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
237 final TestDependencyFactory testDependencyFactory = createTestDependencyFactory();
238 final AwTestContainerView testContainerView =
239 testDependencyFactory.createAwTestContainerView(getActivity());
240 // TODO(mnaganov): Should also have tests for the "pure Chromium" mode.
241 // See http://crbug.com/278106
242 testContainerView.initialize(new AwContents(
243 mBrowserContext, testContainerView, testContainerView.getInternalAccessDelegate(),
244 awContentsClient, false, testDependencyFactory.createLayoutSizer(),
245 supportsLegacyQuirks));
246 AwContents.setShouldDownloadFavicons();
247 return testContainerView;
250 protected AwTestContainerView createAwTestContainerViewOnMainSync(
251 final AwContentsClient client) throws Exception {
252 return createAwTestContainerViewOnMainSync(client, true);
255 protected AwTestContainerView createAwTestContainerViewOnMainSync(
256 final AwContentsClient client, final boolean supportsLegacyQuirks) throws Exception {
257 final AtomicReference<AwTestContainerView> testContainerView =
258 new AtomicReference<AwTestContainerView>();
259 getInstrumentation().runOnMainSync(new Runnable() {
262 testContainerView.set(createAwTestContainerView(client, supportsLegacyQuirks));
265 return testContainerView.get();
268 protected void destroyAwContentsOnMainSync(final AwContents awContents) {
269 if (awContents == null) return;
270 getInstrumentation().runOnMainSync(new Runnable() {
273 awContents.destroy();
278 protected String getTitleOnUiThread(final AwContents awContents) throws Exception {
279 return runTestOnUiThreadAndGetResult(new Callable<String>() {
281 public String call() throws Exception {
282 return awContents.getContentViewCore().getTitle();
287 protected ContentSettings getContentSettingsOnUiThread(
288 final AwContents awContents) throws Exception {
289 return runTestOnUiThreadAndGetResult(new Callable<ContentSettings>() {
291 public ContentSettings call() throws Exception {
292 return awContents.getContentViewCore().getContentSettings();
297 protected AwSettings getAwSettingsOnUiThread(
298 final AwContents awContents) throws Exception {
299 return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
301 public AwSettings call() throws Exception {
302 return awContents.getSettings();
308 * Executes the given snippet of JavaScript code within the given ContentView. Returns the
309 * result of its execution in JSON format.
311 protected String executeJavaScriptAndWaitForResult(final AwContents awContents,
312 TestAwContentsClient viewClient, final String code) throws Exception {
313 return JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
314 viewClient.getOnEvaluateJavaScriptResultHelper(),
319 * Similar to CriteriaHelper.pollForCriteria but runs the callable on the UI thread.
320 * Note that exceptions are treated as failure.
322 protected boolean pollOnUiThread(final Callable<Boolean> callable) throws Exception {
323 return CriteriaHelper.pollForCriteria(new Criteria() {
325 public boolean isSatisfied() {
327 return runTestOnUiThreadAndGetResult(callable);
328 } catch (Throwable e) {
336 * Clears the resource cache. Note that the cache is per-application, so this will clear the
337 * cache for all WebViews used.
339 protected void clearCacheOnUiThread(
340 final AwContents awContents,
341 final boolean includeDiskFiles) throws Exception {
342 getInstrumentation().runOnMainSync(new Runnable() {
345 awContents.clearCache(includeDiskFiles);
351 * Returns pure page scale.
353 protected float getScaleOnUiThread(final AwContents awContents) throws Exception {
354 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
356 public Float call() throws Exception {
357 return awContents.getContentViewCore().getScale();
363 * Returns page scale multiplied by the screen density.
365 protected float getPixelScaleOnUiThread(final AwContents awContents) throws Throwable {
366 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
368 public Float call() throws Exception {
369 return awContents.getScale();
375 * Returns whether a user can zoom the page in.
377 protected boolean canZoomInOnUiThread(final AwContents awContents) throws Throwable {
378 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
380 public Boolean call() throws Exception {
381 return awContents.canZoomIn();
387 * Returns whether a user can zoom the page out.
389 protected boolean canZoomOutOnUiThread(final AwContents awContents) throws Throwable {
390 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
392 public Boolean call() throws Exception {
393 return awContents.canZoomOut();