Bug 1355656 - Implement test cases for two finger fling gestures. r=botond
authorjlogandavison <jlogandavison@gmail.com>
Wed, 30 May 2018 19:43:28 +0000
changeset 421592 21861ee0ddb89d1d3b575ccfeaa1c5043374eeab
parent 421591 fac57eb7e7aaaa0c5396d39dc67eae2451754b9f
child 421593 011f238cc9ab82efbaba621046514cd689bd1f86
push id34098
push usernbeleuzu@mozilla.com
push dateWed, 06 Jun 2018 17:00:32 +0000
treeherdermozilla-central@04cc917f68c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1355656
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1355656 - Implement test cases for two finger fling gestures. r=botond * Add option for second focus point in PinchWithTouchInput * Integrate content controller in PinchWithTouchInput * Move PinchWithTouchInput implementation to APZCTesterBase * Move CreateSingleTouchData to APZTestCommon.h * Add optional PinchOptions parameter to PinchWithTouchInput PinchOptions dictates which fingers should be lifted at the end of the simulated pinch gesture. The default is to lift BOTH fingers.
gfx/layers/apz/test/gtest/APZTestCommon.h
gfx/layers/apz/test/gtest/InputUtils.h
gfx/layers/apz/test/gtest/TestPinching.cpp
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -38,16 +38,35 @@ using namespace mozilla::layers;
 using ::testing::_;
 using ::testing::NiceMock;
 using ::testing::AtLeast;
 using ::testing::AtMost;
 using ::testing::MockFunction;
 using ::testing::InSequence;
 typedef mozilla::layers::GeckoContentController::TapType TapType;
 
+// Some helper functions for constructing input event objects suitable to be
+// passed either to an APZC (which expects an transformed point), or to an APZTM
+// (which expects an untransformed point). We handle both cases by setting both
+// the transformed and untransformed fields to the same value.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, const ScreenIntPoint& aPoint)
+{
+  SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
+  touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
+  return touch;
+}
+
+// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY)
+{
+  return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
+}
+
 template<class T>
 class ScopedGfxPref {
 public:
   ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal)
     : mSetPrefFunc(aSetPrefFunc)
   {
     mOldVal = aGetPrefFunc();
     aSetPrefFunc(aVal);
@@ -327,16 +346,29 @@ public:
      * Do not adjust the touch-start coordinates to overcome the touch-start
      * tolerance threshold. If this option is passed, it's up to the caller
      * to pass in coordinates that are sufficient to overcome the touch-start
      * tolerance *and* cause the desired amount of scrolling.
      */
     ExactCoordinates = 0x2
   };
 
+  enum class PinchOptions {
+    None = 0,
+    LiftFinger1 = 0x1,
+    LiftFinger2 = 0x2,
+    /*
+     * The bitwise OR result of (LiftFinger1 | LiftFinger2).
+     * Defined explicitly here because it is used as the default
+     * argument for PinchWithTouchInput which is defined BEFORE the
+     * definition of operator| for this class.
+     */
+    LiftBothFingers = 0x3
+  };
+
   template<class InputReceiver>
   void Tap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
            TimeDuration aTapLength,
            nsEventStatus (*aOutEventStatuses)[2] = nullptr,
            uint64_t* aOutInputBlockId = nullptr);
 
   template<class InputReceiver>
   void TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
@@ -385,21 +417,48 @@ public:
                  nsEventStatus (*aOutEventStatuses)[4] = nullptr,
                  uint64_t (*aOutInputBlockIds)[2] = nullptr);
 
   template<class InputReceiver>
   void DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
                                const ScreenIntPoint& aPoint,
                                uint64_t (*aOutInputBlockIds)[2] = nullptr);
 
