mobile/android/base/tests/helpers/WaitHelper.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.tests.helpers;
michael@0 6
michael@0 7 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
michael@0 8 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
michael@0 9
michael@0 10 import java.util.regex.Pattern;
michael@0 11
michael@0 12 import org.mozilla.gecko.Actions;
michael@0 13 import org.mozilla.gecko.Actions.EventExpecter;
michael@0 14 import org.mozilla.gecko.tests.UITestContext;
michael@0 15 import org.mozilla.gecko.tests.UITestContext.ComponentType;
michael@0 16 import org.mozilla.gecko.tests.components.ToolbarComponent;
michael@0 17
michael@0 18 import com.jayway.android.robotium.solo.Condition;
michael@0 19 import com.jayway.android.robotium.solo.Solo;
michael@0 20
michael@0 21 /**
michael@0 22 * Provides functionality related to waiting on certain events to happen.
michael@0 23 */
michael@0 24 public final class WaitHelper {
michael@0 25 // TODO: Make public for when Solo.waitForCondition is used directly (i.e. do not want
michael@0 26 // assertion from waitFor)?
michael@0 27 private static final int DEFAULT_MAX_WAIT_MS = 5000;
michael@0 28 private static final int PAGE_LOAD_WAIT_MS = 10000;
michael@0 29 private static final int CHANGE_WAIT_MS = 10000;
michael@0 30
michael@0 31 // TODO: via lucasr - Add ThrobberVisibilityChangeVerifier?
michael@0 32 private static final ChangeVerifier[] PAGE_LOAD_VERIFIERS = new ChangeVerifier[] {
michael@0 33 new ToolbarTitleTextChangeVerifier()
michael@0 34 };
michael@0 35
michael@0 36 private static UITestContext sContext;
michael@0 37 private static Solo sSolo;
michael@0 38 private static Actions sActions;
michael@0 39
michael@0 40 private static ToolbarComponent sToolbar;
michael@0 41
michael@0 42 private WaitHelper() { /* To disallow instantiation. */ }
michael@0 43
michael@0 44 protected static void init(final UITestContext context) {
michael@0 45 sContext = context;
michael@0 46 sSolo = context.getSolo();
michael@0 47 sActions = context.getActions();
michael@0 48
michael@0 49 sToolbar = (ToolbarComponent) context.getComponent(ComponentType.TOOLBAR);
michael@0 50 }
michael@0 51
michael@0 52 /**
michael@0 53 * Waits for the given {@link solo.Condition} using the default wait duration; will throw an
michael@0 54 * AssertionError if the duration is elapsed and the condition is not satisfied.
michael@0 55 */
michael@0 56 public static void waitFor(String message, final Condition condition) {
michael@0 57 message = "Waiting for " + message + ".";
michael@0 58 fAssertTrue(message, sSolo.waitForCondition(condition, DEFAULT_MAX_WAIT_MS));
michael@0 59 }
michael@0 60
michael@0 61 /**
michael@0 62 * Waits for the given {@link solo.Condition} using the given wait duration; will throw an
michael@0 63 * AssertionError if the duration is elapsed and the condition is not satisfied.
michael@0 64 */
michael@0 65 public static void waitFor(String message, final Condition condition, final int waitMillis) {
michael@0 66 message = "Waiting for " + message + " with timeout " + waitMillis + ".";
michael@0 67 fAssertTrue(message, sSolo.waitForCondition(condition, waitMillis));
michael@0 68 }
michael@0 69
michael@0 70 /**
michael@0 71 * Waits for the Gecko event declaring the page has loaded. Takes in and runs a Runnable
michael@0 72 * that will perform the action that will cause the page to load.
michael@0 73 */
michael@0 74 public static void waitForPageLoad(final Runnable initiatingAction) {
michael@0 75 fAssertNotNull("initiatingAction is not null", initiatingAction);
michael@0 76
michael@0 77 // Some changes to the UI occur in response to the same event we listen to for when
michael@0 78 // the page has finished loading (e.g. a page title update). As such, we ensure this
michael@0 79 // UI state has changed before returning from this method; here we store the initial
michael@0 80 // state.
michael@0 81 final ChangeVerifier[] pageLoadVerifiers = PAGE_LOAD_VERIFIERS;
michael@0 82 for (final ChangeVerifier verifier : pageLoadVerifiers) {
michael@0 83 verifier.storeState();
michael@0 84 }
michael@0 85
michael@0 86 // Wait for the page load and title changed event.
michael@0 87 final EventExpecter contentEventExpecter = sActions.expectGeckoEvent("DOMContentLoaded");
michael@0 88 final EventExpecter titleEventExpecter = sActions.expectGeckoEvent("DOMTitleChanged");
michael@0 89
michael@0 90 initiatingAction.run();
michael@0 91
michael@0 92 contentEventExpecter.blockForEventDataWithTimeout(PAGE_LOAD_WAIT_MS);
michael@0 93 contentEventExpecter.unregisterListener();
michael@0 94 titleEventExpecter.blockForEventDataWithTimeout(PAGE_LOAD_WAIT_MS);
michael@0 95 titleEventExpecter.unregisterListener();
michael@0 96
michael@0 97 // Verify remaining state has changed.
michael@0 98 for (final ChangeVerifier verifier : pageLoadVerifiers) {
michael@0 99 // If we timeout, either the state is set to the same value (which is fine), or
michael@0 100 // the state has not yet changed. Since we can't be sure it will ever change, move
michael@0 101 // on and let the assertions fail if applicable.
michael@0 102 final boolean hasTimedOut = !sSolo.waitForCondition(new Condition() {
michael@0 103 @Override
michael@0 104 public boolean isSatisfied() {
michael@0 105 return verifier.hasStateChanged();
michael@0 106 }
michael@0 107 }, CHANGE_WAIT_MS);
michael@0 108
michael@0 109 sContext.dumpLog(verifier.getLogTag(),
michael@0 110 (hasTimedOut ? "timed out." : "was satisfied."));
michael@0 111 }
michael@0 112 }
michael@0 113
michael@0 114 /**
michael@0 115 * Implementations of this interface verify that the state of the test has changed from
michael@0 116 * the invocation of storeState to the invocation of hasStateChanged. A boolean will be
michael@0 117 * returned from hasStateChanged, indicating this change of status.
michael@0 118 */
michael@0 119 private static interface ChangeVerifier {
michael@0 120 public String getLogTag();
michael@0 121
michael@0 122 /**
michael@0 123 * Stores the initial state of the system. This system state is used to diff against
michael@0 124 * the end state to determine if the system has changed. Since this is just a diff
michael@0 125 * (with a timeout), this method could potentially store state inconsistent with
michael@0 126 * what is visible to the user.
michael@0 127 */
michael@0 128 public void storeState();
michael@0 129 public boolean hasStateChanged();
michael@0 130 }
michael@0 131
michael@0 132 private static class ToolbarTitleTextChangeVerifier implements ChangeVerifier {
michael@0 133 private static final String LOGTAG = ToolbarTitleTextChangeVerifier.class.getSimpleName();
michael@0 134
michael@0 135 // A regex that matches the page title that shows up while the page is loading.
michael@0 136 private static final Pattern LOADING_PREFIX = Pattern.compile("[A-Za-z]{3,9}://");
michael@0 137
michael@0 138 private CharSequence mOldTitleText;
michael@0 139
michael@0 140 @Override
michael@0 141 public String getLogTag() {
michael@0 142 return LOGTAG;
michael@0 143 }
michael@0 144
michael@0 145 @Override
michael@0 146 public void storeState() {
michael@0 147 mOldTitleText = sToolbar.getPotentiallyInconsistentTitle();
michael@0 148 sContext.dumpLog(LOGTAG, "stored title, \"" + mOldTitleText + "\".");
michael@0 149 }
michael@0 150
michael@0 151 @Override
michael@0 152 public boolean hasStateChanged() {
michael@0 153 // TODO: Additionally, consider Solo.waitForText.
michael@0 154 // TODO: Robocop sleeps .5 sec between calls. Cache title view?
michael@0 155 final CharSequence title = sToolbar.getPotentiallyInconsistentTitle();
michael@0 156
michael@0 157 // TODO: Handle the case where the URL is shown instead of page title by preference.
michael@0 158 // HACK: We want to wait until the title changes to the state a tester may assert
michael@0 159 // (e.g. the page title). However, the title is set to the URL before the title is
michael@0 160 // loaded from the server and set as the final page title; we ignore the
michael@0 161 // intermediate URL loading state here.
michael@0 162 final boolean isLoading = LOADING_PREFIX.matcher(title).lookingAt();
michael@0 163 final boolean hasStateChanged = !isLoading && !mOldTitleText.equals(title);
michael@0 164
michael@0 165 if (hasStateChanged) {
michael@0 166 sContext.dumpLog(LOGTAG, "state changed to title, \"" + title + "\".");
michael@0 167 }
michael@0 168 return hasStateChanged;
michael@0 169 }
michael@0 170 }
michael@0 171 }

mercurial