Bug 1293614 - Ensure that Wait().until() aligns as best as possible to the original interval sequence. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 09 Aug 2016 16:52:39 +0200
changeset 399664 62f860e704d86bbd5251b4cb81a3283c25a81ac0
parent 398604 6cf0089510fad8deb866136f5b92bbced9498447
child 528009 75d5098fd134b61e7c36b16834111d0cf16a370c
push id25922
push userbmo:hskupin@gmail.com
push dateThu, 11 Aug 2016 20:02:42 +0000
bugs1293614
milestone51.0a1
Bug 1293614 - Ensure that Wait().until() aligns as best as possible to the original interval sequence. MozReview-Commit-ID: 2JEQtLEsdux
testing/marionette/client/marionette_driver/wait.py
testing/marionette/harness/marionette/tests/unit/test_wait.py
--- a/testing/marionette/client/marionette_driver/wait.py
+++ b/testing/marionette/client/marionette_driver/wait.py
@@ -49,18 +49,20 @@ class Wait(object):
 
         :param timeout: How long to wait for the evaluated condition
             to become true.  The default timeout is the `timeout`
             property on the `Marionette` object if set, or
             `wait.DEFAULT_TIMEOUT`.
 
         :param interval: How often the condition should be evaluated.
             In reality the interval may be greater as the cost of
-            evaluating the condition function is not factored in.  The
-            default polling interval is `wait.DEFAULT_INTERVAL`.
+            evaluating the condition function. If that is not the case the
+            interval for the next condition function call is shortend to keep
+            the original interval sequence as best as possible.
+            The default polling interval is `wait.DEFAULT_INTERVAL`.
 
         :param ignored_exceptions: Ignore specific types of exceptions
             whilst waiting for the condition.  Any exceptions not
             whitelisted will be allowed to propagate, terminating the
             wait.
 
         :param clock: Allows overriding the use of the runtime's
             default time library.  See `wait.SystemClock` for
@@ -115,30 +117,35 @@ class Wait(object):
 
         rv = None
         last_exc = None
         until = is_true or until_pred
         start = self.clock.now
 
         while not until(self.clock, self.end):
             try:
+                next = self.clock.now + self.interval
                 rv = condition(self.marionette)
             except (KeyboardInterrupt, SystemExit):
                 raise
             except self.exceptions:
                 last_exc = sys.exc_info()
 
+            # Re-adjust the interval depending on how long the callback
+            # took to evaluate the condition
+            interval_new = max(next - self.clock.now, 0)
+
             if not rv:
-                self.clock.sleep(self.interval)
+                self.clock.sleep(interval_new)
                 continue
 
             if rv is not None:
                 return rv
 
-            self.clock.sleep(self.interval)
+            self.clock.sleep(interval_new)
 
         if message:
             message = " with message: %s" % message
 
         raise errors.TimeoutException(
             "Timed out after %s seconds%s" %
             (round((self.clock.now - start), 1), message if message else ""),
             cause=last_exc)
--- a/testing/marionette/harness/marionette/tests/unit/test_wait.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_wait.py
@@ -12,18 +12,19 @@ from marionette_driver.wait import Wait
 from marionette import MarionetteTestCase
 
 
 class TickingClock(object):
     def __init__(self, incr=1):
         self.ticks = 0
         self.increment = incr
 
-    def sleep(self, dur):
-        self.ticks += self.increment
+    def sleep(self, dur=None):
+        dur = dur if dur is not None else self.increment
+        self.ticks += dur
 
     @property
     def now(self):
         return self.ticks
 
 class SequenceClock(object):
     def __init__(self, times):
         self.times = times
@@ -261,16 +262,50 @@ class WaitUntilTest(MarionetteTestCase):
             self.wt.until(lambda x: x.true(wait=4), is_true=at_third_attempt)
 
     def test_timeout_elapsed_rounding(self):
         wt = Wait(self.m, clock=SequenceClock([1, 0.01, 1]), timeout=0)
         with self.assertRaisesRegexp(errors.TimeoutException,
                                      "Timed out after 1.0 seconds"):
             wt.until(lambda x: x.true(), is_true=now)
 
+    def test_timeout_elapsed_interval_by_delayed_condition_return(self):
+        def callback(mn):
+            self.clock.sleep(11)
+            return mn.false()
+
+        with self.assertRaisesRegexp(errors.TimeoutException,
+                                     "Timed out after 11.0 seconds"):
+            self.wt.until(callback)
+        # With a delayed conditional return > timeout, only 1 iteration is possible
+        self.assertEqual(self.m.waited, 1)
+
+    def test_timeout_with_delayed_condition_return(self):
+        def callback(mn):
+            self.clock.sleep(.5)
+            return mn.false()
+
+        with self.assertRaisesRegexp(errors.TimeoutException,
+                                     "Timed out after 10.0 seconds"):
+            self.wt.until(callback)
+        # With a delayed conditional return < interval, 10 iterations should be possible
+        self.assertEqual(self.m.waited, 10)
+
+    def test_timeout_interval_shorter_than_delayed_condition_return(self):
+        def callback(mn):
+            self.clock.sleep(2)
+            return mn.false()
+
+        with self.assertRaisesRegexp(errors.TimeoutException,
+                                     "Timed out after 10.0 seconds"):
+            self.wt.until(callback)
+        # With a delayed return of the conditional which takes twice that long than the interval,
+        # half of the iterations should be possible
+        self.assertEqual(self.m.waited, 5)
+
     def test_message(self):
         self.wt.exceptions = (TypeError,)
         exc = None
         try:
             self.wt.until(lambda x: x.exception(e=TypeError), message="hooba")
         except errors.TimeoutException as e:
             exc = e