+  template<class InputReceiver>
+  void PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
+                           const ScreenIntPoint& aFocus, const ScreenIntPoint& aSecondFocus,
+                           float aScale,
+                           int& inputId,
+                           nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+                           nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+                           uint64_t* aOutInputBlockId = nullptr,
+                           PinchOptions aOptions = PinchOptions::LiftBothFingers);
+
+  // Pinch with one focus point. Zooms in place with no panning
+  template<class InputReceiver>
+  void PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
+                           const ScreenIntPoint& aFocus, float aScale,
+                           int& inputId,
+                           nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+                           nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+                           uint64_t* aOutInputBlockId = nullptr,
+                           PinchOptions aOptions = PinchOptions::LiftBothFingers);
+
+  template<class InputReceiver>
+  void PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+                                         const ScreenIntPoint& aFocus, float aScale,
+                                         int& inputId, bool aShouldTriggerPinch,
+                                         nsTArray<uint32_t>* aAllowedTouchBehaviors);
+
 protected:
   RefPtr<MockContentControllerDelayed> mcc;
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PanOptions)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PinchOptions)
 
 template<class InputReceiver>
 void
 APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
                     const ScreenIntPoint& aPoint, TimeDuration aTapLength,
                     nsEventStatus (*aOutEventStatuses)[2],
                     uint64_t* aOutInputBlockId)
 {
@@ -629,16 +688,129 @@ APZCTesterBase::DoubleTapAndCheckStatus(
   nsEventStatus statuses[4];
   DoubleTap(aTarget, aPoint, &statuses, aOutInputBlockIds);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
 }
 
+template<class InputReceiver>
+void
+APZCTesterBase::PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
+                                    const ScreenIntPoint& aFocus, float aScale,
+                                    int& inputId,
+                                    nsTArray<uint32_t>* aAllowedTouchBehaviors,
+                                    nsEventStatus (*aOutEventStatuses)[4],
+                                    uint64_t* aOutInputBlockId,
+                                    PinchOptions aOptions)
+{
+  //Perform a pinch gesture with the same start & end focus point
+  PinchWithTouchInput(aTarget, aFocus, aFocus, aScale, inputId,
+                      aAllowedTouchBehaviors, aOutEventStatuses,
+                      aOutInputBlockId, aOptions);
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
+                                    const ScreenIntPoint& aFocus, const ScreenIntPoint& aSecondFocus,
+                                    float aScale,
+                                    int& inputId,
+                                    nsTArray<uint32_t>* aAllowedTouchBehaviors,
+                                    nsEventStatus (*aOutEventStatuses)[4],
+                                    uint64_t* aOutInputBlockId,
+                                    PinchOptions aOptions)
+{
+  // Having pinch coordinates in float type may cause problems with high-precision scale values
+  // since SingleTouchData accepts integer value. But for trivial tests it should be ok.
+  float pinchLength = 100.0;
+  float pinchLengthScaled = pinchLength * aScale;
+
+  // Even if the caller doesn't care about the block id, we need it to set the
+  // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+  uint64_t blockId;
+  if (!aOutInputBlockId) {
+    aOutInputBlockId = &blockId;
+  }
+
+  const TimeDuration TIME_BETWEEN_TOUCH_EVENT = TimeDuration::FromMilliseconds(50);
+
+  MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, mcc->Time(), 0);
+  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
+  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
+  nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[0] = status;
+  }
+
+  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+  if (aAllowedTouchBehaviors) {
+    EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
+    aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
+  } else if (gfxPrefs::TouchActionEnabled()) {
+    SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
+  }
+
+  MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
+  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLength, aFocus.y));
+  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLength, aFocus.y));
+  status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[1] = status;
+  }
+
+  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+  MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
+  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aSecondFocus.x - pinchLengthScaled, aSecondFocus.y));
+  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aSecondFocus.x + pinchLengthScaled, aSecondFocus.y));
+  status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[2] = status;
+  }
+
+  if (aOptions & (PinchOptions::LiftFinger1 | PinchOptions::LiftFinger2)) {
+    mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+    MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0);
+    if (aOptions & PinchOptions::LiftFinger1) {
+      mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aSecondFocus.x - pinchLengthScaled, aSecondFocus.y));
+    }
+    if (aOptions & PinchOptions::LiftFinger2) {
+      mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aSecondFocus.x + pinchLengthScaled, aSecondFocus.y));
+    }
+    status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
+    if (aOutEventStatuses) {
+      (*aOutEventStatuses)[3] = status;
+    }
+  }
+
+  inputId += 2;
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+                                                  const ScreenIntPoint& aFocus, float aScale,
+                                                  int& inputId, bool aShouldTriggerPinch,
+                                                  nsTArray<uint32_t>* aAllowedTouchBehaviors)
+{
+  nsEventStatus statuses[4];  // down, move, move, up
+  PinchWithTouchInput(aTarget, aFocus, aScale, inputId, aAllowedTouchBehaviors, &statuses);
+
+  nsEventStatus expectedMoveStatus = aShouldTriggerPinch
+      ? nsEventStatus_eConsumeDoDefault
+      : nsEventStatus_eIgnore;
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+  EXPECT_EQ(expectedMoveStatus, statuses[1]);
+  EXPECT_EQ(expectedMoveStatus, statuses[2]);
+}
+
 AsyncPanZoomController*
 TestAPZCTreeManager::NewAPZCInstance(LayersId aLayersId,
                                      GeckoContentController* aController)
 {
   MockContentControllerDelayed* mcc = static_cast<MockContentControllerDelayed*>(aController);
   return new TestAsyncPanZoomController(aLayersId, mcc, this,
       AsyncPanZoomController::USE_GESTURE_DETECTOR);
 }
