Bug 1247366 - AppMenuComponent.findAppMenuItemView(): Wait for views and not for text to appear. r?margaret draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Fri, 12 Feb 2016 21:00:01 +0100
changeset 330925 9f0b5c7d5be349304a8870c7ea4da232c9186506
parent 330717 20c7c84490ed0e3b4d0826b4e0a06d9ca5d53824
child 514268 f23dc021bcea10655966a07fdcc63dc37069b92d
push id10858
push users.kaspari@gmail.com
push dateSun, 14 Feb 2016 13:39:55 +0000
reviewersmargaret
bugs1247366
milestone47.0a1
Bug 1247366 - AppMenuComponent.findAppMenuItemView(): Wait for views and not for text to appear. r?margaret MozReview-Commit-ID: Hb5BHnu15nm
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/WaitHelper.java
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.tests.components;
 
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertEquals;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertFalse;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
 
 import java.util.List;
+import java.util.concurrent.Callable;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.menu.MenuItemActionBar;
 import org.mozilla.gecko.menu.MenuItemDefault;
 import org.mozilla.gecko.tests.UITestContext;
 import org.mozilla.gecko.tests.helpers.DeviceHelper;
 import org.mozilla.gecko.tests.helpers.RobotiumHelper;
@@ -28,17 +29,17 @@ import android.widget.RelativeLayout;
 import com.jayway.android.robotium.solo.Condition;
 import com.jayway.android.robotium.solo.RobotiumUtils;
 import com.jayway.android.robotium.solo.Solo;
 
 /**
  * A class representing any interactions that take place on the app menu.
  */
 public class AppMenuComponent extends BaseComponent {
-    private static final long MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS = 7500L;
+    private static final int MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS = 7500;
 
     public enum MenuItem {
         FORWARD(R.string.forward),
         NEW_TAB(R.string.new_tab),
         PAGE(R.string.page),
         RELOAD(R.string.reload);
 
         private final int resourceID;
@@ -119,50 +120,53 @@ public class AppMenuComponent extends Ba
      * Try to find a MenuItemActionBar/MenuItemDefault with the given text set as contentDescription / text.
      *
      * When using legacy menus, make sure the menu has been opened to the appropriate level
      * (i.e. base menu or "More" menu) to ensure the appropriate menu views are in memory.
      * TODO: ^ Maybe we just need to have opened the "More" menu and the current one doesn't matter.
      *
      * This method is dependent on not having two views with equivalent contentDescription / text.
      */
-    private View findAppMenuItemView(String text) {
-        RobotiumHelper.waitForExactText(text, 1, MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS);
+    private View findAppMenuItemView(final String text) {
+        return WaitHelper.waitFor(String.format("menu item view '%s'", text), new Callable<View>() {
+            @Override
+            public View call() throws Exception {
+                final List<View> views = mSolo.getViews();
 
-        final List<View> views = mSolo.getViews();
+                final List<MenuItemActionBar> menuItemActionBarList = RobotiumUtils.filterViews(MenuItemActionBar.class, views);
+                for (MenuItemActionBar menuItem : menuItemActionBarList) {
+                    if (TextUtils.equals(menuItem.getContentDescription(), text)) {
+                        return menuItem;
+                    }
+                }
 
-        final List<MenuItemActionBar> menuItemActionBarList = RobotiumUtils.filterViews(MenuItemActionBar.class, views);
-        for (MenuItemActionBar menuItem : menuItemActionBarList) {
-            if (TextUtils.equals(menuItem.getContentDescription(), text)) {
-                return menuItem;
-            }
-        }
+                final List<MenuItemDefault> menuItemDefaultList = RobotiumUtils.filterViews(MenuItemDefault.class, views);
+                for (MenuItemDefault menuItem : menuItemDefaultList) {
+                    if (TextUtils.equals(menuItem.getText(), text)) {
+                        return menuItem;
+                    }
+                }
 
-        final List<MenuItemDefault> menuItemDefaultList = RobotiumUtils.filterViews(MenuItemDefault.class, views);
-        for (MenuItemDefault menuItem : menuItemDefaultList) {
-            if (TextUtils.equals(menuItem.getText(), text)) {
-                return menuItem;
+                // On Android 2.3, menu items may be instances of
+                // com.android.internal.view.menu.ListMenuItemView, each with a child
+                // android.widget.RelativeLayout which in turn has a child
+                // TextView with the appropriate text.
+                final List<TextView> textViewList = RobotiumUtils.filterViews(TextView.class, views);
+                for (TextView textView : textViewList) {
+                    if (TextUtils.equals(textView.getText(), text)) {
+                        View relativeLayout = (View) textView.getParent();
+                        if (relativeLayout instanceof RelativeLayout) {
+                            View listMenuItemView = (View)relativeLayout.getParent();
+                            return listMenuItemView;
+                        }
+                    }
+                }
+                return null;
             }
-        }
-
-        // On Android 2.3, menu items may be instances of
-        // com.android.internal.view.menu.ListMenuItemView, each with a child
-        // android.widget.RelativeLayout which in turn has a child
-        // TextView with the appropriate text.
-        final List<TextView> textViewList = RobotiumUtils.filterViews(TextView.class, views);
-        for (TextView textView : textViewList) {
-            if (TextUtils.equals(textView.getText(), text)) {
-                View relativeLayout = (View) textView.getParent();
-                if (relativeLayout instanceof RelativeLayout) {
-                    View listMenuItemView = (View)relativeLayout.getParent();
-                    return listMenuItemView;
-                }
-            }
-        }
-        return null;
+        }, MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS);
     }
 
     /**
      * Helper function to let Robotium locate and click menu item from legacy Android menu (devices with Android 2.x).
      *
      * Robotium will also try to open the menu if there are no open dialog.
      *
      * @param menuItemTitle, The title of menu item to open.
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/WaitHelper.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/WaitHelper.java
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests.helpers;
 
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
 
 import android.os.SystemClock;
+
+import java.util.concurrent.Callable;
 import java.util.regex.Pattern;
 
 import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.Actions.EventExpecter;
 import org.mozilla.gecko.tests.UITestContext;
 import org.mozilla.gecko.tests.UITestContext.ComponentType;
 import org.mozilla.gecko.tests.components.ToolbarComponent;
 
@@ -66,16 +68,46 @@ public final class WaitHelper {
      * AssertionError if the duration is elapsed and the condition is not satisfied.
      */
     public static void waitFor(String message, final Condition condition, final int waitMillis) {
         message = "Waiting for " + message + " with timeout " + waitMillis + ".";
         fAssertTrue(message, sSolo.waitForCondition(condition, waitMillis));
     }
 
     /**
+     * Waits for the given Callable to return something that is not null, using the given wait
+     * duration; will throw an AssertionError if the duration is elapsed and the callable has not
+     * returned a non-null object.
+     *
+     * @return the value returned by the Callable. Or null if the duration has elapsed.
+     */
+    public static <V> V waitFor(String message, final Callable<V> callable, int waitMillis) {
+        sContext.dumpLog("WaitHelper", "Waiting for " + message + " with timeout " + waitMillis + ".");
+
+        final Object[] value = new Object[1];
+
+        Condition condition = new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                try {
+                    V result = callable.call();
+                    value[0] = result;
+                    return result != null;
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        };
+
+        sSolo.waitForCondition(condition, waitMillis);
+
+        return (V) value[0];
+    }
+
+    /**
      * Waits for the Gecko event declaring the page has loaded. Takes in and runs a Runnable
      * that will perform the action that will cause the page to load.
      */
     public static void waitForPageLoad(final Runnable initiatingAction) {
         fAssertNotNull("initiatingAction is not null", initiatingAction);
 
         // Some changes to the UI occur in response to the same event we listen to for when
         // the page has finished loading (e.g. a page title update). As such, we ensure this