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.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.test.util.CallbackHelper;
23 import org.chromium.content.browser.test.util.Criteria;
24 import org.chromium.content.browser.test.util.CriteriaHelper;
25 import org.chromium.content_public.browser.LoadUrlParams;
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 public static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
39 public 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 public void enableJavaScriptOnUiThread(final AwContents awContents) {
84 getInstrumentation().runOnMainSync(new Runnable() {
87 awContents.getSettings().setJavaScriptEnabled(true);
92 public 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 public void loadUrlSync(final AwContents awContents,
106 CallbackHelper onPageFinishedHelper,
107 final String url) throws Exception {
108 loadUrlSync(awContents, onPageFinishedHelper, url, null);
111 public void loadUrlSync(final AwContents awContents,
112 CallbackHelper onPageFinishedHelper,
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);
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);
135 * Loads url on the UI thread but does not block.
137 public void loadUrlAsync(final AwContents awContents,
138 final String url) throws Exception {
139 loadUrlAsync(awContents, url, null);
142 public void loadUrlAsync(final AwContents awContents,
144 final Map<String, String> extraHeaders) {
145 getInstrumentation().runOnMainSync(new Runnable() {
148 LoadUrlParams params = new LoadUrlParams(url);
149 params.setExtraHeaders(extraHeaders);
150 awContents.loadUrl(params);
156 * Posts url on the UI thread and blocks until onPageFinished is called.
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);
168 * Loads url on the UI thread but does not block.
170 public void postUrlAsync(final AwContents awContents,
171 final String url, byte[] postData) throws Exception {
172 class PostUrl implements Runnable {
174 public PostUrl(byte[] postData) {
175 mPostData = postData;
179 awContents.loadUrl(LoadUrlParams.createLoadHttpPostParams(url,
183 getInstrumentation().runOnMainSync(new PostUrl(postData));
187 * Loads data on the UI thread and blocks until onPageFinished is called.
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);
199 public void loadDataSyncWithCharset(final AwContents awContents,
200 CallbackHelper onPageFinishedHelper,
201 final String data, final String mimeType,
202 final boolean isBase64Encoded, final String charset)
204 int currentCallCount = onPageFinishedHelper.getCallCount();
205 getInstrumentation().runOnMainSync(new Runnable() {
208 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
209 data, mimeType, isBase64Encoded, charset));
212 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
213 TimeUnit.MILLISECONDS);
217 * Loads data on the UI thread but does not block.
219 public void loadDataAsync(final AwContents awContents, final String data,
220 final String mimeType, final boolean isBase64Encoded)
222 getInstrumentation().runOnMainSync(new Runnable() {
225 awContents.loadUrl(LoadUrlParams.createLoadDataParams(
226 data, mimeType, isBase64Encoded));
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);
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() {
247 awContents.loadUrl(LoadUrlParams.createLoadDataParamsWithBaseUrl(
248 data, mimeType, isBase64Encoded, baseUrl, historyUrl));
254 * Reloads the current page synchronously.
256 public void reloadSync(final AwContents awContents,
257 CallbackHelper onPageFinishedHelper) throws Exception {
258 int currentCallCount = onPageFinishedHelper.getCallCount();
259 getInstrumentation().runOnMainSync(new Runnable() {
262 awContents.getNavigationController().reload(true);
265 onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
266 TimeUnit.MILLISECONDS);
270 * Factory class used in creation of test AwContents instances.
272 * Test cases can provide subclass instances to the createAwTest* methods in order to create an
273 * AwContents instance with injected test dependencies.
275 public static class TestDependencyFactory extends AwContents.DependencyFactory {
276 public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
277 return new AwTestContainerView(activity, false);
279 public AwSettings createAwSettings(Context context, boolean supportsLegacyQuirks) {
280 return new AwSettings(context, false, supportsLegacyQuirks);
284 protected TestDependencyFactory createTestDependencyFactory() {
285 return new TestDependencyFactory();
288 public AwTestContainerView createAwTestContainerView(
289 final AwContentsClient awContentsClient) {
290 return createAwTestContainerView(awContentsClient, false);
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;
302 // The browser context needs to be a process-wide singleton.
303 private AwBrowserContext mBrowserContext =
304 new AwBrowserContext(new InMemorySharedPreferences());
306 public AwTestContainerView createDetachedAwTestContainerView(
307 final AwContentsClient awContentsClient) {
308 return createDetachedAwTestContainerView(awContentsClient, false);
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;
326 public AwTestContainerView createAwTestContainerViewOnMainSync(
327 final AwContentsClient client) throws Exception {
328 return createAwTestContainerViewOnMainSync(client, false);
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() {
338 testContainerView.set(createAwTestContainerView(client, supportsLegacyQuirks));
341 return testContainerView.get();
344 public void destroyAwContentsOnMainSync(final AwContents awContents) {
345 if (awContents == null) return;
346 getInstrumentation().runOnMainSync(new Runnable() {
349 awContents.destroy();
354 public String getTitleOnUiThread(final AwContents awContents) throws Exception {
355 return runTestOnUiThreadAndGetResult(new Callable<String>() {
357 public String call() throws Exception {
358 return awContents.getTitle();
363 public ContentSettings getContentSettingsOnUiThread(
364 final AwContents awContents) throws Exception {
365 return runTestOnUiThreadAndGetResult(new Callable<ContentSettings>() {
367 public ContentSettings call() throws Exception {
368 return awContents.getContentViewCore().getContentSettings();
373 public AwSettings getAwSettingsOnUiThread(
374 final AwContents awContents) throws Exception {
375 return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
377 public AwSettings call() throws Exception {
378 return awContents.getSettings();
384 * Executes the given snippet of JavaScript code within the given ContentView. Returns the
385 * result of its execution in JSON format.
387 public String executeJavaScriptAndWaitForResult(final AwContents awContents,
388 TestAwContentsClient viewClient, final String code) throws Exception {
389 return JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
390 viewClient.getOnEvaluateJavaScriptResultHelper(),
395 * Wrapper around CriteriaHelper.pollForCriteria. This uses AwTestBase-specifc timeouts and
396 * treats timeouts and exceptions as test failures automatically.
398 public static void poll(final Callable<Boolean> callable) throws Exception {
399 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
401 public boolean isSatisfied() {
403 return callable.call();
404 } catch (Throwable e) {
405 Log.e(TAG, "Exception while polling.", e);
409 }, WAIT_TIMEOUT_MS, CHECK_INTERVAL));
413 * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI thread.
415 public void pollOnUiThread(final Callable<Boolean> callable) throws Exception {
416 poll(new Callable<Boolean>() {
418 public Boolean call() throws Exception {
419 return runTestOnUiThreadAndGetResult(callable);
425 * Clears the resource cache. Note that the cache is per-application, so this will clear the
426 * cache for all WebViews used.
428 public void clearCacheOnUiThread(
429 final AwContents awContents,
430 final boolean includeDiskFiles) throws Exception {
431 getInstrumentation().runOnMainSync(new Runnable() {
434 awContents.clearCache(includeDiskFiles);
440 * Returns pure page scale.
442 public float getScaleOnUiThread(final AwContents awContents) throws Exception {
443 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
445 public Float call() throws Exception {
446 return awContents.getPageScaleFactor();
452 * Returns page scale multiplied by the screen density.
454 public float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
455 return runTestOnUiThreadAndGetResult(new Callable<Float>() {
457 public Float call() throws Exception {
458 return awContents.getScale();
464 * Returns whether a user can zoom the page in.
466 public boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
467 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
469 public Boolean call() throws Exception {
470 return awContents.canZoomIn();
476 * Returns whether a user can zoom the page out.
478 public boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
479 return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
481 public Boolean call() throws Exception {
482 return awContents.canZoomOut();