Commit af8ff5d4 authored by Theresa Wellington's avatar Theresa Wellington Committed by Commit Bot

Add more tests for app menu tests to increase coverage

Adds a suite of tests to cover functionality in AppMenuButtonHelperImpl.
Also adds more tests for AppMenu click, long-press and key event
handling and a variety of tests to boost coverage of app menu classes.

BUG=966644

Change-Id: I132b095cf576567052cacce7f7b60dbb8327c7f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1900653
Commit-Queue: Theresa  <twellington@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714224}
parent f31159e3
...@@ -15,6 +15,7 @@ chrome_junit_test_java_sources = [ ...@@ -15,6 +15,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java", "junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java",
"junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java", "junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java",
"junit/src/org/chromium/chrome/browser/SSLClientCertificateRequestTest.java", "junit/src/org/chromium/chrome/browser/SSLClientCertificateRequestTest.java",
"junit/src/org/chromium/chrome/browser/appmenu/AppMenuPopupPositionTest.java",
"junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java", "junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java",
"junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java", "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java",
"junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskTest.java", "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskTest.java",
......
...@@ -267,8 +267,10 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl ...@@ -267,8 +267,10 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl
int popupHeight = setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight, int popupHeight = setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight,
sizingPadding, footerHeight, headerHeight, anchorView); sizingPadding, footerHeight, headerHeight, anchorView);
int[] popupPosition = getPopupPosition(mCurrentScreenRotation, visibleDisplayFrame, int[] popupPosition = getPopupPosition(mTempLocation, mIsByPermanentButton,
sizingPadding, anchorView, popupWidth, popupHeight, showFromBottom); mNegativeSoftwareVerticalOffset, mNegativeVerticalOffsetNotTopAnchored,
mCurrentScreenRotation, visibleDisplayFrame, sizingPadding, anchorView, popupWidth,
popupHeight, showFromBottom, anchorView.getRootView().getLayoutDirection());
mPopup.setContentView(contentView); mPopup.setContentView(contentView);
mPopup.showAtLocation( mPopup.showAtLocation(
...@@ -298,16 +300,19 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl ...@@ -298,16 +300,19 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl
} }
} }
private int[] getPopupPosition(int screenRotation, Rect appRect, Rect padding, View anchorView, @VisibleForTesting
int popupWidth, int popupHeight, boolean isAnchorAtBottom) { static int[] getPopupPosition(int[] tempLocation, boolean isByPermanentButton,
anchorView.getLocationInWindow(mTempLocation); int negativeSoftwareVerticalOffset, int negativeVerticalOffsetNotTopAnchored,
int anchorViewX = mTempLocation[0]; int screenRotation, Rect appRect, Rect padding, View anchorView, int popupWidth,
int anchorViewY = mTempLocation[1]; int popupHeight, boolean isAnchorAtBottom, int viewLayoutDirection) {
anchorView.getLocationInWindow(tempLocation);
int anchorViewX = tempLocation[0];
int anchorViewY = tempLocation[1];
int[] offsets = new int[2]; int[] offsets = new int[2];
// If we have a hardware menu button, locate the app menu closer to the estimated // If we have a hardware menu button, locate the app menu closer to the estimated
// hardware menu button location. // hardware menu button location.
if (mIsByPermanentButton) { if (isByPermanentButton) {
int horizontalOffset = -anchorViewX; int horizontalOffset = -anchorViewX;
switch (screenRotation) { switch (screenRotation) {
case Surface.ROTATION_0: case Surface.ROTATION_0:
...@@ -328,21 +333,21 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl ...@@ -328,21 +333,21 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl
// padding of the background. // padding of the background.
offsets[1] = -padding.bottom; offsets[1] = -padding.bottom;
} else { } else {
offsets[1] = -mNegativeSoftwareVerticalOffset; offsets[1] = -negativeSoftwareVerticalOffset;
// If the anchor is at the bottom of the screen, align the popup with the bottom of the // If the anchor is at the bottom of the screen, align the popup with the bottom of the
// anchor. The anchor may not be fully visible, so // anchor. The anchor may not be fully visible, so
// (appRect.bottom - anchorViewLocationOnScreenY) is used to determine the visible // (appRect.bottom - anchorViewLocationOnScreenY) is used to determine the visible
// bottom edge of the anchor view. // bottom edge of the anchor view.
if (isAnchorAtBottom) { if (isAnchorAtBottom) {
anchorView.getLocationOnScreen(mTempLocation); anchorView.getLocationOnScreen(tempLocation);
int anchorViewLocationOnScreenY = mTempLocation[1]; int anchorViewLocationOnScreenY = tempLocation[1];
offsets[1] += appRect.bottom - anchorViewLocationOnScreenY - popupHeight; offsets[1] += appRect.bottom - anchorViewLocationOnScreenY - popupHeight;
offsets[1] -= mNegativeVerticalOffsetNotTopAnchored; offsets[1] -= negativeVerticalOffsetNotTopAnchored;
if (!mIsByPermanentButton) offsets[1] += padding.bottom; offsets[1] += padding.bottom;
} }
if (anchorView.getRootView().getLayoutDirection() != View.LAYOUT_DIRECTION_RTL) { if (viewLayoutDirection != View.LAYOUT_DIRECTION_RTL) {
offsets[0] = anchorView.getWidth() - popupWidth; offsets[0] = anchorView.getWidth() - popupWidth;
} }
} }
...@@ -364,10 +369,15 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl ...@@ -364,10 +369,15 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl
@Override @Override
public boolean onItemLongClick(MenuItem menuItem, View view) { public boolean onItemLongClick(MenuItem menuItem, View view) {
if (!menuItem.isEnabled()) return false; if (!menuItem.isEnabled()) return false;
Context context = ContextUtils.getApplicationContext();
CharSequence titleCondensed = menuItem.getTitleCondensed(); CharSequence titleCondensed = menuItem.getTitleCondensed();
CharSequence message = CharSequence message =
TextUtils.isEmpty(titleCondensed) ? menuItem.getTitle() : titleCondensed; TextUtils.isEmpty(titleCondensed) ? menuItem.getTitle() : titleCondensed;
return showToastForItem(message, view);
}
@VisibleForTesting
boolean showToastForItem(CharSequence message, View view) {
Context context = ContextUtils.getApplicationContext();
return Toast.showAnchoredToast(context, view, message); return Toast.showAnchoredToast(context, view, message);
} }
...@@ -545,4 +555,9 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl ...@@ -545,4 +555,9 @@ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuAdapter.OnCl
void finishAnimationsForTests() { void finishAnimationsForTests() {
if (mMenuItemEnterAnimator != null) mMenuItemEnterAnimator.end(); if (mMenuItemEnterAnimator != null) mMenuItemEnterAnimator.end();
} }
@VisibleForTesting
AppMenuAdapter getAdapterForTests() {
return mAdapter;
}
} }
...@@ -13,6 +13,8 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; ...@@ -13,6 +13,8 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
/** A UI coordinator the app menu. */ /** A UI coordinator the app menu. */
class AppMenuCoordinatorImpl implements AppMenuCoordinator { class AppMenuCoordinatorImpl implements AppMenuCoordinator {
private static Boolean sHasPermanentMenuKeyForTesting;
/** /**
* Factory which creates the AppMenuHandlerImpl. * Factory which creates the AppMenuHandlerImpl.
*/ */
...@@ -79,7 +81,9 @@ class AppMenuCoordinatorImpl implements AppMenuCoordinator { ...@@ -79,7 +81,9 @@ class AppMenuCoordinatorImpl implements AppMenuCoordinator {
public void showAppMenuForKeyboardEvent() { public void showAppMenuForKeyboardEvent() {
if (mAppMenuHandler == null || !mAppMenuHandler.shouldShowAppMenu()) return; if (mAppMenuHandler == null || !mAppMenuHandler.shouldShowAppMenu()) return;
boolean hasPermanentMenuKey = ViewConfiguration.get(mContext).hasPermanentMenuKey(); boolean hasPermanentMenuKey = sHasPermanentMenuKeyForTesting != null
? sHasPermanentMenuKeyForTesting.booleanValue()
: ViewConfiguration.get(mContext).hasPermanentMenuKey();
mAppMenuHandler.showAppMenu( mAppMenuHandler.showAppMenu(
hasPermanentMenuKey ? null : mButtonDelegate.getMenuButtonView(), false, hasPermanentMenuKey ? null : mButtonDelegate.getMenuButtonView(), false,
mButtonDelegate.isMenuFromBottom()); mButtonDelegate.isMenuFromBottom());
...@@ -111,4 +115,13 @@ class AppMenuCoordinatorImpl implements AppMenuCoordinator { ...@@ -111,4 +115,13 @@ class AppMenuCoordinatorImpl implements AppMenuCoordinator {
AppMenuHandlerImpl getAppMenuHandlerImplForTesting() { AppMenuHandlerImpl getAppMenuHandlerImplForTesting() {
return mAppMenuHandler; return mAppMenuHandler;
} }
/**
* @param hasPermanentMenuKey Overrides {@link ViewConfiguration#hasPermanentMenuKey()} for
* testing. Pass null to reset.
*/
@VisibleForTesting
static void setHasPermanentMenuKeyForTesting(Boolean hasPermanentMenuKey) {
sHasPermanentMenuKeyForTesting = hasPermanentMenuKey;
}
} }
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 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. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="21"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 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. -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent"
android:layout_width="match_parent" >
<ImageButton
android:id="@+id/top_button"
android:layout_width="48dp"
android:layout_height="48dp"
app:srcCompat="@drawable/test_ic_more_vert_black_24dp"
android:layout_gravity="top|end"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/bottom_button"
android:layout_width="48dp"
android:layout_height="48dp"
app:srcCompat="@drawable/test_ic_more_vert_black_24dp"
android:layout_gravity="bottom|end"
tools:ignore="ContentDescription" />
<View
android:id="@+id/menu_anchor_stub"
android:layout_width="0px"
android:layout_height="0px"
android:layout_gravity="bottom|start" />
</FrameLayout>
\ No newline at end of file
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
<item android:id="@+id/icon_one" <item android:id="@+id/icon_one"
android:title="Icon One" /> android:title="Icon One" />
<item android:id="@+id/icon_two" <item android:id="@+id/icon_two"
android:title="Icon Two" /> android:title="Icon Two"
android:titleCondensed="2" />
<item android:id="@+id/icon_three" <item android:id="@+id/icon_three"
android:title="Icon Three" /> android:title="Icon Three" />
</menu> </menu>
......
...@@ -310,6 +310,28 @@ public class AppMenuAdapterTest extends DummyUiActivityTestCase { ...@@ -310,6 +310,28 @@ public class AppMenuAdapterTest extends DummyUiActivityTestCase {
view5.getTag(R.id.menu_item_enter_anim_id)); view5.getTag(R.id.menu_item_enter_anim_id));
} }
@Test
@MediumTest
public void testTitleMenuItem_ToggleCheckbox() {
List<MenuItem> items = new ArrayList<>();
items.add(buildTitleMenuItem(1, 2, TITLE_1, 3, TITLE_2));
AppMenuAdapter adapter = new AppMenuAdapter(
mClickHandler, items, getActivity().getLayoutInflater(), 0, null);
ViewGroup parentView = getActivity().findViewById(android.R.id.content);
View view = adapter.getView(0, null, parentView);
AppMenuItemIcon checkbox = view.findViewById(R.id.checkbox);
Assert.assertFalse("Checkbox should be unchecked", checkbox.isChecked());
TestThreadUtils.runOnUiThreadBlocking(() -> checkbox.toggle());
Assert.assertTrue("Checkbox should be checked", checkbox.isChecked());
TestThreadUtils.runOnUiThreadBlocking(() -> checkbox.toggle());
Assert.assertFalse("Checkbox should be unchecked again", checkbox.isChecked());
}
static MenuItem buildMenuItem(int id, CharSequence title) { static MenuItem buildMenuItem(int id, CharSequence title) {
return buildMenuItem(id, title, true); return buildMenuItem(id, title, true);
} }
......
...@@ -4,16 +4,23 @@ ...@@ -4,16 +4,23 @@
package org.chromium.chrome.browser.appmenu; package org.chromium.chrome.browser.appmenu;
import android.graphics.Rect;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.junit.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
...@@ -23,10 +30,12 @@ import org.chromium.chrome.browser.lifecycle.LifecycleObserver; ...@@ -23,10 +30,12 @@ import org.chromium.chrome.browser.lifecycle.LifecycleObserver;
import org.chromium.chrome.browser.ui.widget.highlight.ViewHighlighterTestUtils; import org.chromium.chrome.browser.ui.widget.highlight.ViewHighlighterTestUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.R; import org.chromium.chrome.test.R;
import org.chromium.chrome.test.ui.DummyUiActivity;
import org.chromium.chrome.test.ui.DummyUiActivityTestCase; import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** /**
...@@ -42,6 +51,12 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -42,6 +51,12 @@ public class AppMenuTest extends DummyUiActivityTestCase {
private TestAppMenuDelegate mDelegate; private TestAppMenuDelegate mDelegate;
private TestAppMenuObserver mMenuObserver; private TestAppMenuObserver mMenuObserver;
private TestActivityLifecycleDispatcher mLifecycleDispatcher; private TestActivityLifecycleDispatcher mLifecycleDispatcher;
private TestMenuButtonDelegate mTestMenuButtonDelegate;
@BeforeClass
public static void setUpBeforeActivityLaunched() {
DummyUiActivity.setTestLayout(R.layout.test_app_menu_activity_layout);
}
@Override @Override
public void setUpTest() throws Exception { public void setUpTest() throws Exception {
...@@ -50,12 +65,18 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -50,12 +65,18 @@ public class AppMenuTest extends DummyUiActivityTestCase {
mLifecycleDispatcher.observerRegisteredCallbackHelper.waitForCallback(0); mLifecycleDispatcher.observerRegisteredCallbackHelper.waitForCallback(0);
} }
@AfterClass
public static void tearDownAfterActivityDestroyed() {
AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(null);
}
private void setUpTestOnUiThread() { private void setUpTestOnUiThread() {
mLifecycleDispatcher = new TestActivityLifecycleDispatcher(); mLifecycleDispatcher = new TestActivityLifecycleDispatcher();
mDelegate = new TestAppMenuDelegate(); mDelegate = new TestAppMenuDelegate();
mTestMenuButtonDelegate = new TestMenuButtonDelegate();
mAppMenuCoordinator = new AppMenuCoordinatorImpl(getActivity(), mLifecycleDispatcher, mAppMenuCoordinator = new AppMenuCoordinatorImpl(getActivity(), mLifecycleDispatcher,
new TestMenuButtonDelegate(), mDelegate, getActivity().getWindow().getDecorView(), mTestMenuButtonDelegate, mDelegate, getActivity().getWindow().getDecorView(),
new View(getActivity())); getActivity().findViewById(R.id.menu_anchor_stub));
mAppMenuHandler = mAppMenuCoordinator.getAppMenuHandlerImplForTesting(); mAppMenuHandler = mAppMenuCoordinator.getAppMenuHandlerImplForTesting();
mMenuObserver = new TestAppMenuObserver(); mMenuObserver = new TestAppMenuObserver();
mAppMenuCoordinator.getAppMenuHandler().addObserver(mMenuObserver); mAppMenuCoordinator.getAppMenuHandler().addObserver(mMenuObserver);
...@@ -66,7 +87,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -66,7 +87,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
@Test @Test
@MediumTest @MediumTest
public void testShowHideAppMenu() throws TimeoutException { public void testShowHideAppMenu() throws TimeoutException {
showSimpleMenuAndAssert(); showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu()); TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
mMenuObserver.menuHiddenCallback.waitForCallback(0); mMenuObserver.menuHiddenCallback.waitForCallback(0);
...@@ -79,10 +100,89 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -79,10 +100,89 @@ public class AppMenuTest extends DummyUiActivityTestCase {
mPropertiesDelegate.menuDismissedCallback.getCallCount()); mPropertiesDelegate.menuDismissedCallback.getCallCount());
} }
@Test
@MediumTest
public void testHideAppMenuMultiple() throws TimeoutException {
showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.getAppMenu().dismiss());
mMenuObserver.menuHiddenCallback.waitForCallback(0);
Assert.assertEquals("Incorrect number of calls to #onMenuDismissed after first call", 1,
mPropertiesDelegate.menuDismissedCallback.getCallCount());
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.getAppMenu().dismiss());
Assert.assertEquals("Incorrect number of calls to #onMenuDismissed after second call", 1,
mPropertiesDelegate.menuDismissedCallback.getCallCount());
}
@Test
@MediumTest
public void testShowAppMenu_AnchorTop() throws TimeoutException {
AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(false);
showMenuAndAssert();
View topAnchor = getActivity().findViewById(R.id.top_button);
Rect viewRect = getViewLocationRect(topAnchor);
Rect popupRect = getPopupLocationRect();
// Check that top right corner of app menu aligns with the top right corner of the anchor.
int alignmentSlop = viewRect.bottom - viewRect.top;
Assert.assertEquals("Popup should overlap top anchor. Anchor rect: " + viewRect
+ ", popup rect: " + popupRect,
viewRect.top, popupRect.top, alignmentSlop);
Assert.assertTrue("Popup should overlap top anchor. Anchor rect: " + viewRect
+ ", popup rect: " + popupRect,
viewRect.top <= popupRect.top);
Assert.assertEquals("Popup should be aligned with right of anchor. Anchor rect: " + viewRect
+ ", popup rect: " + popupRect,
viewRect.right, popupRect.right);
}
@Test
@MediumTest
public void testShowAppMenu_AnchorBottom() throws TimeoutException {
AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(false);
mTestMenuButtonDelegate.useBottomAnchor = true;
showMenuAndAssert();
View bottomAnchor = getActivity().findViewById(R.id.bottom_button);
Rect viewRect = getViewLocationRect(bottomAnchor);
Rect popupRect = getPopupLocationRect();
// Check that bottom right corner of app menu aligns with bottom right corner of the anchor.
int alignmentSlop = viewRect.bottom - viewRect.top;
Assert.assertEquals("Popup should be overlap bottom anchor. Anchor rect: " + viewRect
+ ", popup rect: " + popupRect,
viewRect.bottom, popupRect.bottom, alignmentSlop);
Assert.assertTrue("Popup should overlap bottom anchor. Anchor rect: " + viewRect
+ ", popup rect: " + popupRect,
viewRect.bottom >= popupRect.bottom);
Assert.assertEquals("Popup should be aligned with right of anchor. Anchor rect: " + viewRect
+ ", popup rect: " + popupRect,
viewRect.right, popupRect.right);
}
@Test
@MediumTest
public void testShowAppMenu_PermanentButton() throws TimeoutException {
AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(true);
showMenuAndAssert();
View anchorStub = getActivity().findViewById(R.id.menu_anchor_stub);
Rect viewRect = getViewLocationRect(anchorStub);
Rect popupRect = getPopupLocationRect();
// Check a basic alignment property. Full coverage checked in unit tests.
Assert.assertNotEquals("Popup should be offset from right of anchor."
+ "Anchor rect: " + viewRect + ", popup rect: " + popupRect,
viewRect.right, popupRect.right);
}
@Test @Test
@MediumTest @MediumTest
public void testShowDestroyAppMenu() throws TimeoutException { public void testShowDestroyAppMenu() throws TimeoutException {
showSimpleMenuAndAssert(); showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuCoordinator.destroy()); TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuCoordinator.destroy());
...@@ -93,18 +193,94 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -93,18 +193,94 @@ public class AppMenuTest extends DummyUiActivityTestCase {
@Test @Test
@MediumTest @MediumTest
public void testClickMenuItem() throws TimeoutException { public void testClickMenuItem() throws TimeoutException {
showSimpleMenuAndAssert(); showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking( TestThreadUtils.runOnUiThreadBlocking(
() ()
-> AppMenuTestSupport.callOnItemClick( -> AppMenuTestSupport.callOnItemClick(
mAppMenuCoordinator, R.id.menu_item_three)); mAppMenuCoordinator, R.id.menu_item_three));
mDelegate.itemSelectedCallbackHelper.waitForCallback(0);
Assert.assertEquals("Item selected callback should have been called.", 1,
mDelegate.itemSelectedCallbackHelper.getCallCount());
Assert.assertEquals("Incorrect id for last selected item.", R.id.menu_item_three, Assert.assertEquals("Incorrect id for last selected item.", R.id.menu_item_three,
mDelegate.lastSelectedItemId); mDelegate.lastSelectedItemId);
} }
@Test
@MediumTest
public void testClickMenuItem_Disabled() throws TimeoutException {
showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(
() -> AppMenuTestSupport.callOnItemClick(mAppMenuCoordinator, R.id.menu_item_two));
Assert.assertEquals("Item selected callback should not have been called.", 0,
mDelegate.itemSelectedCallbackHelper.getCallCount());
}
@Test
@MediumTest
public void testClickMenuItem_UsingPosition() throws TimeoutException {
showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(
() -> mAppMenuHandler.getAppMenu().onItemClick(null, null, 0, 0));
Assert.assertEquals("Item selected callback should have been called.", 1,
mDelegate.itemSelectedCallbackHelper.getCallCount());
Assert.assertEquals("Incorrect id for last selected item.", R.id.menu_item_one,
mDelegate.lastSelectedItemId);
}
@Test
@MediumTest
public void testLongClickMenuItem_Title() throws TimeoutException {
mPropertiesDelegate.enableAppIconRow = true;
showMenuAndAssert();
AppMenu spiedMenu = Mockito.spy(mAppMenuHandler.getAppMenu());
View dummyView = new View(getActivity());
TestThreadUtils.runOnUiThreadBlocking(() -> {
spiedMenu.onItemLongClick(
mAppMenuHandler.getAppMenu().getMenu().findItem(R.id.icon_one), dummyView);
});
Mockito.verify(spiedMenu, Mockito.times(1)).showToastForItem("Icon One", dummyView);
}
@Test
@MediumTest
public void testLongClickMenuItem_TitleCondensed() throws TimeoutException {
mPropertiesDelegate.enableAppIconRow = true;
showMenuAndAssert();
AppMenu spiedMenu = Mockito.spy(mAppMenuHandler.getAppMenu());
View dummyView = new View(getActivity());
TestThreadUtils.runOnUiThreadBlocking(() -> {
spiedMenu.onItemLongClick(
mAppMenuHandler.getAppMenu().getMenu().findItem(R.id.icon_two), dummyView);
});
Mockito.verify(spiedMenu, Mockito.times(1)).showToastForItem("2", dummyView);
}
@Test
@MediumTest
public void testLongClickMenuItem_Disabled() throws TimeoutException {
mPropertiesDelegate.enableAppIconRow = true;
showMenuAndAssert();
AppMenu spiedMenu = Mockito.spy(mAppMenuHandler.getAppMenu());
View dummyView = new View(getActivity());
TestThreadUtils.runOnUiThreadBlocking(() -> {
spiedMenu.onItemLongClick(
mAppMenuHandler.getAppMenu().getMenu().findItem(R.id.icon_three), dummyView);
});
Mockito.verify(spiedMenu, Mockito.times(0))
.showToastForItem(Mockito.any(CharSequence.class), Mockito.any(View.class));
}
@Test @Test
@MediumTest @MediumTest
public void testAppMenuBlockers() throws TimeoutException { public void testAppMenuBlockers() throws TimeoutException {
...@@ -126,7 +302,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -126,7 +302,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
mAppMenuCoordinator.unregisterAppMenuBlocker(blocker1); mAppMenuCoordinator.unregisterAppMenuBlocker(blocker1);
Assert.assertTrue("App menu should be allowed to show, only blocker2 registered", Assert.assertTrue("App menu should be allowed to show, only blocker2 registered",
AppMenuTestSupport.shouldShowAppMenu(mAppMenuCoordinator)); AppMenuTestSupport.shouldShowAppMenu(mAppMenuCoordinator));
showSimpleMenuAndAssert(); showMenuAndAssert();
} }
@Test @Test
...@@ -139,7 +315,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -139,7 +315,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
mMenuObserver.menuHighlightChangedCallback.waitForCallback(0); mMenuObserver.menuHighlightChangedCallback.waitForCallback(0);
Assert.assertTrue(mMenuObserver.menuHighlighting); Assert.assertTrue(mMenuObserver.menuHighlighting);
showSimpleMenuAndAssert(); showMenuAndAssert();
View itemView = getViewAtPosition(0); View itemView = getViewAtPosition(0);
ViewHighlighterTestUtils.checkHighlightOn(itemView); ViewHighlighterTestUtils.checkHighlightOn(itemView);
...@@ -162,7 +338,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -162,7 +338,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
mMenuObserver.menuHighlightChangedCallback.waitForCallback(0); mMenuObserver.menuHighlightChangedCallback.waitForCallback(0);
Assert.assertTrue(mMenuObserver.menuHighlighting); Assert.assertTrue(mMenuObserver.menuHighlighting);
showSimpleMenuAndAssert(); showMenuAndAssert();
View itemView = ((LinearLayout) getViewAtPosition(3)).getChildAt(0); View itemView = ((LinearLayout) getViewAtPosition(3)).getChildAt(0);
ViewHighlighterTestUtils.checkHighlightOn(itemView); ViewHighlighterTestUtils.checkHighlightOn(itemView);
...@@ -176,7 +352,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -176,7 +352,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
@Test @Test
@MediumTest @MediumTest
public void testMenuItemContentChanged() throws TimeoutException { public void testMenuItemContentChanged() throws TimeoutException {
showSimpleMenuAndAssert(); showMenuAndAssert();
View itemView = getViewAtPosition(1); View itemView = getViewAtPosition(1);
Assert.assertEquals("Menu item text incorrect", "Menu Item Two", Assert.assertEquals("Menu item text incorrect", "Menu Item Two",
((TextView) itemView.findViewById(R.id.menu_item_text)).getText()); ((TextView) itemView.findViewById(R.id.menu_item_text)).getText());
...@@ -197,7 +373,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -197,7 +373,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
public void testHeaderFooter() throws TimeoutException { public void testHeaderFooter() throws TimeoutException {
mPropertiesDelegate.headerResourceId = R.layout.test_menu_header; mPropertiesDelegate.headerResourceId = R.layout.test_menu_header;
mPropertiesDelegate.footerResourceId = R.layout.test_menu_footer; mPropertiesDelegate.footerResourceId = R.layout.test_menu_footer;
showSimpleMenuAndAssert(); showMenuAndAssert();
mPropertiesDelegate.headerInflatedCallback.waitForCallback(0); mPropertiesDelegate.headerInflatedCallback.waitForCallback(0);
mPropertiesDelegate.footerInflatedCallback.waitForCallback(0); mPropertiesDelegate.footerInflatedCallback.waitForCallback(0);
...@@ -212,7 +388,7 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -212,7 +388,7 @@ public class AppMenuTest extends DummyUiActivityTestCase {
@Test @Test
@MediumTest @MediumTest
public void testAppMenuHiddenOnStopWithNative() throws TimeoutException { public void testAppMenuHiddenOnStopWithNative() throws TimeoutException {
showSimpleMenuAndAssert(); showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.onStopWithNative()); TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.onStopWithNative());
Assert.assertFalse(mAppMenuHandler.isAppMenuShowing()); Assert.assertFalse(mAppMenuHandler.isAppMenuShowing());
} }
...@@ -220,15 +396,231 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -220,15 +396,231 @@ public class AppMenuTest extends DummyUiActivityTestCase {
@Test @Test
@MediumTest @MediumTest
public void testAppMenuHiddenOnConfigurationChange() throws TimeoutException { public void testAppMenuHiddenOnConfigurationChange() throws TimeoutException {
showSimpleMenuAndAssert(); showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.onConfigurationChanged(null)); TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.onConfigurationChanged(null));
Assert.assertFalse(mAppMenuHandler.isAppMenuShowing()); Assert.assertFalse(mAppMenuHandler.isAppMenuShowing());
} }
private void showSimpleMenuAndAssert() throws TimeoutException { @Test
@MediumTest
public void testAppMenuKeyEvent_HiddenOnHardwareButtonPress() throws Exception {
showMenuAndAssert();
AppMenu appMenu = mAppMenuHandler.getAppMenu();
TestThreadUtils.runOnUiThreadBlocking(() -> {
KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU);
KeyEvent up = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU);
appMenu.onKey(appMenu.getListView(), KeyEvent.KEYCODE_MENU, down);
appMenu.onKey(appMenu.getListView(), KeyEvent.KEYCODE_MENU, up);
});
mMenuObserver.menuHiddenCallback.waitForCallback(0);
}
@Test
@MediumTest
public void testAppMenuKeyEvent_IgnoreUnrelatedKeyCode() throws Exception {
showMenuAndAssert();
AppMenu appMenu = mAppMenuHandler.getAppMenu();
KeyEvent unrelated = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BOOKMARK);
Assert.assertFalse("#onKeyEvent should return false for unrelated codes",
appMenu.onKey(null, KeyEvent.KEYCODE_BOOKMARK, unrelated));
}
@Test
@MediumTest
public void testAppMenuKeyEvent_IgnoreUnrelatedKeyEvent() throws Exception {
showMenuAndAssert();
AppMenu appMenu = mAppMenuHandler.getAppMenu();
KeyEvent unrelated = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_MENU);
Assert.assertFalse("#onKeyEvent should return false for unrelated events",
appMenu.onKey(null, KeyEvent.KEYCODE_MENU, unrelated));
}
@Test
@MediumTest
public void testAppMenuKeyEvent_IgnoreEventsWhenHidden() throws Exception {
// Show app menu to initialize, then hide.
showMenuAndAssert();
TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
mMenuObserver.menuHiddenCallback.waitForCallback(0);
AppMenu appMenu = mAppMenuHandler.getAppMenu();
Assert.assertNull("ListView should be null.", appMenu.getListView());
KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU);
Assert.assertFalse("#onKeyEvent should return false when app menu hidden",
appMenu.onKey(null, KeyEvent.KEYCODE_MENU, null));
}
@Test
@MediumTest
public void testAppMenuButtonHelper_DownUp() throws Exception {
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
Assert.assertFalse("View should start unpressed",
mTestMenuButtonDelegate.getMenuButtonView().isPressed());
Assert.assertFalse("App menu should be not be active", buttonHelper.isAppMenuActive());
MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
waitForMenuToShow(0);
Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
Assert.assertTrue(
"View should be pressed", mTestMenuButtonDelegate.getMenuButtonView().isPressed());
Assert.assertTrue("App menu should be active", buttonHelper.isAppMenuActive());
MotionEvent upMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), upMotionEvent);
Assert.assertFalse("View should no longer be pressed",
mTestMenuButtonDelegate.getMenuButtonView().isPressed());
Assert.assertTrue("App menu should still be active", buttonHelper.isAppMenuActive());
}
@Test
@MediumTest
public void testAppMenuButtonHelper_DownCancel() throws Exception {
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
Assert.assertFalse("View should start unpressed",
mTestMenuButtonDelegate.getMenuButtonView().isPressed());
MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
waitForMenuToShow(0);
Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
Assert.assertTrue(
"View should be pressed", mTestMenuButtonDelegate.getMenuButtonView().isPressed());
MotionEvent cancelMotionEvent =
MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), cancelMotionEvent);
Assert.assertFalse("View should no longer be pressed",
mTestMenuButtonDelegate.getMenuButtonView().isPressed());
}
@Test
@MediumTest
public void testAppMenuButtonHelper_ClickRunnable() throws Exception {
Assert.assertFalse("View should start unpressed",
mTestMenuButtonDelegate.getMenuButtonView().isPressed());
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
CallbackHelper clickCallbackHelper = new CallbackHelper();
Runnable clickRunnable = () -> clickCallbackHelper.notifyCalled();
buttonHelper.setOnClickRunnable(clickRunnable);
MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
clickCallbackHelper.waitForCallback(0);
waitForMenuToShow(0);
}
@Test
@MediumTest
public void testAppMenuButtonHelper_ShowTwice() throws Exception {
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
CallbackHelper showCallbackHelper = new CallbackHelper();
Runnable showListener = () -> showCallbackHelper.notifyCalled();
buttonHelper.setOnAppMenuShownListener(showListener);
MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
waitForMenuToShow(0);
Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
Assert.assertEquals(
"Runnable should have been called once", 1, showCallbackHelper.getCallCount());
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
Assert.assertEquals("Runnable should still only have been called once", 1,
showCallbackHelper.getCallCount());
}
@Test
@MediumTest
public void testAppMenuButtonHelper_ShowBlocked() throws Exception {
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
AppMenuBlocker blocker1 = () -> false;
mAppMenuCoordinator.registerAppMenuBlocker(blocker1);
CallbackHelper showCallbackHelper = new CallbackHelper();
Runnable showListener = () -> showCallbackHelper.notifyCalled();
buttonHelper.setOnAppMenuShownListener(showListener);
MotionEvent downMotionEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
sendMotionEventToButtonHelper(
buttonHelper, mTestMenuButtonDelegate.getMenuButtonView(), downMotionEvent);
Assert.assertEquals(
"Runnable should not have been called once", 0, showCallbackHelper.getCallCount());
}
@Test
@MediumTest
public void testAppMenuButtonHelper_AccessibilityActions() throws Exception {
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
TestThreadUtils.runOnUiThreadBlocking(
()
-> buttonHelper.performAccessibilityAction(
mTestMenuButtonDelegate.getMenuButtonView(),
AccessibilityNodeInfo.ACTION_CLICK, null));
waitForMenuToShow(0);
Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
TestThreadUtils.runOnUiThreadBlocking(
()
-> buttonHelper.performAccessibilityAction(
mTestMenuButtonDelegate.getMenuButtonView(),
AccessibilityNodeInfo.ACTION_CLICK, null));
mMenuObserver.menuHiddenCallback.waitForCallback(0);
Assert.assertFalse("Menu should be hidden", mAppMenuHandler.isAppMenuShowing());
}
@Test
@MediumTest
public void testAppMenuButtonHelper_showEnterKeyPress() throws Exception {
AppMenuButtonHelperImpl buttonHelper =
(AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper();
TestThreadUtils.runOnUiThreadBlocking(
() -> buttonHelper.onEnterKeyPress(mTestMenuButtonDelegate.getMenuButtonView()));
waitForMenuToShow(0);
Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
}
private void showMenuAndAssert() throws TimeoutException {
int currentCallCount = mMenuObserver.menuShownCallback.getCallCount(); int currentCallCount = mMenuObserver.menuShownCallback.getCallCount();
TestThreadUtils.runOnUiThreadBlocking( TestThreadUtils.runOnUiThreadBlocking(
() -> mAppMenuCoordinator.showAppMenuForKeyboardEvent()); () -> mAppMenuCoordinator.showAppMenuForKeyboardEvent());
waitForMenuToShow(currentCallCount);
}
private void waitForMenuToShow(int currentCallCount) throws TimeoutException {
mMenuObserver.menuShownCallback.waitForCallback(currentCallCount); mMenuObserver.menuShownCallback.waitForCallback(currentCallCount);
Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing()); Assert.assertTrue("Menu should be showing", mAppMenuHandler.isAppMenuShowing());
...@@ -253,15 +645,18 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -253,15 +645,18 @@ public class AppMenuTest extends DummyUiActivityTestCase {
} }
private class TestMenuButtonDelegate implements MenuButtonDelegate { private class TestMenuButtonDelegate implements MenuButtonDelegate {
public boolean useBottomAnchor;
@Nullable @Nullable
@Override @Override
public View getMenuButtonView() { public View getMenuButtonView() {
return null; return getActivity().findViewById(
useBottomAnchor ? R.id.bottom_button : R.id.top_button);
} }
@Override @Override
public boolean isMenuFromBottom() { public boolean isMenuFromBottom() {
return false; return useBottomAnchor;
} }
} }
...@@ -273,4 +668,36 @@ public class AppMenuTest extends DummyUiActivityTestCase { ...@@ -273,4 +668,36 @@ public class AppMenuTest extends DummyUiActivityTestCase {
!= null); != null);
return AppMenuTestSupport.getListView(mAppMenuCoordinator).getChildAt(index); return AppMenuTestSupport.getListView(mAppMenuCoordinator).getChildAt(index);
} }
private Rect getPopupLocationRect() {
View contentView = mAppMenuHandler.getAppMenu().getPopup().getContentView();
CriteriaHelper.pollUiThread(() -> contentView.getHeight() != 0);
Rect bgPadding = new Rect();
mAppMenuHandler.getAppMenu().getPopup().getBackground().getPadding(bgPadding);
Rect popupRect = new Rect();
int[] popupLocation = new int[2];
contentView.getLocationOnScreen(popupLocation);
popupRect.left = popupLocation[0] - bgPadding.left;
popupRect.top = popupLocation[1] - bgPadding.top;
popupRect.right = popupLocation[0] + contentView.getWidth() + bgPadding.right;
popupRect.bottom = popupLocation[1] + contentView.getHeight() + bgPadding.bottom;
return popupRect;
}
private Rect getViewLocationRect(View anchor) {
Rect viewRect = new Rect();
int[] viewLocation = new int[2];
anchor.getLocationOnScreen(viewLocation);
viewRect.left = viewLocation[0];
viewRect.top = viewLocation[1];
viewRect.right = viewRect.left + anchor.getWidth();
viewRect.bottom = viewRect.top + anchor.getHeight();
return viewRect;
}
private void sendMotionEventToButtonHelper(AppMenuButtonHelperImpl helper, View view,
MotionEvent event) throws ExecutionException {
TestThreadUtils.runOnUiThreadBlocking(() -> helper.onTouch(view, event));
}
} }
...@@ -42,6 +42,8 @@ class TestAppMenuPropertiesDelegate implements AppMenuPropertiesDelegate { ...@@ -42,6 +42,8 @@ class TestAppMenuPropertiesDelegate implements AppMenuPropertiesDelegate {
@Override @Override
public void prepareMenu(Menu menu, AppMenuHandler handler) { public void prepareMenu(Menu menu, AppMenuHandler handler) {
menu.findItem(R.id.menu_item_two).setEnabled(false);
menu.findItem(R.id.icon_row_menu_id).setVisible(enableAppIconRow); menu.findItem(R.id.icon_row_menu_id).setVisible(enableAppIconRow);
if (enableAppIconRow) { if (enableAppIconRow) {
menu.findItem(R.id.icon_one) menu.findItem(R.id.icon_one)
...@@ -53,6 +55,7 @@ class TestAppMenuPropertiesDelegate implements AppMenuPropertiesDelegate { ...@@ -53,6 +55,7 @@ class TestAppMenuPropertiesDelegate implements AppMenuPropertiesDelegate {
menu.findItem(R.id.icon_three) menu.findItem(R.id.icon_three)
.setIcon(AppCompatResources.getDrawable(ContextUtils.getApplicationContext(), .setIcon(AppCompatResources.getDrawable(ContextUtils.getApplicationContext(),
R.drawable.test_ic_arrow_forward_black_24dp)); R.drawable.test_ic_arrow_forward_black_24dp));
menu.findItem(R.id.icon_three).setEnabled(false);
} }
} }
......
// Copyright 2019 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.chrome.browser.appmenu;
import android.graphics.Rect;
import android.view.Surface;
import android.view.View;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.test.support.DisableHistogramsRule;
/**
* Tests AppMenu#getPopupPosition.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class AppMenuPopupPositionTest {
@Rule
public DisableHistogramsRule mDisableHistogramsRule = new DisableHistogramsRule();
private final int[] mTempLocation = new int[2];
private final int mAppWidth = 400;
private final int mAppHeight = 1000;
private final int mBgPadding = 10;
private final int mPopupWidth = 300;
private final int mPopupHeight = 500;
private final int mAnchorX = 100;
private final int mAnchorY = 300;
private final int mAnchorWidth = 40;
private final int mNegativeSoftwareVerticalOffset = 25;
private final int mNegativeSoftwareVerticalOffsetNotTopAnchored = 15;
private final View mAnchorView = Mockito.mock(View.class);
private final Rect mAppRect = new Rect(0, 0, mAppWidth, mAppHeight);
private final Rect mBgPaddingRect = new Rect(mBgPadding, mBgPadding, mBgPadding, mBgPadding);
@Before
public void setUp() {
Mockito.doAnswer((InvocationOnMock invocation) -> {
mTempLocation[0] = mAnchorX;
mTempLocation[1] = mAnchorY;
return null;
})
.when(mAnchorView)
.getLocationInWindow(mTempLocation);
Mockito.doAnswer((InvocationOnMock invocation) -> {
mTempLocation[0] = mAnchorX;
mTempLocation[1] = mAnchorY;
return null;
})
.when(mAnchorView)
.getLocationOnScreen(mTempLocation);
Mockito.when(mAnchorView.getWidth()).thenReturn(mAnchorWidth);
}
@Test
public void testPermanentButton_Portrait() {
// Popup should should be in the middle of the screen, anchored near the anchor view.
int expectedX = (mAppWidth - mPopupWidth) / 2;
int expectedY = mAnchorY - mBgPadding;
int[] results =
getPopupPosition(true, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_LTR);
Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
results = getPopupPosition(true, Surface.ROTATION_180, false, View.LAYOUT_DIRECTION_LTR);
Assert.assertEquals("Incorrect popup x for rotation 180", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y for rotation 180", expectedY, results[1]);
}
@Test
public void testPermanentButton_Landscape() {
int[] results =
getPopupPosition(true, Surface.ROTATION_90, false, View.LAYOUT_DIRECTION_LTR);
// Popup should be positioned toward the right edge of the screen, anchored near the anchor
// view.
int expectedX = mAppWidth - mPopupWidth;
int expectedY = mAnchorY - mBgPadding;
Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
// Popup should be positioned toward the left edge of the screen, anchored near the anchor
// view.
expectedX = 0;
results = getPopupPosition(true, Surface.ROTATION_270, false, View.LAYOUT_DIRECTION_LTR);
Assert.assertEquals("Incorrect popup x for rotation 180", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y for rotation 180", expectedY, results[1]);
}
@Test
public void testTopButton_LTR() {
int[] results =
getPopupPosition(false, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_LTR);
// The top right edge of the popup should be aligned with the top right edge of the button.
int expectedX = mAnchorX + mAnchorWidth - mPopupWidth;
int expectedY = mAnchorY - mNegativeSoftwareVerticalOffset;
Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
}
@Test
public void testTopButton_RTL() {
int[] results =
getPopupPosition(false, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_RTL);
// The top left edge of the popup should be aligned with the top left edge of the button.
int expectedX = mAnchorX;
int expectedY = mAnchorY - mNegativeSoftwareVerticalOffset;
Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
}
@Test
public void testBottomButton_LTR() {
int[] results =
getPopupPosition(false, Surface.ROTATION_0, true, View.LAYOUT_DIRECTION_LTR);
// The bottom popup menu positioning assumes the anchor is at the bottom of the screen. It
// aligns the popup based on the bottom of the screen plus offsets.
int expectedX = mAnchorX + mAnchorWidth - mPopupWidth;
int expectedY = mAppHeight - mPopupHeight - mNegativeSoftwareVerticalOffset
- mNegativeSoftwareVerticalOffsetNotTopAnchored + mBgPadding;
Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
}
@Test
public void testBottomButton_RTL() {
int[] results =
getPopupPosition(false, Surface.ROTATION_0, true, View.LAYOUT_DIRECTION_RTL);
// The bottom popup menu positioning assumes the anchor is at the bottom of the screen. It
// aligns the popup based on the bottom of the screen plus offsets.
int expectedX = mAnchorX;
int expectedY = mAppHeight - mPopupHeight - mNegativeSoftwareVerticalOffset
- mNegativeSoftwareVerticalOffsetNotTopAnchored + mBgPadding;
Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
}
private int[] getPopupPosition(boolean isByPermanentButton, int rotation,
boolean isAnchorAtBottom, int layoutDirection) {
return AppMenu.getPopupPosition(mTempLocation, isByPermanentButton,
mNegativeSoftwareVerticalOffset, mNegativeSoftwareVerticalOffsetNotTopAnchored,
rotation, mAppRect, mBgPaddingRect, mAnchorView, mPopupWidth, mPopupHeight,
isAnchorAtBottom, layoutDirection);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment