Add unit test for toolbars' force touch gestures

This CL adds a unit test for the force touch gesture recognizer added
to the toolbar buttons, triggering the popup menu.

Bug: 864430
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: Icc2524f7e8e58bf20fb4b1381913cb82c57206a8
Reviewed-on: https://chromium-review.googlesource.com/1150142
Reviewed-by: Mark Cogan <marq@chromium.org>
Commit-Queue: Gauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578349}
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/BUILD.gn b/ios/chrome/browser/ui/toolbar/adaptive/BUILD.gn
index 724ae32..a5e724a 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/adaptive/BUILD.gn
@@ -113,6 +113,24 @@
   libs = [ "XCTest.framework" ]
 }
 
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "adaptive_toolbar_view_controller_unittest.mm",
+  ]
+  deps = [
+    ":adaptive_ui",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/popup_menu/public",
+    "//ios/chrome/browser/ui/toolbar/buttons",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+}
+
 source_set("hooks") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller_unittest.mm b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller_unittest.mm
new file mode 100644
index 0000000..3305b073
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller_unittest.mm
@@ -0,0 +1,112 @@
+// Copyright 2018 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.
+
+#import "ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller.h"
+
+#import <UIKit/UIGestureRecognizerSubclass.h>
+
+#include "base/run_loop.h"
+#import "base/test/ios/wait_util.h"
+#include "base/test/scoped_task_environment.h"
+#import "ios/chrome/browser/ui/commands/popup_menu_commands.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_long_press_delegate.h"
+#import "ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller.h"
+#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h"
+#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button_factory.h"
+#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+UIView* GetTabGridToolbarButton(UIView* parentView) {
+  if (parentView.accessibilityIdentifier == kToolbarStackButtonIdentifier)
+    return parentView;
+
+  for (UIView* subview in parentView.subviews) {
+    UIView* buttonSubview = GetTabGridToolbarButton(subview);
+    if (buttonSubview)
+      return buttonSubview;
+  }
+  return nil;
+}
+
+class AdaptiveToolbarViewControllerTest : public PlatformTest {
+ protected:
+  AdaptiveToolbarViewControllerTest()
+      : scopedTaskEnvironment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+
+  base::test::ScopedTaskEnvironment scopedTaskEnvironment_;
+};
+
+TEST_F(AdaptiveToolbarViewControllerTest, DetectForceTouch) {
+  id dispatcher = OCMProtocolMock(@protocol(PopupMenuCommands));
+  id longPressDelegate = OCMProtocolMock(@protocol(PopupMenuLongPressDelegate));
+  ToolbarButtonFactory* factory =
+      [[ToolbarButtonFactory alloc] initWithStyle:NORMAL];
+
+  AdaptiveToolbarViewController* toolbar =
+      [[PrimaryToolbarViewController alloc] init];
+  toolbar.buttonFactory = factory;
+  toolbar.longPressDelegate = longPressDelegate;
+  toolbar.dispatcher = dispatcher;
+
+  UIView* buttonView = GetTabGridToolbarButton(toolbar.view);
+
+  ASSERT_NE(buttonView, nil);
+  ASSERT_GE(buttonView.gestureRecognizers.count, 1UL);
+
+  UIGestureRecognizer* gestureRecognizer = buttonView.gestureRecognizers[0];
+
+  id event = OCMClassMock([UIEvent class]);
+
+  id touch = OCMClassMock([UITouch class]);
+  OCMStub([touch maximumPossibleForce]).andReturn(1);
+  OCMStub([touch force]).andReturn(0.7);
+  [gestureRecognizer touchesBegan:[NSSet setWithObject:touch] withEvent:event];
+  [gestureRecognizer touchesMoved:[NSSet setWithObject:touch] withEvent:event];
+
+  // Check that the dispatcher is called when the force touch is detected.
+  OCMExpect([dispatcher showTabGridButtonPopup]);
+
+  touch = OCMClassMock([UITouch class]);
+  OCMStub([touch maximumPossibleForce]).andReturn(1);
+  OCMStub([touch force]).andReturn(0.9);
+  [gestureRecognizer touchesMoved:[NSSet setWithObject:touch] withEvent:event];
+
+  base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.05));
+
+  EXPECT_OCMOCK_VERIFY(dispatcher);
+
+  // Check that the longPressDelegate is notified when the gesture recognizer
+  // changes, even with lower force.
+  [[[longPressDelegate expect] ignoringNonObjectArgs]
+      longPressFocusPointChangedTo:CGPointZero];
+
+  touch = OCMClassMock([UITouch class]);
+  OCMStub([touch maximumPossibleForce]).andReturn(1);
+  OCMStub([touch force]).andReturn(0.9);
+  [gestureRecognizer touchesMoved:[NSSet setWithObject:touch] withEvent:event];
+
+  base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.05));
+
+  EXPECT_OCMOCK_VERIFY(longPressDelegate);
+
+  // Change the state to Ended here, as the long press gesture recognizer isn't
+  // working on unit test (the state is cancelled).
+  gestureRecognizer.state = UIGestureRecognizerStateEnded;
+
+  base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.05));
+
+  EXPECT_OCMOCK_VERIFY(longPressDelegate);
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/ui/util/BUILD.gn b/ios/chrome/browser/ui/util/BUILD.gn
index be0c9bf..6abd6cb 100644
--- a/ios/chrome/browser/ui/util/BUILD.gn
+++ b/ios/chrome/browser/ui/util/BUILD.gn
@@ -63,6 +63,7 @@
   sources = [
     "CRUILabel+AttributeUtils_unittest.mm",
     "core_text_util_unittest.mm",
+    "force_touch_long_press_gesture_recognizer_unittest.mm",
     "label_link_controller_unittest.mm",
     "label_observer_unittest.mm",
     "manual_text_framer_unittest.mm",
@@ -76,6 +77,7 @@
     "//ios/third_party/material_components_ios",
     "//ios/third_party/material_roboto_font_loader_ios",
     "//testing/gtest",
+    "//third_party/ocmock",
     "//url",
   ]
 }
