Tests under pointerevents/ios are flaky
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 May 2019 20:39:28 +0000 (20:39 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 May 2019 20:39:28 +0000 (20:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197624

Reviewed by Dean Jackson.

Tests under pointerevents/ios generate touches that use UIScriptController may not succeed if ran in multiple iterations or in a specific
order due to not ensuring that all touches are released when the test completes. We now ensure that we do when running swipes, taps, and pinches.

* pointerevents/ios/pointer-events-dispatch-on-stylus.html: Use the new ui.tapStylus() method to generate a tap with the stylus which ensures all
touches are removed upon completion.
* pointerevents/ios/pointer-events-dispatch-on-touch.html: Use a tap to ensure all touches are removed upon completion.
* pointerevents/ios/pointer-events-implicit-capture-has-pointer-capture-in-pointer-down.html: Use a tap to ensure all touches are removed upon completion.
* pointerevents/ios/pointer-events-implicit-capture-release-exception.html: Use a tap to ensure all touches are removed upon completion.
* pointerevents/ios/pointer-events-implicit-capture-release.html: Use a tap to ensure all touches are removed upon completion.
* pointerevents/ios/pointer-events-prevent-default-allows-click-event.html: Ensure both the "click" event and the tap generation have succeeded before
marking the test as complete.
* pointerevents/ios/pointer-events-set-pointer-capture-exceptions.html: Use a tap to ensure all touches are removed upon completion.
* pointerevents/ios/touch-action-none-link-traversal.html: Ensure both the "load" event and the tap generation have succeeded before marking the test
as complete.
* pointerevents/ios/touch-action-pan-x-pan-y.html: Remove the requestAnimationFrame() call since ui.swipe() now resolves its promise once all touches
have completed.
* pointerevents/ios/touch-action-pan-x.html: Remove the requestAnimationFrame() call since ui.swipe() now resolves its promise once all touches
have completed.
* pointerevents/ios/touch-action-pan-y.html: Remove the requestAnimationFrame() call since ui.swipe() now resolves its promise once all touches
have completed.
* pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html: Remove the requestAnimationFrame() call since ui.pinchOut() now resolves its promise
once all touches have completed.
* pointerevents/ios/touch-action-pointercancel-pan-x.html: We don't need to track "pointermove" events since dispatch of "pointercancel" is asynchronous
and the number of "pointermove" events prior to its dispatch can legitimately vary.
* pointerevents/ios/touch-action-pointercancel-pan-y.html: We don't need to track "pointermove" events since dispatch of "pointercancel" is asynchronous
and the number of "pointermove" events prior to its dispatch can legitimately vary.
* pointerevents/utils.js:
(const.ui.new.UIController.prototype.swipe): Wait until the swipe is complete before resolving the promise.
(const.ui.new.UIController.prototype.pinchOut): Use a custom sequence to ensure that the pinch releases touches upon completion.
(const.ui.new.UIController.prototype.tapStylus): Introduce this new method to perform a stylus tap which ensures all touches are complete before resolving
the promise.
(const.ui.new.UIController.prototype.beginTouches): Deleted.
(const.ui.new.UIController.prototype.beginStylus): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244974 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/pointerevents/ios/pointer-events-dispatch-on-stylus.html
LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch.html
LayoutTests/pointerevents/ios/pointer-events-implicit-capture-has-pointer-capture-in-pointer-down.html
LayoutTests/pointerevents/ios/pointer-events-implicit-capture-release-exception.html
LayoutTests/pointerevents/ios/pointer-events-implicit-capture-release.html
LayoutTests/pointerevents/ios/pointer-events-prevent-default-allows-click-event.html
LayoutTests/pointerevents/ios/pointer-events-set-pointer-capture-exceptions.html
LayoutTests/pointerevents/ios/touch-action-none-link-traversal.html
LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y.html
LayoutTests/pointerevents/ios/touch-action-pan-x.html
LayoutTests/pointerevents/ios/touch-action-pan-y.html
LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html
LayoutTests/pointerevents/ios/touch-action-pointercancel-pan-x.html
LayoutTests/pointerevents/ios/touch-action-pointercancel-pan-y.html
LayoutTests/pointerevents/utils.js

index 97b7247..4887e17 100644 (file)
@@ -1,3 +1,44 @@
+2019-05-06  Antoine Quint  <graouts@apple.com>
+
+        Tests under pointerevents/ios are flaky
+        https://bugs.webkit.org/show_bug.cgi?id=197624
+
+        Reviewed by Dean Jackson.
+
+        Tests under pointerevents/ios generate touches that use UIScriptController may not succeed if ran in multiple iterations or in a specific
+        order due to not ensuring that all touches are released when the test completes. We now ensure that we do when running swipes, taps, and pinches.
+
+        * pointerevents/ios/pointer-events-dispatch-on-stylus.html: Use the new ui.tapStylus() method to generate a tap with the stylus which ensures all
+        touches are removed upon completion.
+        * pointerevents/ios/pointer-events-dispatch-on-touch.html: Use a tap to ensure all touches are removed upon completion.
+        * pointerevents/ios/pointer-events-implicit-capture-has-pointer-capture-in-pointer-down.html: Use a tap to ensure all touches are removed upon completion.
+        * pointerevents/ios/pointer-events-implicit-capture-release-exception.html: Use a tap to ensure all touches are removed upon completion.
+        * pointerevents/ios/pointer-events-implicit-capture-release.html: Use a tap to ensure all touches are removed upon completion.
+        * pointerevents/ios/pointer-events-prevent-default-allows-click-event.html: Ensure both the "click" event and the tap generation have succeeded before
+        marking the test as complete.
+        * pointerevents/ios/pointer-events-set-pointer-capture-exceptions.html: Use a tap to ensure all touches are removed upon completion.
+        * pointerevents/ios/touch-action-none-link-traversal.html: Ensure both the "load" event and the tap generation have succeeded before marking the test
+        as complete.
+        * pointerevents/ios/touch-action-pan-x-pan-y.html: Remove the requestAnimationFrame() call since ui.swipe() now resolves its promise once all touches
+        have completed.
+        * pointerevents/ios/touch-action-pan-x.html: Remove the requestAnimationFrame() call since ui.swipe() now resolves its promise once all touches
+        have completed.
+        * pointerevents/ios/touch-action-pan-y.html: Remove the requestAnimationFrame() call since ui.swipe() now resolves its promise once all touches
+        have completed.
+        * pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html: Remove the requestAnimationFrame() call since ui.pinchOut() now resolves its promise
+        once all touches have completed.
+        * pointerevents/ios/touch-action-pointercancel-pan-x.html: We don't need to track "pointermove" events since dispatch of "pointercancel" is asynchronous
+        and the number of "pointermove" events prior to its dispatch can legitimately vary.
+        * pointerevents/ios/touch-action-pointercancel-pan-y.html: We don't need to track "pointermove" events since dispatch of "pointercancel" is asynchronous
+        and the number of "pointermove" events prior to its dispatch can legitimately vary.
+        * pointerevents/utils.js:
+        (const.ui.new.UIController.prototype.swipe): Wait until the swipe is complete before resolving the promise.
+        (const.ui.new.UIController.prototype.pinchOut): Use a custom sequence to ensure that the pinch releases touches upon completion.
+        (const.ui.new.UIController.prototype.tapStylus): Introduce this new method to perform a stylus tap which ensures all touches are complete before resolving
+        the promise.
+        (const.ui.new.UIController.prototype.beginTouches): Deleted.
+        (const.ui.new.UIController.prototype.beginStylus): Deleted.
+
 2019-05-06  Truitt Savell  <tsavell@apple.com>
 
         Fix typo in https://trac.webkit.org/changeset/244962/webkit
index f522931..6df32c7 100644 (file)
@@ -27,7 +27,7 @@ target_test((target, test) => {
         assert_approx_equals(event.tiltY, 20, 1);
         test.done();
     });
-    ui.beginStylus({ x: 50, y: 50, pressure: 0.75, azimuthAngle: fifteenDegrees, altitudeAngle: thirtyDegrees });
+    ui.tapStylus({ x: 50, y: 50, pressure: 0.75, azimuthAngle: fifteenDegrees, altitudeAngle: thirtyDegrees });
 }, "Pointer events get dispatched in response to a stylus.");
 
 </script>
