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);
} }
......
...@@ -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