[Android] Create AWAITING state, use in TabModelConditions
Gated Conditions's checks are now delayed until the gate can be evaluated yet to tell if the gated Condition is required, returning fulfilled() or notFulfilled() instead of awaiting(). The motivation is to remove dependencies on ChromeTabbedActivityTestRule from Stations that use TabModelConditions. These Stations right now assume the ChromeTabbedActivity exists and has tab models initialized already. Bug: 339499637 Change-Id: I07ba4acf44433e1df9f7472058943dbf24b8ac49 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5529863 Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org> Commit-Queue: Henrique Nakashima <hnakashima@chromium.org> Cr-Commit-Position: refs/heads/main@{#1299552}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
529e87c7dd
commit
c6c0155629
@ -82,11 +82,11 @@ public class ActivityElement<ActivityT extends Activity>
|
||||
}
|
||||
mMatchedActivity = candidate;
|
||||
if (mMatchedActivity == null) {
|
||||
return notFulfilled("No Activity with expected class");
|
||||
return awaiting("No Activity with expected class");
|
||||
}
|
||||
|
||||
@ActivityState int state = ApplicationStatus.getStateForActivity(mMatchedActivity);
|
||||
return whether(
|
||||
return fulfilledOrAwaiting(
|
||||
state == ActivityState.RESUMED,
|
||||
"matched: %s (state=%s)",
|
||||
mMatchedActivity,
|
||||
|
@ -127,7 +127,7 @@ public abstract class Condition {
|
||||
Supplier<?> supplier = kv.getValue();
|
||||
if (!supplier.hasValue()) {
|
||||
if (suppliersMissing == null) {
|
||||
suppliersMissing = new StringBuilder("waiting for suppliers for: ");
|
||||
suppliersMissing = new StringBuilder("waiting for suppliers of: ");
|
||||
} else {
|
||||
suppliersMissing.append(", ");
|
||||
}
|
||||
@ -137,7 +137,7 @@ public abstract class Condition {
|
||||
}
|
||||
|
||||
if (suppliersMissing != null) {
|
||||
return notFulfilled(suppliersMissing.toString());
|
||||
return awaiting(suppliersMissing.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -208,4 +208,40 @@ public abstract class Condition {
|
||||
public static ConditionStatus whether(boolean isFulfilled, String message, Object... args) {
|
||||
return whether(isFulfilled, String.format(message, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #checkWithSuppliers()} should return this when it does not have information to check
|
||||
* the Condition yet.
|
||||
*
|
||||
* <p>It is considered not fulfilled for most purposes. The exception is that if the Condition
|
||||
* is used as a gate Condition, the gated Condition will not be checked, considered FULFILLED,
|
||||
* or considered NOT_FULFILLED until the gate resolves to FULFILLED or NOT_FULFILLED.
|
||||
*
|
||||
* @param message A short message stating what is being awaited for
|
||||
*/
|
||||
public static ConditionStatus awaiting(@Nullable String message) {
|
||||
return new ConditionStatus(Status.AWAITING, message);
|
||||
}
|
||||
|
||||
/** {@link #awaiting(String)} with format parameters. */
|
||||
@FormatMethod
|
||||
public static ConditionStatus awaiting(String message, Object... args) {
|
||||
return new ConditionStatus(Status.AWAITING, String.format(message, args));
|
||||
}
|
||||
|
||||
/** {@link #checkWithSuppliers()} can return this as a convenience method. */
|
||||
public static ConditionStatus fulfilledOrAwaiting(
|
||||
boolean isFulfilled, @Nullable String message) {
|
||||
return isFulfilled ? fulfilled(message) : awaiting(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #fulfilledOrAwaiting(boolean, String)} with more details to be logged as a short
|
||||
* message.
|
||||
*/
|
||||
@FormatMethod
|
||||
public static ConditionStatus fulfilledOrAwaiting(
|
||||
boolean isFulfilled, String message, Object... args) {
|
||||
return fulfilledOrAwaiting(isFulfilled, String.format(message, args));
|
||||
}
|
||||
}
|
||||
|
@ -26,13 +26,15 @@ public class ConditionStatus {
|
||||
@IntDef({
|
||||
ConditionStatus.Status.NOT_FULFILLED,
|
||||
ConditionStatus.Status.FULFILLED,
|
||||
ConditionStatus.Status.ERROR
|
||||
ConditionStatus.Status.ERROR,
|
||||
ConditionStatus.Status.AWAITING
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Status {
|
||||
int NOT_FULFILLED = 0;
|
||||
int FULFILLED = 1;
|
||||
int ERROR = 2;
|
||||
int AWAITING = 3;
|
||||
}
|
||||
|
||||
private final long mTimestamp;
|
||||
@ -59,6 +61,10 @@ public class ConditionStatus {
|
||||
return mStatus == Status.ERROR;
|
||||
}
|
||||
|
||||
public boolean isAwaiting() {
|
||||
return mStatus == Status.AWAITING;
|
||||
}
|
||||
|
||||
public @Status int getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
@ -87,6 +93,7 @@ public class ConditionStatus {
|
||||
case Status.FULFILLED -> "REQUIRED";
|
||||
case Status.NOT_FULFILLED -> "NOT REQ";
|
||||
case Status.ERROR -> "ERROR";
|
||||
case Status.AWAITING -> "AWAITING";
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
fullMessage.append(statusMessage);
|
||||
|
@ -105,9 +105,10 @@ class StatusStore {
|
||||
|
||||
private String getStatusPrefix() {
|
||||
return switch (mStatus) {
|
||||
case Status.FULFILLED -> "OK |";
|
||||
case Status.NOT_FULFILLED -> "NO |";
|
||||
case Status.ERROR -> "ERR |";
|
||||
case Status.FULFILLED -> "OK |";
|
||||
case Status.NOT_FULFILLED -> "NO |";
|
||||
case Status.ERROR -> "ERR |";
|
||||
case Status.AWAITING -> "WAIT |";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + mStatus);
|
||||
};
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ public class ViewConditions {
|
||||
protected ConditionStatus checkWithSuppliers() throws Exception {
|
||||
ConditionStatus gateStatus = mGate.check();
|
||||
String gateMessage = gateStatus.getMessageAsGate();
|
||||
if (gateStatus.isAwaiting()) {
|
||||
return notFulfilled(gateMessage);
|
||||
}
|
||||
|
||||
if (!gateStatus.isFulfilled()) {
|
||||
return fulfilled(gateMessage);
|
||||
}
|
||||
|
@ -224,6 +224,7 @@ android_library("chrome_java_transit") {
|
||||
"javatests/src/org/chromium/chrome/test/transit/RegularTabSwitcherStation.java",
|
||||
"javatests/src/org/chromium/chrome/test/transit/SettingsStation.java",
|
||||
"javatests/src/org/chromium/chrome/test/transit/TabModelConditions.java",
|
||||
"javatests/src/org/chromium/chrome/test/transit/TabModelSelectorCondition.java",
|
||||
"javatests/src/org/chromium/chrome/test/transit/TabSwitcherActionMenuFacility.java",
|
||||
"javatests/src/org/chromium/chrome/test/transit/TabSwitcherStation.java",
|
||||
"javatests/src/org/chromium/chrome/test/transit/WebPageIncognitoAppMenuFacility.java",
|
||||
|
@ -64,7 +64,8 @@ public abstract class HubBaseStation extends Station {
|
||||
R.string.accessibility_tab_switcher_incognito_stack)));
|
||||
|
||||
protected final ChromeTabbedActivityTestRule mChromeTabbedActivityTestRule;
|
||||
private ActivityElement<ChromeTabbedActivity> mActivityElement;
|
||||
protected ActivityElement<ChromeTabbedActivity> mActivityElement;
|
||||
protected TabModelSelectorCondition mTabModelSelectorCondition;
|
||||
|
||||
/**
|
||||
* @param chromeTabbedActivityTestRule The {@link ChromeTabbedActivityTestRule} of the test.
|
||||
@ -81,13 +82,15 @@ public abstract class HubBaseStation extends Station {
|
||||
@Override
|
||||
public void declareElements(Elements.Builder elements) {
|
||||
mActivityElement = elements.declareActivity(ChromeTabbedActivity.class);
|
||||
mTabModelSelectorCondition =
|
||||
elements.declareEnterCondition(new TabModelSelectorCondition(mActivityElement));
|
||||
|
||||
elements.declareView(HUB_TOOLBAR);
|
||||
elements.declareView(HUB_PANE_HOST);
|
||||
elements.declareView(HUB_MENU_BUTTON);
|
||||
|
||||
Condition incognitoTabsExist =
|
||||
TabModelConditions.anyIncognitoTabsExist(mChromeTabbedActivityTestRule);
|
||||
TabModelConditions.anyIncognitoTabsExist(mTabModelSelectorCondition);
|
||||
elements.declareViewIf(REGULAR_TOGGLE_TAB_BUTTON, incognitoTabsExist);
|
||||
elements.declareViewIf(INCOGNITO_TOGGLE_TAB_BUTTON, incognitoTabsExist);
|
||||
|
||||
@ -169,8 +172,7 @@ public abstract class HubBaseStation extends Station {
|
||||
// TODO(crbug.com/40287437): Content description seems reasonable for now, this might get
|
||||
// harder
|
||||
// once we use a recycler view with text based buttons.
|
||||
String contentDescription =
|
||||
mChromeTabbedActivityTestRule.getActivity().getString(contentDescriptionRes);
|
||||
String contentDescription = mActivityElement.get().getString(contentDescriptionRes);
|
||||
onView(
|
||||
allOf(
|
||||
isDescendantOfA(HUB_PANE_SWITCHER.getViewMatcher()),
|
||||
|
@ -37,6 +37,6 @@ public class HubTabSwitcherStation extends HubTabSwitcherBaseStation {
|
||||
|
||||
elements.declareViewIf(
|
||||
EMPTY_STATE_TEXT,
|
||||
TabModelConditions.noRegularTabsExist(mChromeTabbedActivityTestRule));
|
||||
TabModelConditions.noRegularTabsExist(mTabModelSelectorCondition));
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ public class RegularTabSwitcherStation extends TabSwitcherStation {
|
||||
super.declareElements(elements);
|
||||
|
||||
Condition noRegularTabsExist =
|
||||
TabModelConditions.noRegularTabsExist(mChromeTabbedActivityTestRule);
|
||||
TabModelConditions.noRegularTabsExist(mTabModelSelectorCondition);
|
||||
elements.declareViewIf(EMPTY_STATE_TEXT, noRegularTabsExist);
|
||||
|
||||
Condition incognitoTabsExist =
|
||||
TabModelConditions.anyIncognitoTabsExist(mChromeTabbedActivityTestRule);
|
||||
TabModelConditions.anyIncognitoTabsExist(mTabModelSelectorCondition);
|
||||
elements.declareViewIf(INCOGNITO_TOGGLE_TABS, incognitoTabsExist);
|
||||
elements.declareViewIf(REGULAR_TOGGLE_TAB_BUTTON, incognitoTabsExist);
|
||||
elements.declareViewIf(INCOGNITO_TOGGLE_TAB_BUTTON, incognitoTabsExist);
|
||||
|
@ -4,49 +4,54 @@
|
||||
|
||||
package org.chromium.chrome.test.transit;
|
||||
|
||||
import org.chromium.base.supplier.Supplier;
|
||||
import org.chromium.base.test.transit.Condition;
|
||||
import org.chromium.base.test.transit.ConditionStatus;
|
||||
import org.chromium.base.test.transit.UiThreadCondition;
|
||||
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
||||
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
|
||||
|
||||
/** {@link Condition}s regarding the state of TabModels. */
|
||||
public class TabModelConditions {
|
||||
/** Fulfilled when no regular tabs are open. */
|
||||
public static Condition noRegularTabsExist(ChromeTabbedActivityTestRule testRule) {
|
||||
return new NoTabsExistCondition(testRule, /* incognito= */ false);
|
||||
public static Condition noRegularTabsExist(
|
||||
Supplier<TabModelSelector> tabModelSelectorSupplier) {
|
||||
return new NoTabsExistCondition(tabModelSelectorSupplier, /* incognito= */ false);
|
||||
}
|
||||
|
||||
/** Fulfilled when no incognito tabs are open. */
|
||||
public static Condition noIncognitoTabsExist(ChromeTabbedActivityTestRule testRule) {
|
||||
return new NoTabsExistCondition(testRule, /* incognito= */ true);
|
||||
public static Condition noIncognitoTabsExist(
|
||||
Supplier<TabModelSelector> tabModelSelectorSupplier) {
|
||||
return new NoTabsExistCondition(tabModelSelectorSupplier, /* incognito= */ true);
|
||||
}
|
||||
|
||||
/** Fulfilled when one or more regular tabs are open. */
|
||||
public static Condition anyRegularTabsExist(ChromeTabbedActivityTestRule testRule) {
|
||||
return new AnyTabsExistCondition(testRule, /* incognito= */ false);
|
||||
public static Condition anyRegularTabsExist(
|
||||
Supplier<TabModelSelector> tabModelSelectorSupplier) {
|
||||
return new AnyTabsExistCondition(tabModelSelectorSupplier, /* incognito= */ false);
|
||||
}
|
||||
|
||||
/** Fulfilled when one or more incognito tabs are open. */
|
||||
public static Condition anyIncognitoTabsExist(ChromeTabbedActivityTestRule testRule) {
|
||||
return new AnyTabsExistCondition(testRule, /* incognito= */ true);
|
||||
public static Condition anyIncognitoTabsExist(
|
||||
Supplier<TabModelSelector> tabModelSelectorSupplier) {
|
||||
return new AnyTabsExistCondition(tabModelSelectorSupplier, /* incognito= */ true);
|
||||
}
|
||||
|
||||
private static class NoTabsExistCondition extends UiThreadCondition {
|
||||
|
||||
private final ChromeTabbedActivityTestRule mChromeTabbedActivityTestRule;
|
||||
private final Supplier<TabModelSelector> mTabModelSelectorSupplier;
|
||||
private final boolean mIncognito;
|
||||
private final String mTabType;
|
||||
|
||||
public NoTabsExistCondition(
|
||||
ChromeTabbedActivityTestRule chromeTabbedActivityTestRule, boolean incognito) {
|
||||
mChromeTabbedActivityTestRule = chromeTabbedActivityTestRule;
|
||||
Supplier<TabModelSelector> tabModelSelectorSupplier, boolean incognito) {
|
||||
mTabModelSelectorSupplier =
|
||||
dependOnSupplier(tabModelSelectorSupplier, "TabModelSelector");
|
||||
mIncognito = incognito;
|
||||
mTabType = incognito ? "incognito" : "regular";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionStatus checkWithSuppliers() {
|
||||
int tabCount = mChromeTabbedActivityTestRule.tabsCount(mIncognito);
|
||||
int tabCount = mTabModelSelectorSupplier.get().getModel(mIncognito).getCount();
|
||||
return whether(tabCount == 0, "%d %s tabs", tabCount, mTabType);
|
||||
}
|
||||
|
||||
@ -57,21 +62,21 @@ public class TabModelConditions {
|
||||
}
|
||||
|
||||
private static class AnyTabsExistCondition extends UiThreadCondition {
|
||||
|
||||
private final ChromeTabbedActivityTestRule mChromeTabbedActivityTestRule;
|
||||
private final Supplier<TabModelSelector> mTabModelSelectorSupplier;
|
||||
private final boolean mIncognito;
|
||||
private final String mTabType;
|
||||
|
||||
public AnyTabsExistCondition(
|
||||
ChromeTabbedActivityTestRule chromeTabbedActivityTestRule, boolean incognito) {
|
||||
mChromeTabbedActivityTestRule = chromeTabbedActivityTestRule;
|
||||
Supplier<TabModelSelector> tabModelSelectorSupplier, boolean incognito) {
|
||||
mTabModelSelectorSupplier =
|
||||
dependOnSupplier(tabModelSelectorSupplier, "TabModelSelector");
|
||||
mIncognito = incognito;
|
||||
mTabType = incognito ? "incognito" : "regular";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionStatus checkWithSuppliers() {
|
||||
int tabCount = mChromeTabbedActivityTestRule.tabsCount(mIncognito);
|
||||
int tabCount = mTabModelSelectorSupplier.get().getModel(mIncognito).getCount();
|
||||
return whether(tabCount > 0, "%d %s tabs", tabCount, mTabType);
|
||||
}
|
||||
|
||||
|
44
chrome/test/android/javatests/src/org/chromium/chrome/test/transit/TabModelSelectorCondition.java
Normal file
44
chrome/test/android/javatests/src/org/chromium/chrome/test/transit/TabModelSelectorCondition.java
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.chrome.test.transit;
|
||||
|
||||
import org.chromium.base.supplier.Supplier;
|
||||
import org.chromium.base.test.transit.ConditionStatus;
|
||||
import org.chromium.base.test.transit.UiThreadCondition;
|
||||
import org.chromium.chrome.browser.ChromeTabbedActivity;
|
||||
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
|
||||
|
||||
/** Condition fulfilled when an initialized TabModel exists. */
|
||||
public class TabModelSelectorCondition extends UiThreadCondition
|
||||
implements Supplier<TabModelSelector> {
|
||||
|
||||
private final Supplier<ChromeTabbedActivity> mActivitySupplier;
|
||||
private TabModelSelector mSelector;
|
||||
|
||||
public TabModelSelectorCondition(Supplier<ChromeTabbedActivity> activitySupplier) {
|
||||
mActivitySupplier = activitySupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionStatus checkWithSuppliers() throws Exception {
|
||||
TabModelSelector selector = mActivitySupplier.get().getTabModelSelectorSupplier().get();
|
||||
if (selector != null) {
|
||||
mSelector = selector;
|
||||
return fulfilled();
|
||||
} else {
|
||||
return awaiting("Activity has no TabModelSelector");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabModelSelector get() {
|
||||
return mSelector;
|
||||
}
|
||||
}
|
@ -96,7 +96,8 @@ public abstract class TabSwitcherStation extends Station {
|
||||
|
||||
protected final ChromeTabbedActivityTestRule mChromeTabbedActivityTestRule;
|
||||
protected final boolean mIsIncognito;
|
||||
private ActivityElement<ChromeTabbedActivity> mActivityElement;
|
||||
protected ActivityElement<ChromeTabbedActivity> mActivityElement;
|
||||
protected TabModelSelectorCondition mTabModelSelectorCondition;
|
||||
|
||||
/** Instantiate one of the subclasses instead. */
|
||||
protected TabSwitcherStation(
|
||||
@ -112,6 +113,8 @@ public abstract class TabSwitcherStation extends Station {
|
||||
@CallSuper
|
||||
public void declareElements(Elements.Builder elements) {
|
||||
mActivityElement = elements.declareActivity(ChromeTabbedActivity.class);
|
||||
mTabModelSelectorCondition =
|
||||
elements.declareEnterCondition(new TabModelSelectorCondition(mActivityElement));
|
||||
|
||||
elements.declareView(TOOLBAR);
|
||||
elements.declareView(TOOLBAR_NEW_TAB_BUTTON);
|
||||
|
Reference in New Issue
Block a user