index 5d4748c..3f71121 100644 (file)
@@ -21,7 +21,7 @@ target_test((target, test) => {
         assert_equals(event.pointerType, "touch");
         test.done();
     });
-    ui.beginTouches({ x: 50, y: 50 });
+    ui.tap({ x: 50, y: 50 });
 }, "Pointer events get dispatched in response to a touch.");
 
 </script>
index 3587115..8470ceb 100644 (file)
@@ -14,7 +14,7 @@
 
 target_test((target, test) => {
     target.addEventListener("pointerdown", event => assert_true(target.hasPointerCapture(event.pointerId)));
-    ui.beginTouches({ x: 50, y: 50 }).then(() => test.done());
+    ui.tap({ x: 50, y: 50 }).then(() => test.done());
 }, "Calling hasPointerCapture() in the 'pointerdown' event handler returns true for direct manipulation devices.");
 
 </script>
index 8c9d1ec..4d48e2f 100644 (file)
@@ -18,7 +18,7 @@ target_test((target, test) => {
         assert_throws("NotFoundError", () => target.releasePointerCapture(event.pointerId + 1));
         assert_true(target.hasPointerCapture(event.pointerId));
     });
-    ui.beginTouches({ x: 50, y: 50 }).then(() => test.done());
+    ui.tap({ x: 50, y: 50 }).then(() => test.done());
 }, "Calling releasePointerCapture() in the 'pointerdown' event handler with a bogus pointer id raises an exception and does not alter pointer capture.");
 
 </script>
