 Chromium Code Reviews
 Chromium Code Reviews Issue 2632043002:
  Create ContentShellActivityTestRule and BaseJUnitRunner  (Closed)
    
  
    Issue 2632043002:
  Create ContentShellActivityTestRule and BaseJUnitRunner  (Closed) 
  | Index: content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java | 
| diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..5e5beac28bc60e6e3eb45c1e4205a69039e23649 | 
| --- /dev/null | 
| +++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java | 
| @@ -0,0 +1,280 @@ | 
| +// Copyright 2012 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +package org.chromium.content_shell_apk; | 
| + | 
| +import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout; | 
| + | 
| +import android.annotation.TargetApi; | 
| +import android.content.ComponentName; | 
| +import android.content.Context; | 
| +import android.content.Intent; | 
| +import android.net.Uri; | 
| +import android.os.Build; | 
| +import android.os.PowerManager; | 
| +import android.support.test.InstrumentationRegistry; | 
| +import android.support.test.rule.ActivityTestRule; | 
| +import android.text.TextUtils; | 
| +import android.view.ViewGroup; | 
| + | 
| +import org.junit.Assert; | 
| +import org.junit.runner.Description; | 
| +import org.junit.runners.model.Statement; | 
| + | 
| +import org.chromium.base.ThreadUtils; | 
| +import org.chromium.base.test.util.CallbackHelper; | 
| +import org.chromium.base.test.util.UrlUtils; | 
| +import org.chromium.content.browser.ContentView; | 
| +import org.chromium.content.browser.ContentViewCore; | 
| +import org.chromium.content.browser.test.util.Criteria; | 
| +import org.chromium.content.browser.test.util.CriteriaHelper; | 
| +import org.chromium.content.browser.test.util.TestCallbackHelperContainer; | 
| +import org.chromium.content_public.browser.LoadUrlParams; | 
| +import org.chromium.content_public.browser.NavigationController; | 
| +import org.chromium.content_public.browser.WebContents; | 
| +import org.chromium.content_shell.Shell; | 
| + | 
| +import java.lang.annotation.ElementType; | 
| +import java.lang.annotation.Retention; | 
| +import java.lang.annotation.RetentionPolicy; | 
| +import java.lang.annotation.Target; | 
| +import java.util.concurrent.Callable; | 
| +import java.util.concurrent.ExecutionException; | 
| +import java.util.concurrent.TimeUnit; | 
| + | 
| +public class ContentShellActivityTestRule extends ActivityTestRule<ContentShellActivity> { | 
| 
jbudorick
2017/01/18 15:10:09
This needs a javadoc comment.
 
the real yoland
2017/02/22 00:44:57
Done
 | 
| + | 
| + private static final long WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT = scaleTimeout(10000); | 
| + | 
| + protected static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS = scaleTimeout(15); | 
| + | 
| + /** | 
| + * @param activityClass | 
| + */ | 
| + public ContentShellActivityTestRule() { | 
| + super(ContentShellActivity.class); | 
| + } | 
| + | 
| + @Override | 
| + protected void beforeActivityLaunched() { | 
| + assertScreenIsOn(); | 
| + } | 
| + | 
| + @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) | 
| + @SuppressWarnings("deprecation") | 
| + private void assertScreenIsOn() { | 
| + PowerManager pm = (PowerManager) InstrumentationRegistry.getContext().getSystemService( | 
| + Context.POWER_SERVICE); | 
| + | 
| + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { | 
| + Assert.assertTrue("Many tests will fail if the screen is not on.", pm.isInteractive()); | 
| + } else { | 
| + Assert.assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn()); | 
| + } | 
| + } | 
| + | 
| + /** | 
| + * Starts the ContentShell activity and loads the given URL. | 
| + * The URL can be null, in which case will default to ContentShellActivity.DEFAULT_SHELL_URL. | 
| + */ | 
| + protected ContentShellActivity launchContentShellWithUrl(String url) { | 
| 
jbudorick
2017/01/18 15:10:09
So a test that wanted to call this would call
  m
 
the real yoland
2017/02/22 00:44:57
yes
 | 
| + Intent intent = new Intent(Intent.ACTION_MAIN); | 
| + intent.addCategory(Intent.CATEGORY_LAUNCHER); | 
| + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | 
| + if (url != null) intent.setData(Uri.parse(url)); | 
| + intent.setComponent( | 
| + new ComponentName(InstrumentationRegistry.getInstrumentation().getTargetContext(), | 
| + ContentShellActivity.class)); | 
| + return launchActivity(intent); | 
| + } | 
| + | 
| + // TODO(cjhopman): These functions are inconsistent with launchContentShell***. Should be | 
| 
jbudorick
2017/01/18 15:10:09
Do this.
 
