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