diff --git a/ios/chrome/browser/ui/util/force_touch_long_press_gesture_recognizer_unittest.mm b/ios/chrome/browser/ui/util/force_touch_long_press_gesture_recognizer_unittest.mm
new file mode 100644
index 0000000..f3909a7f
--- /dev/null
+++ b/ios/chrome/browser/ui/util/force_touch_long_press_gesture_recognizer_unittest.mm
@@ -0,0 +1,69 @@
+// Copyright 2018 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.
+
+#import "ios/chrome/browser/ui/util/force_touch_long_press_gesture_recognizer.h"
+
+#import <UIKit/UIGestureRecognizerSubclass.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface ForceTouchLongPressGestureRecognizerReceiverForTest : NSObject
+
+@end
+
+@implementation ForceTouchLongPressGestureRecognizerReceiverForTest
+
+- (void)handleGestureRecognizer:(UIGestureRecognizer*)gesture {
+}
+
+@end
+
+namespace {
+
+using ForceTouchLongPressGestureRecognizerTest = PlatformTest;
+
+TEST_F(ForceTouchLongPressGestureRecognizerTest, DetectForceTouch) {
+  ForceTouchLongPressGestureRecognizerReceiverForTest* testReceiver =
+      [[ForceTouchLongPressGestureRecognizerReceiverForTest alloc] init];
+
+  ForceTouchLongPressGestureRecognizer* gestureRecognizer =
+      [[ForceTouchLongPressGestureRecognizer alloc]
+          initWithTarget:testReceiver
+                  action:@selector(handleGestureRecognizer:)];
+  gestureRecognizer.forceThreshold = 0.6;
+
+  ASSERT_EQ(UIGestureRecognizerStatePossible, gestureRecognizer.state);
+
+  id event = OCMClassMock([UIEvent class]);
+
+  id touch = OCMClassMock([UITouch class]);
+  OCMStub([touch maximumPossibleForce]).andReturn(1);
+  OCMStub([touch force]).andReturn(0.5);
+  [gestureRecognizer touchesBegan:[NSSet setWithObject:touch] withEvent:event];
+  [gestureRecognizer touchesMoved:[NSSet setWithObject:touch] withEvent:event];
+
+  EXPECT_EQ(UIGestureRecognizerStatePossible, gestureRecognizer.state);
+
+  touch = OCMClassMock([UITouch class]);
+  OCMStub([touch maximumPossibleForce]).andReturn(1);
+  OCMStub([touch force]).andReturn(0.7);
+  [gestureRecognizer touchesMoved:[NSSet setWithObject:touch] withEvent:event];
+
+  EXPECT_EQ(UIGestureRecognizerStateBegan, gestureRecognizer.state);
+
+  touch = OCMClassMock([UITouch class]);
+  OCMStub([touch maximumPossibleForce]).andReturn(1);
+  OCMStub([touch force]).andReturn(0.7);
+  [gestureRecognizer touchesEnded:[NSSet setWithObject:touch] withEvent:event];
+
+  EXPECT_EQ(UIGestureRecognizerStateCancelled, gestureRecognizer.state);
+}
+
+}  // namespace
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index a4facee..12ea7a3 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -235,6 +235,7 @@
     "//ios/chrome/browser/ui/table_view/cells:unit_tests",
     "//ios/chrome/browser/ui/tabs:unit_tests",
     "//ios/chrome/browser/ui/toolbar:unit_tests",
+    "//ios/chrome/browser/ui/toolbar/adaptive:unit_tests",
     "//ios/chrome/browser/ui/toolbar/clean:unit_tests",
     "//ios/chrome/browser/ui/toolbar/legacy:unit_tests",
     "//ios/chrome/browser/ui/tools_menu:unit_tests",