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.content_shell_apk;
7 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
9 import android.content.ComponentName;
10 import android.content.Intent;
11 import android.net.Uri;
12 import android.test.ActivityInstrumentationTestCase2;
13 import android.text.TextUtils;
14 import android.view.ViewGroup;
16 import org.chromium.base.ThreadUtils;
17 import org.chromium.base.test.util.UrlUtils;
18 import org.chromium.content.browser.ContentView;
19 import org.chromium.content.browser.ContentViewCore;
20 import org.chromium.content.browser.test.util.CallbackHelper;
21 import org.chromium.content.browser.test.util.Criteria;
22 import org.chromium.content.browser.test.util.CriteriaHelper;
23 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
24 import org.chromium.content_public.browser.LoadUrlParams;
25 import org.chromium.content_public.browser.NavigationController;
26 import org.chromium.content_public.browser.WebContents;
27 import org.chromium.content_shell.Shell;
29 import java.lang.annotation.ElementType;
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.lang.annotation.Target;
33 import java.lang.reflect.Method;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicBoolean;
38 * Base test class for all ContentShell based tests.
40 public class ContentShellTestBase extends ActivityInstrumentationTestCase2<ContentShellActivity> {
42 /** The maximum time the waitForActiveShellToBeDoneLoading method will wait. */
43 private static final long WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT = scaleTimeout(10000);
45 protected static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS = scaleTimeout(15);
47 public ContentShellTestBase() {
48 super(ContentShellActivity.class);
52 * Starts the ContentShell activity and loads the given URL.
53 * The URL can be null, in which case will default to ContentShellActivity.DEFAULT_SHELL_URL.
55 protected ContentShellActivity launchContentShellWithUrl(String url) {
56 return launchContentShellWithUrlAndCommandLineArgs(url, null);
60 * Starts the ContentShell activity appending the provided command line arguments
61 * and loads the given URL. The URL can be null, in which case will default to
62 * ContentShellActivity.DEFAULT_SHELL_URL.
64 protected ContentShellActivity launchContentShellWithUrlAndCommandLineArgs(String url,
65 String[] commandLineArgs) {
66 Intent intent = new Intent(Intent.ACTION_MAIN);
67 intent.addCategory(Intent.CATEGORY_LAUNCHER);
68 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
69 if (url != null) intent.setData(Uri.parse(url));
70 intent.setComponent(new ComponentName(getInstrumentation().getTargetContext(),
71 ContentShellActivity.class));
72 if (commandLineArgs != null) {
73 intent.putExtra(ContentShellActivity.COMMAND_LINE_ARGS_KEY, commandLineArgs);
75 setActivityIntent(intent);
79 // TODO(cjhopman): These functions are inconsistent with launchContentShell***. Should be
80 // startContentShell*** and should use the url exactly without the getTestFileUrl call. Possibly
81 // these two ways of starting the activity (launch* and start*) should be merged into one.
83 * Starts the content shell activity with the provided test url.
84 * The url is synchronously loaded.
85 * @param url Test url to load.
87 protected void startActivityWithTestUrl(String url) throws Throwable {
88 launchContentShellWithUrl(UrlUtils.getTestFileUrl(url));
89 assertNotNull(getActivity());
90 assertTrue(waitForActiveShellToBeDoneLoading());
91 assertEquals(UrlUtils.getTestFileUrl(url), getContentViewCore().getWebContents().getUrl());
95 * Starts the content shell activity with the provided test url and optional command line
96 * arguments to append.
97 * The url is synchronously loaded.
98 * @param url Test url to load.
99 * @param commandLineArgs Optional command line args to append when launching the activity.
101 protected void startActivityWithTestUrlAndCommandLineArgs(
102 String url, String[] commandLineArgs) throws Throwable {
103 launchContentShellWithUrlAndCommandLineArgs(
104 UrlUtils.getTestFileUrl(url), commandLineArgs);
105 assertNotNull(getActivity());
106 assertTrue(waitForActiveShellToBeDoneLoading());
110 * Returns the current ContentViewCore or null if there is no ContentView.
112 protected ContentViewCore getContentViewCore() {
113 return getActivity().getActiveShell().getContentViewCore();
117 * Returns the WebContents of this Shell.
119 protected WebContents getWebContents() {
120 return getActivity().getActiveShell().getWebContents();
124 * Waits for the Active shell to finish loading. This times out after
125 * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds and it shouldn't be used for long
126 * loading pages. Instead it should be used more for test initialization. The proper way
127 * to wait is to use a TestCallbackHelperContainer after the initial load is completed.
128 * @return Whether or not the Shell was actually finished loading.
129 * @throws InterruptedException
131 protected boolean waitForActiveShellToBeDoneLoading() throws InterruptedException {
132 final ContentShellActivity activity = getActivity();
134 // Wait for the Content Shell to be initialized.
135 return CriteriaHelper.pollForCriteria(new Criteria() {
137 public boolean isSatisfied() {
139 final AtomicBoolean isLoaded = new AtomicBoolean(false);
140 runTestOnUiThread(new Runnable() {
143 Shell shell = activity.getActiveShell();
145 // There are two cases here that need to be accounted for.
146 // The first is that we've just created a Shell and it isn't
147 // loading because it has no URL set yet. The second is that
148 // we've set a URL and it actually is loading.
149 isLoaded.set(!shell.isLoading()
150 && !TextUtils.isEmpty(shell.getContentViewCore()
151 .getWebContents().getUrl()));
158 return isLoaded.get();
159 } catch (Throwable e) {
163 }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
167 * Loads a URL in the specified content view.
169 * @param viewCore The content view core to load the URL in.
170 * @param callbackHelperContainer The callback helper container used to monitor progress.
171 * @param params The URL params to use.
173 protected void loadUrl(
174 final NavigationController navigationController,
175 TestCallbackHelperContainer callbackHelperContainer,
176 final LoadUrlParams params) throws Throwable {
177 handleBlockingCallbackAction(
178 callbackHelperContainer.getOnPageFinishedHelper(),
182 navigationController.loadUrl(params);
188 * Handles performing an action on the UI thread that will return when the specified callback
191 * @param callbackHelper The callback helper that will be blocked on.
192 * @param action The action to be performed on the UI thread.
194 protected void handleBlockingCallbackAction(
195 CallbackHelper callbackHelper, Runnable action) throws Throwable {
196 int currentCallCount = callbackHelper.getCallCount();
197 runTestOnUiThread(action);
198 callbackHelper.waitForCallback(
199 currentCallCount, 1, WAIT_PAGE_LOADING_TIMEOUT_SECONDS, TimeUnit.SECONDS);
202 // TODO(aelias): This method needs to be removed once http://crbug.com/179511 is fixed.
203 // Meanwhile, we have to wait if the page has the <meta viewport> tag.
205 * Waits till the ContentViewCore receives the expected page scale factor
206 * from the compositor and asserts that this happens.
208 protected void assertWaitForPageScaleFactorMatch(final float expectedScale)
209 throws InterruptedException {
210 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
212 public boolean isSatisfied() {
213 return getContentViewCore().getScale() == expectedScale;
219 * Replaces the {@link ContentViewCore#mContainerView} with a newly created
220 * {@link ContentView}.
222 @SuppressWarnings("javadoc")
223 protected void replaceContainerView() throws Throwable {
224 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
227 ContentView cv = ContentView.newInstance(getActivity(), getContentViewCore());
228 ((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv);
229 getContentViewCore().setContainerView(cv);
230 getContentViewCore().setContainerViewInternals(cv);
237 protected void runTest() throws Throwable {
240 Method method = getClass().getMethod(getName(), (Class[]) null);
241 if (method.isAnnotationPresent(RerunWithUpdatedContainerView.class)) {
242 replaceContainerView();
245 } catch (Throwable e) {
246 throw new Throwable("@RerunWithUpdatedContainerView failed."
247 + " See ContentShellTestBase#runTest.", e);
252 * Annotation for tests that should be executed a second time after replacing
253 * the ContentViewCore's container view (see {@link #runTest()}).
255 * <p>Please note that {@link #setUp()} is only invoked once before both runs,
256 * and that any state changes produced by the first run are visible to the second run.
258 @Target(ElementType.METHOD)
259 @Retention(RetentionPolicy.RUNTIME)
260 public @interface RerunWithUpdatedContainerView {