the real yoland
2017/02/22 00:44:57
Done
 | 
| + // startContentShell*** and should use the url exactly without the getTestFileUrl call. Possibly | 
| + // these two ways of starting the activity (launch* and start*) should be merged into one. | 
| + /** | 
| + * Starts the content shell activity with the provided test url. | 
| + * The url is synchronously loaded. | 
| + * @param url Test url to load. | 
| + */ | 
| + protected void startActivityWithTestUrl(String url) { | 
| + launchContentShellWithUrl(UrlUtils.getIsolatedTestFileUrl(url)); | 
| + Assert.assertNotNull(getActivity()); | 
| + waitForActiveShellToBeDoneLoading(); | 
| + Assert.assertEquals(UrlUtils.getIsolatedTestFileUrl(url), | 
| + getContentViewCore().getWebContents().getUrl()); | 
| + } | 
| + | 
| + /** | 
| + * Returns the current ContentViewCore or null if there is no ContentView. | 
| + */ | 
| + protected ContentViewCore getContentViewCore() { | 
| + return getActivity().getActiveShell().getContentViewCore(); | 
| + } | 
| + | 
| + /** | 
| + * Returns the WebContents of this Shell. | 
| + */ | 
| + protected WebContents getWebContents() { | 
| + return getActivity().getActiveShell().getWebContents(); | 
| + } | 
| + | 
| + /** | 
| + * Waits for the Active shell to finish loading. This times out after | 
| + * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds and it shouldn't be used for long | 
| + * loading pages. Instead it should be used more for test initialization. The proper way | 
| + * to wait is to use a TestCallbackHelperContainer after the initial load is completed. | 
| + */ | 
| + protected void waitForActiveShellToBeDoneLoading() { | 
| + final ContentShellActivity activity = getActivity(); | 
| + | 
| + // Wait for the Content Shell to be initialized. | 
| + CriteriaHelper.pollUiThread(new Criteria() { | 
| + @Override | 
| + public boolean isSatisfied() { | 
| + Shell shell = activity.getActiveShell(); | 
| + // There are two cases here that need to be accounted for. | 
| + // The first is that we've just created a Shell and it isn't | 
| + // loading because it has no URL set yet. The second is that | 
| + // we've set a URL and it actually is loading. | 
| + if (shell == null) { | 
| + updateFailureReason("Shell is null."); | 
| + return false; | 
| + } | 
| + if (shell.isLoading()) { | 
| + updateFailureReason("Shell is still loading."); | 
| + return false; | 
| + } | 
| + if (TextUtils.isEmpty(shell.getContentViewCore().getWebContents().getUrl())) { | 
| + updateFailureReason("Shell's URL is empty or null."); | 
| + return false; | 
| + } | 
| + return true; | 
| + } | 
| + }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL); | 
| + } | 
| + | 
| + /** | 
| + * Creates a new {@link Shell} and waits for it to finish loading. | 
| + * @param url The URL to create the new {@link Shell} with. | 
| + * @return A new instance of a {@link Shell}. | 
| + * @throws ExecutionException | 
| + */ | 
| + protected Shell loadNewShell(final String url) throws ExecutionException { | 
| + Shell shell = ThreadUtils.runOnUiThreadBlocking(new Callable<Shell>() { | 
| + @Override | 
| + public Shell call() { | 
| + getActivity().getShellManager().launchShell(url); | 
| + return getActivity().getActiveShell(); | 
| + } | 
| + }); | 
| + | 
| + Assert.assertNotNull("Unable to create shell.", shell); | 
| + Assert.assertEquals("Active shell unexpected.", shell, getActivity().getActiveShell()); | 
| + | 
| + waitForActiveShellToBeDoneLoading(); | 
| + | 
| + return shell; | 
| + } | 
| + /** | 
| 
jbudorick
2017/01/18 15:10:09
nit: +1 blank line before this
 
the real yoland
2017/02/22 00:44:57
Done
 | 
| + * Loads a URL in the specified content view. | 
| + * | 
| + * @param navigationController The navigation controller to load the URL in. | 
| + * @param callbackHelperContainer The callback helper container used to monitor progress. | 
| + * @param params The URL params to use. | 
| + */ | 
| + protected void loadUrl( | 
| + final NavigationController navigationController, | 
| + TestCallbackHelperContainer callbackHelperContainer, | 
| + final LoadUrlParams params) throws Throwable { | 
| + handleBlockingCallbackAction( | 
| + callbackHelperContainer.getOnPageFinishedHelper(), | 
| + new Runnable() { | 
| + @Override | 
| + public void run() { | 
| + navigationController.loadUrl(params); | 
| + } | 
| + }); | 
| + } | 
| + | 
| + /** | 
| + * Handles performing an action on the UI thread that will return when the specified callback | 
| + * is incremented. | 
| + * | 
| + * @param callbackHelper The callback helper that will be blocked on. | 
| + * @param action The action to be performed on the UI thread. | 
| + */ | 
| + protected void handleBlockingCallbackAction( | 
| + CallbackHelper callbackHelper, Runnable action) throws Throwable { | 
| + int currentCallCount = callbackHelper.getCallCount(); | 
| + runOnUiThread(action); | 
| + callbackHelper.waitForCallback( | 
| + currentCallCount, 1, WAIT_PAGE_LOADING_TIMEOUT_SECONDS, TimeUnit.SECONDS); | 
| + } | 
| + | 
| + // TODO(aelias): This method needs to be removed once http://crbug.com/179511 is fixed. | 
| 
jbudorick
2017/01/18 15:10:09
Can you check if we still need this method? 179511
 