index 0f3b673..d4ba0bf 100644 (file)
@@ -18,7 +18,7 @@ target_test((target, test) => {
         target.releasePointerCapture(event.pointerId);
         assert_false(target.hasPointerCapture(event.pointerId));
     });
-    ui.beginTouches({ x: 50, y: 50 }).then(() => test.done());
+    ui.tap({ x: 50, y: 50 }).then(() => test.done());
 }, "Calling releasePointerCapture() in the 'pointerdown' event handler makes hasPointerCapture() return false.");
 
 </script>
index a8ae94f..c3a9d5a 100644 (file)
@@ -14,8 +14,9 @@
 
 target_test((target, test) => {
     target.addEventListener("pointerdown", event => event.preventDefault());
-    target.addEventListener("click", event => test.done());
-    ui.tap({ x: 100, y: 100 });
+    const clicked = new Promise(resolve => target.addEventListener("click", resolve));
+    const tapped = ui.tap({ x: 100, y: 100 });
+    Promise.all([clicked, tapped]).then(() => test.done());
 }, "A 'click' event is dispatched when tapping even if preventDefault() was called for a pointer event.");
 
 </script>
index e397491..e325615 100644 (file)
@@ -19,7 +19,7 @@ target_test((target, test) => {
         assert_true(target.hasPointerCapture(event.pointerId));
         assert_throws("InvalidStateError", () => document.createElement("div").setPointerCapture(event.pointerId), "Calling setPointerCapture() with a valid pointer id on a disconnected element throws an InvalidStateError exception.");
     });
-    ui.beginTouches({ x: 50, y: 50 }).then(() => test.done());
+    ui.tap({ x: 50, y: 50 }).then(() => test.done());
 }, "The setPointerCapture() method can throw.");
 
 </script>
