Home | History | Annotate | Download | only in content_shell_apk
      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.
      4 
      5 package org.chromium.content_shell_apk;
      6 
      7 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
      8 
      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;
     15 
     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_shell.Shell;
     26 
     27 import java.lang.annotation.ElementType;
     28 import java.lang.annotation.Retention;
     29 import java.lang.annotation.RetentionPolicy;
     30 import java.lang.annotation.Target;
     31 import java.lang.reflect.Method;
     32 import java.util.concurrent.TimeUnit;
     33 import java.util.concurrent.atomic.AtomicBoolean;
     34 
     35 /**
     36  * Base test class for all ContentShell based tests.
     37  */
     38 public class ContentShellTestBase extends ActivityInstrumentationTestCase2<ContentShellActivity> {
     39 
     40     /** The maximum time the waitForActiveShellToBeDoneLoading method will wait. */
     41     private static final long WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT = scaleTimeout(10000);
     42 
     43     protected static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS = scaleTimeout(15);
     44 
     45     public ContentShellTestBase() {
     46         super(ContentShellActivity.class);
     47     }
     48 
     49     /**
     50      * Starts the ContentShell activity and loads the given URL.
     51      * The URL can be null, in which case will default to ContentShellActivity.DEFAULT_SHELL_URL.
     52      */
     53     protected ContentShellActivity launchContentShellWithUrl(String url) {
     54         return launchContentShellWithUrlAndCommandLineArgs(url, null);
     55     }
     56 
     57     /**
     58      * Starts the ContentShell activity appending the provided command line arguments
     59      * and loads the given URL. The URL can be null, in which case will default to
     60      * ContentShellActivity.DEFAULT_SHELL_URL.
     61      */
     62     protected ContentShellActivity launchContentShellWithUrlAndCommandLineArgs(String url,
     63             String[] commandLineArgs) {
     64         Intent intent = new Intent(Intent.ACTION_MAIN);
     65         intent.addCategory(Intent.CATEGORY_LAUNCHER);
     66         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     67         if (url != null) intent.setData(Uri.parse(url));
     68         intent.setComponent(new ComponentName(getInstrumentation().getTargetContext(),
     69               ContentShellActivity.class));
     70         if (commandLineArgs != null) {
     71             intent.putExtra(ContentShellActivity.COMMAND_LINE_ARGS_KEY, commandLineArgs);
     72         }
     73         setActivityIntent(intent);
     74         return getActivity();
     75     }
     76 
     77     // TODO(cjhopman): These functions are inconsistent with launchContentShell***. Should be
     78     // startContentShell*** and should use the url exactly without the getTestFileUrl call. Possibly
     79     // these two ways of starting the activity (launch* and start*) should be merged into one.
     80     /**
     81      * Starts the content shell activity with the provided test url.
     82      * The url is synchronously loaded.
     83      * @param url Test url to load.
     84      */
     85     protected void startActivityWithTestUrl(String url) throws Throwable {
     86         launchContentShellWithUrl(UrlUtils.getTestFileUrl(url));
     87         assertNotNull(getActivity());
     88         assertTrue(waitForActiveShellToBeDoneLoading());
     89         assertEquals(UrlUtils.getTestFileUrl(url), getContentViewCore().getWebContents().getUrl());
     90     }
     91 
     92     /**
     93      * Starts the content shell activity with the provided test url and optional command line
     94      * arguments to append.
     95      * The url is synchronously loaded.
     96      * @param url Test url to load.
     97      * @param commandLineArgs Optional command line args to append when launching the activity.
     98      */
     99     protected void startActivityWithTestUrlAndCommandLineArgs(
    100             String url, String[] commandLineArgs) throws Throwable {
    101         launchContentShellWithUrlAndCommandLineArgs(
    102                 UrlUtils.getTestFileUrl(url), commandLineArgs);
    103         assertNotNull(getActivity());
    104         assertTrue(waitForActiveShellToBeDoneLoading());
    105     }
    106 
    107     /**
    108      * Returns the current ContentViewCore or null if there is no ContentView.
    109      */
    110     protected ContentViewCore getContentViewCore() {
    111         return getActivity().getActiveShell().getContentViewCore();
    112     }
    113 
    114     /**
    115      * Waits for the Active shell to finish loading.  This times out after
    116      * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds and it shouldn't be used for long
    117      * loading pages. Instead it should be used more for test initialization. The proper way
    118      * to wait is to use a TestCallbackHelperContainer after the initial load is completed.
    119      * @return Whether or not the Shell was actually finished loading.
    120      * @throws InterruptedException
    121      */
    122     protected boolean waitForActiveShellToBeDoneLoading() throws InterruptedException {
    123         final ContentShellActivity activity = getActivity();
    124 
    125         // Wait for the Content Shell to be initialized.
    126         return CriteriaHelper.pollForCriteria(new Criteria() {
    127             @Override
    128             public boolean isSatisfied() {
    129                 try {
    130                     final AtomicBoolean isLoaded = new AtomicBoolean(false);
    131                     runTestOnUiThread(new Runnable() {
    132                         @Override
    133                         public void run() {
    134                             Shell shell = activity.getActiveShell();
    135                             if (shell != null) {
    136                                 // There are two cases here that need to be accounted for.
    137                                 // The first is that we've just created a Shell and it isn't
    138                                 // loading because it has no URL set yet.  The second is that
    139                                 // we've set a URL and it actually is loading.
    140                                 isLoaded.set(!shell.isLoading()
    141                                         && !TextUtils.isEmpty(shell.getContentViewCore()
    142                                                 .getWebContents().getUrl()));
    143                             } else {
    144                                 isLoaded.set(false);
    145                             }
    146                         }
    147                     });
    148 
    149                     return isLoaded.get();
    150                 } catch (Throwable e) {
    151                     return false;
    152                 }
    153             }
    154         }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
    155     }
    156 
    157     /**
    158      * Loads a URL in the specified content view.
    159      *
    160      * @param viewCore The content view core to load the URL in.
    161      * @param callbackHelperContainer The callback helper container used to monitor progress.
    162      * @param params The URL params to use.
    163      */
    164     protected void loadUrl(
    165             final ContentViewCore viewCore, TestCallbackHelperContainer callbackHelperContainer,
    166             final LoadUrlParams params) throws Throwable {
    167         handleBlockingCallbackAction(
    168                 callbackHelperContainer.getOnPageFinishedHelper(),
    169                 new Runnable() {
    170                     @Override
    171                     public void run() {
    172                         viewCore.getWebContents().getNavigationController().loadUrl(params);
    173                     }
    174                 });
    175     }
    176 
    177     /**
    178      * Handles performing an action on the UI thread that will return when the specified callback
    179      * is incremented.
    180      *
    181      * @param callbackHelper The callback helper that will be blocked on.
    182      * @param action The action to be performed on the UI thread.
    183      */
    184     protected void handleBlockingCallbackAction(
    185             CallbackHelper callbackHelper, Runnable action) throws Throwable {
    186         int currentCallCount = callbackHelper.getCallCount();
    187         runTestOnUiThread(action);
    188         callbackHelper.waitForCallback(
    189                 currentCallCount, 1, WAIT_PAGE_LOADING_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    190     }
    191 
    192     // TODO(aelias): This method needs to be removed once http://crbug.com/179511 is fixed.
    193     // Meanwhile, we have to wait if the page has the <meta viewport> tag.
    194     /**
    195      * Waits till the ContentViewCore receives the expected page scale factor
    196      * from the compositor and asserts that this happens.
    197      */
    198     protected void assertWaitForPageScaleFactorMatch(final float expectedScale)
    199             throws InterruptedException {
    200         assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
    201             @Override
    202             public boolean isSatisfied() {
    203                 return getContentViewCore().getScale() == expectedScale;
    204             }
    205         }));
    206     }
    207 
    208     /**
    209      * Replaces the {@link ContentViewCore#mContainerView} with a newly created
    210      * {@link ContentView}.
    211      */
    212     @SuppressWarnings("javadoc")
    213     protected void replaceContainerView() throws Throwable {
    214         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
    215                 @Override
    216             public void run() {
    217                 ContentView cv = ContentView.newInstance(getActivity(), getContentViewCore());
    218                 ((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv);
    219                 getContentViewCore().setContainerView(cv);
    220                 getContentViewCore().setContainerViewInternals(cv);
    221                 cv.requestFocus();
    222             }
    223         });
    224     }
    225 
    226     @Override
    227     protected void runTest() throws Throwable {
    228         super.runTest();
    229         try {
    230             Method method = getClass().getMethod(getName(), (Class[]) null);
    231             if (method.isAnnotationPresent(RerunWithUpdatedContainerView.class)) {
    232                 replaceContainerView();
    233                 super.runTest();
    234             }
    235         } catch (Throwable e) {
    236             throw new Throwable("@RerunWithUpdatedContainerView failed."
    237                     + " See ContentShellTestBase#runTest.", e);
    238         }
    239     }
    240 
    241     /**
    242      * Annotation for tests that should be executed a second time after replacing
    243      * the ContentViewCore's container view (see {@link #runTest()}).
    244      *
    245      * <p>Please note that {@link #setUp()} is only invoked once before both runs,
    246      * and that any state changes produced by the first run are visible to the second run.
    247      */
    248     @Target(ElementType.METHOD)
    249     @Retention(RetentionPolicy.RUNTIME)
    250     public @interface RerunWithUpdatedContainerView {
    251     }
    252 }
    253