the real yoland
2017/02/22 00:44:57
This is still used https://cs.chromium.org/chromiu
 | 
| + // Meanwhile, we have to wait if the page has the <meta viewport> tag. | 
| + /** | 
| + * Waits till the ContentViewCore receives the expected page scale factor | 
| + * from the compositor and asserts that this happens. | 
| + */ | 
| + protected void assertWaitForPageScaleFactorMatch(float expectedScale) { | 
| + CriteriaHelper.pollInstrumentationThread( | 
| + Criteria.equals(expectedScale, new Callable<Float>() { | 
| + @Override | 
| + public Float call() { | 
| + return getContentViewCore().getScale(); | 
| + } | 
| + })); | 
| + } | 
| + | 
| + /** | 
| + * Replaces the {@link ContentViewCore#mContainerView} with a newly created | 
| + * {@link ContentView}. | 
| + */ | 
| + @SuppressWarnings("javadoc") | 
| + protected void replaceContainerView() throws Throwable { | 
| + ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 
| + @Override | 
| + public void run() { | 
| + ContentView cv = ContentView.createContentView(getActivity(), getContentViewCore()); | 
| + ((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv); | 
| + getContentViewCore().setContainerView(cv); | 
| + getContentViewCore().setContainerViewInternals(cv); | 
| + cv.requestFocus(); | 
| + } | 
| + }); | 
| + } | 
| + | 
| + //TODO(yolandyan): this should be done through parameterized test | 
| 
jbudorick
2017/01/18 15:10:08
This CL should wait on parameterized tests landing
 
the real yoland
2017/02/22 00:44:57
This is actually no longer true, parameterized tes
 | 
| + @Override | 
| + public Statement apply(final Statement base, final Description desc) { | 
| + return super.apply(new Statement() { | 
| + @Override | 
| + public void evaluate() throws Throwable { | 
| + base.evaluate(); | 
| + try { | 
| + if (desc.getAnnotation(RerunWithUpdatedContainerView.class) != null) { | 
| + replaceContainerView(); | 
| + base.evaluate(); | 
| + } | 
| + } catch (Throwable e) { | 
| + throw new Throwable("@RerunWithUpdatedContainerView failed." | 
| + + " See ContentShellTestBase#runTest.", e); | 
| + } | 
| + } | 
| + }, desc); | 
| + } | 
| + | 
| + /** | 
| + * Annotation for tests that should be executed a second time after replacing | 
| + * the ContentViewCore's container view (see {@link #apply()}). | 
| + * | 
| + * <p>Please note that activity launch is only invoked once before both runs, | 
| + * and that any state changes produced by the first run are visible to the second run. | 
| + */ | 
| + @Target(ElementType.METHOD) | 
| + @Retention(RetentionPolicy.RUNTIME) | 
| + public @interface RerunWithUpdatedContainerView { | 
| + } | 
| +} |