index 5e4baa7..835d9e9 100644 (file)
@@ -39,12 +39,16 @@ async_test(test => {
 
     assert_equals(iframe.contentWindow.location.href, "about:blank", "The iframe initially has no URL.");
 
-    iframe.addEventListener("load", () => {
-        assert_true(iframe.contentWindow.location.href.includes("disabled.html"), "Upon navigation the URL has a location.");
-        test.done();
+    const loaded = new Promise(resolve => {
+        iframe.addEventListener("load", () => {
+            assert_true(iframe.contentWindow.location.href.includes("disabled.html"), "Upon navigation the URL has a location.");
+            resolve();
+        });
     });
 
-    ui.tap({ x: 100, y: 100 });
+    const tapped = ui.tap({ x: 100, y: 100 });
+
+    Promise.all([loaded, tapped]).then(() => test.done());
 }, "Testing that setting touch-action: none allows link traversal.");
 
 </script>
index 82b5008..d201b9c 100644 (file)
@@ -19,11 +19,9 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
     target.style.touchAction = "pan-x pan-y";
 
     ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
-        requestAnimationFrame(() => {
-            assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
-            assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
-            test.done();
-        });
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
     });
 
 }, "Testing that setting 'touch-action: pan-x pan-y' on an element allows page scrolling in both axes.");
index fd9c145..cb9205f 100644 (file)
@@ -19,11 +19,9 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
     target.style.touchAction = "pan-x";
 
     ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
-        requestAnimationFrame(() => {
-            assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
-            assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
-            test.done();
-        });
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
+        test.done();
     });
 }, "Testing that setting touch-action: pan-x on an element prevents page scrolling in the y-axis.");
 
index 6ba9c81..d4407d3 100644 (file)
@@ -19,11 +19,9 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
     target.style.touchAction = "pan-y";
 
     ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
-        requestAnimationFrame(() => {
-            assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
-            assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
-            test.done();
-        });
+        assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
     });
 }, "Testing that setting touch-action: pan-y on an element prevents page scrolling in the x-axis.");
 
index 2795760..2da63c9 100644 (file)
@@ -19,10 +19,8 @@ target_test({ width: "400px", height: "400px" }, (target, test) => {
     target.style.touchAction = "pinch-zoom";
 
     ui.pinchOut({ x: 50, y: 50, width: 100, height: 100, scale: 0.5 }).then(() => {
-        requestAnimationFrame(() => {
-            assert_not_equals(window.internals.pageScaleFactor(), 1, "The page was scaled.");
-            test.done();
-        });
+        assert_not_equals(window.internals.pageScaleFactor(), 1, "The page was scaled.");
+        test.done();
     });
 }, "Testing that setting touch-action: pinch-zoom on an element allows page zooming.");
 
index 04e06d6..b2a247a 100644 (file)
@@ -18,7 +18,7 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
 
     target.style.touchAction = "pan-x";
 
