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));
217 * Reloads the current page synchronously.
219 protected void reloadSync(final AwContents awContents,
220 CallbackHelper onPageFinishedHelper) throws Exception {
221 int currentCallCount = onPageFinishedHelper.getCallCount();
222 getInstrumentation().runOnMainSync(new Runnable() {
225 awContents.getContentViewCore().reload(true);
228 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
229 TimeUnit.MILLISECONDS);
233 * Factory class used in creation of test AwContents instances.
235 * Test cases can provide subclass instances to the createAwTest* methods in order to create an
236 * AwContents instance with injected test dependencies.
238 public static class TestDependencyFactory {
239 public AwLayoutSizer createLayoutSizer() {
240 return new AwLayoutSizer();
242 public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
243 return new AwTestContainerView(activity);
245 public AwSettings createAwSettings(Context context, boolean supportsLegacyQuirks) {
246 return new AwSettings(context, false, supportsLegacyQuirks);
250 protected TestDependencyFactory createTestDependencyFactory() {
251 return new TestDependencyFactory();
254 protected AwTestContainerView createAwTestContainerView(
255 final AwContentsClient awContentsClient) {
256 return createAwTestContainerView(awContentsClient, true);
259 protected AwTestContainerView createAwTestContainerView(
260 final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
261 AwTestContainerView testContainerView =
262 createDetachedAwTestContainerView(awContentsClient, supportsLegacyQuirks);
263 getActivity().addView(testContainerView);
264 testContainerView.requestFocus();
265 return testContainerView;
268 // The browser context needs to be a process-wide singleton.
269 private AwBrowserContext mBrowserContext =
270 new AwBrowserContext(new InMemorySharedPreferences());
272 protected AwTestContainerView createDetachedAwTestContainerView(
273 final AwContentsClient awContentsClient) {
274 return createDetachedAwTestContainerView(awContentsClient, true);
277 protected AwTestContainerView createDetachedAwTestContainerView(
278 final AwContentsClient awContentsClient, boolean supportsLegacyQuirks) {
279 final TestDependencyFactory testDependencyFactory = createTestDependencyFactory();
280 final AwTestContainerView testContainerView =
281 testDependencyFactory.createAwTestContainerView(getActivity());
282 AwSettings awSettings = testDependencyFactory.createAwSettings(getActivity(),
283 supportsLegacyQuirks);
284 // TODO(mnaganov): Should also have tests for the "pure Chromium" mode.
285 // See http://crbug.com/278106
286 testContainerView.initialize(new AwContents(
287 mBrowserContext, testContainerView, testContainerView.getInternalAccessDelegate(),
288 awContentsClient, awSettings, testDependencyFactory.createLayoutSizer()));
289 AwContents.setShouldDownloadFavicons();
290 return testContainerView;
293 protected AwTestContainerView createAwTestContainerViewOnMainSync(
294 final AwContentsClient client) throws Exception {
295 return createAwTestContainerViewOnMainSync(client, true);
298 protected AwTestContainerView createAwTestContainerViewOnMainSync(
299 final AwContentsClient client, final boolean supportsLegacyQuirks) throws Exception {
300 final AtomicReference<AwTestContainerView> testContainerView =
301 new AtomicReference<AwTestContainerView>();
302 getInstrumentation().runOnMainSync(new Runnable() {
305 testContainerView.set(createAwTestContainerView(client, supportsLegacyQuirks));
308 return testContainerView.get();
311 protected void destroyAwContentsOnMainSync(final AwContents awContents) {
312 if (awContents == null) return;
313 getInstrumentation().runOnMainSync(new Runnable() {
316 awContents.destroy();
321 protected String getTitleOnUiThread(final AwContents awContents) throws Exception {
322 return runTestOnUiThreadAndGetResult(new Callable<String>() {
324 public String call() throws Exception {
325 return awContents.getContentViewCore().getTitle();
330 protected ContentSettings getContentSettingsOnUiThread(
331 final AwContents awContents) throws Exception {
332 return runTestOnUiThreadAndGetResult(new Callable<ContentSettings>() {
334 public ContentSettings call() throws Exception {
335 return awContents.getContentViewCore().getContentSettings();
340 protected AwSettings getAwSettingsOnUiThread(
341 final AwContents awContents) throws Exception {
342 return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
344 public AwSettings call() throws Exception {
345 return awContents.getSettings();
351 * Executes the given snippet of JavaScript code within the given ContentView. Returns the
352 * result of its execution in JSON format.
354 protected String executeJavaScriptAndWaitForResult(final AwContents awContents,
355 TestAwContentsClient viewClient, final String code) throws Exception {
356 return JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
357 viewClient.getOnEvaluateJavaScriptResultHelper(),
362 * Wrapper around CriteriaHelper.pollForCriteria. This uses AwTestBase-specifc timeouts and
363 * treats timeouts and exceptions as test failures automatically.
365 protected static void poll(final Callable<Boolean> callable) throws Exception {
366 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
368 public boolean isSatisfied() {
370 return callable.call();
371 } catch (Throwable e) {
372 Log.e(TAG, "Exception while polling.", e);
376 }, WAIT_TIMEOUT_MS, CHECK_INTERVAL));
380 * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI thread.
382 protected void pollOnUiThread(final Callable<Boolean> callable) throws Exception {
383 poll(new Callable<Boolean>() {
385 public Boolean call() throws Exception {
386 return runTestOnUiThreadAndGetResult(callable);
392 * Clears the resource cache. Note that the cache is per-application, so this will clear the
393 * cache for all WebViews used.
395 protected void clearCacheOnUiThread(
396 final AwContents awContents,
397 final boolean includeDiskFiles) throws Exception {
398 getInstrumentation().runOnMainSync(new Runnable() {
401 awContents.clearCache(includeDiskFiles);
407 * Returns pure page scale.
409 protected float getScaleOnUiThread(final AwContents awContents) throws Exception {
410 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
412 public Float call() throws Exception {
413 return awContents.getPageScaleFactor();
419 * Returns page scale multiplied by the screen density.
421 protected float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
422 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
424 public Float call() throws Exception {
425 return awContents.getScale();
431 * Returns whether a user can zoom the page in.
433 protected boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
434 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
436 public Boolean call() throws Exception {
437 return awContents.canZoomIn();
443 * Returns whether a user can zoom the page out.
445 protected boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
446 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
448 public Boolean call() throws Exception {
449 return awContents.canZoomOut();