--- a/gfx/layers/apz/test/gtest/InputUtils.h
+++ b/gfx/layers/apz/test/gtest/InputUtils.h
@@ -23,35 +23,16 @@
  * void SetAllowedTouchBehavior(uint64_t aInputBlockId,
  *                              const nsTArray<uint32_t>& aBehaviours);
  * The classes that currently implement these are APZCTreeManager and
  * TestAsyncPanZoomController. Using this template allows us to test individual
  * APZC instances in isolation and also an entire APZ tree, while using the same
  * code to dispatch input events.
  */
 
-// Some helper functions for constructing input event objects suitable to be
-// passed either to an APZC (which expects an transformed point), or to an APZTM
-// (which expects an untransformed point). We handle both cases by setting both
-// the transformed and untransformed fields to the same value.
-SingleTouchData
-CreateSingleTouchData(int32_t aIdentifier, const ScreenIntPoint& aPoint)
-{
-  SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
-  touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
-  return touch;
-}
-
-// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
-SingleTouchData
-CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY)
-{
-  return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
-}
-
 PinchGestureInput
 CreatePinchGestureInput(PinchGestureInput::PinchGestureType aType,
                         const ScreenPoint& aFocus,
                         float aCurrentSpan, float aPreviousSpan)
 {
   ParentLayerPoint localFocus(aFocus.x, aFocus.y);
   PinchGestureInput result(aType, 0, TimeStamp(), localFocus,
                            aCurrentSpan, aPreviousSpan, 0);
@@ -156,97 +137,16 @@ PinchWithPinchInputAndCheckStatus(const 
   nsEventStatus expectedStatus = aShouldTriggerPinch
       ? nsEventStatus_eConsumeNoDefault
       : nsEventStatus_eIgnore;
   EXPECT_EQ(expectedStatus, statuses[0]);
   EXPECT_EQ(expectedStatus, statuses[1]);
 }
 
 template<class InputReceiver>
-void
-PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
-                    const ScreenIntPoint& aFocus, float aScale,
-                    int& inputId,
-                    nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
-                    nsEventStatus (*aOutEventStatuses)[4] = nullptr,
-                    uint64_t* aOutInputBlockId = nullptr)
-{
-  // Having pinch coordinates in float type may cause problems with high-precision scale values
-  // since SingleTouchData accepts integer value. But for trivial tests it should be ok.
-  float pinchLength = 100.0;
-  float pinchLengthScaled = pinchLength * aScale;
-
-  // Even if the caller doesn't care about the block id, we need it to set the
-  // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
-  uint64_t blockId;
-  if (!aOutInputBlockId) {
-    aOutInputBlockId = &blockId;
-  }
-
-  MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
-  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
-  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
-  nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
-  if (aOutEventStatuses) {
-    (*aOutEventStatuses)[0] = status;
-  }
-
-  if (aAllowedTouchBehaviors) {
-    EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
-    aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
-  } else if (gfxPrefs::TouchActionEnabled()) {
-    SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
-  }
-
-  MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
-  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLength, aFocus.y));
-  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLength, aFocus.y));
-  status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
-  if (aOutEventStatuses) {
-    (*aOutEventStatuses)[1] = status;
-  }
-
-  MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
-  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
-  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
-  status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
-  if (aOutEventStatuses) {
-    (*aOutEventStatuses)[2] = status;
-  }
-
-  MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
-  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
-  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
-  status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
-  if (aOutEventStatuses) {
-    (*aOutEventStatuses)[3] = status;
-  }
-
-  inputId += 2;
-}
-
-template<class InputReceiver>
-void
-PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
-                                  const ScreenIntPoint& aFocus, float aScale,
-                                  int& inputId, bool aShouldTriggerPinch,
-                                  nsTArray<uint32_t>* aAllowedTouchBehaviors)
-{
-  nsEventStatus statuses[4];  // down, move, move, up
-  PinchWithTouchInput(aTarget, aFocus, aScale, inputId, aAllowedTouchBehaviors, &statuses);
-
-  nsEventStatus expectedMoveStatus = aShouldTriggerPinch
-      ? nsEventStatus_eConsumeDoDefault
-      : nsEventStatus_eIgnore;
-  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
-  EXPECT_EQ(expectedMoveStatus, statuses[1]);
-  EXPECT_EQ(expectedMoveStatus, statuses[2]);
-}
-
-template<class InputReceiver>
 nsEventStatus
 Wheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
       const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
 {
   ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
       ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
       aPoint, aDelta.x, aDelta.y, false, WheelDeltaAdjustmentStrategy::eNone);
   return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