-    const eventTracker = new EventTracker(target, ["pointerdown", "pointermove", "pointerup", "pointercancel"]);
+    const eventTracker = new EventTracker(target, ["pointerdown", "pointerup", "pointercancel"]);
 
     const one = ui.finger();
     ui.sequence([
@@ -31,9 +31,6 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
     ]).then(() => {
         eventTracker.assertMatchesEvents([
             { type: "pointerdown" },
-            { type: "pointermove" },
-            { type: "pointermove" },
-            { type: "pointermove" },
             { type: "pointercancel" }
         ]);
         test.done();
index 5398c67..f97ec06 100644 (file)
@@ -18,7 +18,7 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
 
     target.style.touchAction = "pan-y";
 
-    const eventTracker = new EventTracker(target, ["pointerdown", "pointermove", "pointerup", "pointercancel"]);
+    const eventTracker = new EventTracker(target, ["pointerdown", "pointerup", "pointercancel"]);
 
     const one = ui.finger();
     ui.sequence([
@@ -31,9 +31,6 @@ target_test({ width: "200px", height: "200px" }, (target, test) => {
     ]).then(() => {
         eventTracker.assertMatchesEvents([
             { type: "pointerdown" },
-            { type: "pointermove" },
-            { type: "pointermove" },
-            { type: "pointermove" },
             { type: "pointercancel" }
         ]);
         test.done();
index e836778..b144edc 100644 (file)
@@ -107,15 +107,12 @@ const ui = new (class UIController {
         return this.fingers[id] = new Finger(id);
     }
 
-    beginTouches(options)
-    {
-        return this._run(`uiController.touchDownAtPoint(${options.x}, ${options.y}, ${options.numberOfTouches || 1})`);
-    }
-
     swipe(from, to)
     {
-        const durationInSeconds = 0.5;
-        return this._run(`uiController.dragFromPointToPoint(${from.x}, ${from.y}, ${to.x}, ${to.y}, ${durationInSeconds})`);
+        const durationInSeconds = 0.1;
+        return new Promise(resolve => this._run(`uiController.dragFromPointToPoint(${from.x}, ${from.y}, ${to.x}, ${to.y}, ${durationInSeconds})`).then(() =>
+            setTimeout(resolve, durationInSeconds * 1000)
+        ));
     }
 
     tap(options)
@@ -132,55 +129,30 @@ const ui = new (class UIController {
         options.x = options.x || 0;
         options.y = options.y || 0;
 
-        const startEvent = {
-            inputType : "hand",
-            timeOffset : 0,
-            touches : [
-                { inputType : "finger",
-                  phase : "moved",
-                  id : 1,
-                  x : options.x,
-                  y : options.y,
-                  pressure : 0
-                },
-                { inputType : "finger",
-                  phase : "moved",
-                  id : 2,
-                  x : (options.x + options.width) / options.scale,
-                  y : (options.y + options.height) / options.scale,
-                  pressure : 0
-                }
-            ]
-        };
-
-        const endEvent = {
-            inputType : "hand",
-            timeOffset : 0.5,
-            touches : [
-                { inputType : "finger",
-                  phase : "moved",
-                  id : 1,
-                  x : options.x,
-                  y : options.y,
-                  pressure : 0
-                },
-                { inputType : "finger",
-                  phase : "moved",
-                  id : 2,
-                  x : options.x + options.width,
-                  y : options.y + options.height,
-                  pressure : 0
-                }
-            ]
-        };
-
-        return this._runEvents([{
-            interpolate : "linear",
-            timestep: 0.1,
-            coordinateSpace : "content",
-            startEvent: startEvent,
-            endEvent: endEvent
-        }]);
+        const startPoint = { x: options.x + options.width, y: options.y + options.height };
+        const endPoint = { x: options.x + options.width * options.scale, y: options.y + options.height * options.scale };
+
+        function step(factor)
+        {
+            return {
+                x: endPoint.x + (startPoint.x - endPoint.x) * (1 - factor),
+                y: endPoint.y + (startPoint.y - endPoint.y) * (1 - factor)
+            };
+        }
+
+        const one = this.finger();
+        const two = this.finger();
+        return this.sequence([
+            one.begin({ x: options.x, y: options.y }),
+            two.begin(step(0)),
+            two.move(step(0.2)),
+            two.move(step(0.4)),
+            two.move(step(0.6)),
+            two.move(step(0.8)),
+            two.move(step(1)),
+            one.end(),
+            two.end()
+        ]);
     }
 
     sequence(touches)
@@ -217,12 +189,12 @@ const ui = new (class UIController {
         }));
     }
 
-    beginStylus(options)
+    tapStylus(options)
     {
         options.azimuthAngle = options.azimuthAngle || 0;
         options.altitudeAngle = options.altitudeAngle || 0;
         options.pressure = options.pressure || 0;
-        return this._run(`uiController.stylusDownAtPoint(${options.x}, ${options.y}, ${options.azimuthAngle}, ${options.altitudeAngle}, ${options.pressure})`);
+        return this._run(`uiController.stylusTapAtPoint(${options.x}, ${options.y}, ${options.azimuthAngle}, ${options.altitudeAngle}, ${options.pressure})`);
     }
 
     _runEvents(events)