--- a/gfx/layers/apz/test/gtest/TestPinching.cpp
+++ b/gfx/layers/apz/test/gtest/TestPinching.cpp
@@ -265,16 +265,68 @@ TEST_F(APZCPinchGestureDetectorTester, P
   // Since we are preventing the pinch action we should not be sending the pinch
   // gesture notifications that would normally be sent when APZAllowZooming is
   // false.
   EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
 
   DoPinchWithPreventDefaultTest();
 }
 
+TEST_F(APZCPinchGestureDetectorTester, Panning_TwoFingerFling_ZoomDisabled) {
+  SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+  MakeApzcUnzoomable();
+
+  // Perform a two finger pan
+  int touchInputId = 0;
+  uint64_t blockId = 0;
+  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100),
+      1, touchInputId, nullptr, nullptr, &blockId);
+
+  // Expect to be in a flinging state
+  apzc->AssertStateIsFling();
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Panning_TwoFingerFling_ZoomEnabled) {
+  SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+  MakeApzcZoomable();
+
+  // Perform a two finger pan
+  int touchInputId = 0;
+  uint64_t blockId = 0;
+  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100),
+      1, touchInputId, nullptr, nullptr, &blockId);
+
+  // Expect to NOT be in flinging state
+  apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Panning_TwoThenOneFingerFling_ZoomEnabled) {
+  SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+  MakeApzcZoomable();
+
+  // Perform a two finger pan lifting only the first finger
+  int touchInputId = 0;
+  uint64_t blockId = 0;
+  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100),
+      1, touchInputId, nullptr, nullptr, &blockId, PinchOptions::LiftFinger2);
+
+  // Lift second finger after a pause
+  mcc->AdvanceBy(TimeDuration::FromMilliseconds(50));
+  TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time());
+
+  // Expect to NOT be in flinging state
+  apzc->AssertStateIsReset();
+}
+
 TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
   // set up APZ
   apzc->SetFrameMetrics(GetPinchableFrameMetrics());
   MakeApzcUnzoomable();
 
   nsEventStatus statuses[3];  // scalebegin, scale, scaleend
   PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300),
       10, &statuses);