[mutter] tests/clutter/timeline-interpolate: Rework test to be less flaky



commit 6f30764320f6674814bae230e6965dc323437930
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Mon Mar 1 14:38:32 2021 +0100

    tests/clutter/timeline-interpolate: Rework test to be less flaky
    
    This more or less rewrites this test so that it explicitly tests the
    "interpolation" when a timeline loops, i.e. that if something occupies
    the thread when a timeline was supposed to have looped, we end up in the
    right place "in the middle" of the next timeline cycle.
    
    The test more or less does this:
    
     * Start a 3 second looping timeline
     * Sleep so that we're in the middle of the first cycle
     * Sleep again so that we end up in the middle of the next cycle
    
    The semantics checked are that we see the following frames:
    
     * The first frame with timestamp 0
     * The second frame in the middle of the first cycle (timestamp ~= 1.5
       sceonds)
     * The third frame in the end of the first cycle (timestamp == 3.0
       seconds)
     * The fourth frame, first in the second cycle, with timestamp ~= 1.5
       seconds)
    
    This means we can increase the "grace period" to the double (from 0.5 s
    to 1 s), while at the same time decrease the time spent running the test
    (from 10 s to 4.5 s). This should hopefully make the test less flaky,
    especially in slower runners, e.g. aarch64.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1751>

 src/tests/clutter/conform/timeline-interpolate.c | 174 ++++++++++++++---------
 1 file changed, 109 insertions(+), 65 deletions(-)
---
diff --git a/src/tests/clutter/conform/timeline-interpolate.c 
b/src/tests/clutter/conform/timeline-interpolate.c
index 6167f1e9fe..4c9d058c5f 100644
--- a/src/tests/clutter/conform/timeline-interpolate.c
+++ b/src/tests/clutter/conform/timeline-interpolate.c
@@ -5,21 +5,13 @@
 
 #include "tests/clutter-test-utils.h"
 
-/* We ask for 1 frame per millisecond.
- * Whenever this rate can't be achieved then the timeline
- * will interpolate the number frames that should have
- * passed between timeouts. */
-#define TEST_TIMELINE_FPS 1000
-#define TEST_TIMELINE_DURATION 5000
-
-/* We are at the mercy of the system scheduler so this
- * may not be a very reliable tolerance.
- *
- * It's set as very tolerable (1 ms shorter than the frame interval) as
- * otherwise CI, which are very prone to not get CPU time scheduled, tend to
- * often fail.
+#define TEST_TIMELINE_DURATION 3000
+
+/*
+ * Make the test tolarate being half a second off track in each direction,
+ * the thing we're testing for will still be tested for.
  */
-#define TEST_ERROR_TOLERANCE ((TEST_TIMELINE_FPS / 4) - 1)
+#define TEST_ERROR_TOLERANCE 500
 
 typedef struct _TestState
 {
@@ -28,7 +20,7 @@ typedef struct _TestState
   int new_frame_counter;
   int expected_frame;
   int completion_count;
-  gboolean passed;
+  int cycle_frame_counter;
 } TestState;
 
 
@@ -41,7 +33,6 @@ new_frame_cb (ClutterTimeline *timeline,
   int current_frame;
   long msec_diff;
   int loop_overflow = 0;
-  static int step = 1;
 
   current_time = g_get_real_time ();
 
@@ -61,57 +52,100 @@ new_frame_cb (ClutterTimeline *timeline,
       state->expected_frame = TEST_TIMELINE_DURATION;
     }
 
-  if (current_frame >= (state->expected_frame-TEST_ERROR_TOLERANCE)
-      && current_frame <= (state->expected_frame+TEST_ERROR_TOLERANCE))
-    {
-      g_test_message ("elapsed milliseconds=%-5li "
-                     "expected frame=%-4i actual frame=%-4i (OK)",
-                     msec_diff,
-                     state->expected_frame,
-                     current_frame);
-    }
-  else
+  switch (state->cycle_frame_counter)
     {
-      g_test_message ("elapsed milliseconds=%-5li "
-                     "expected frame=%-4i actual frame=%-4i (FAILED)",
-                     msec_diff,
-                     state->expected_frame,
-                     current_frame);
-      state->passed = FALSE;
+    case 0:
+    case 1:
+      if (current_frame >= (state->expected_frame - TEST_ERROR_TOLERANCE) &&
+          current_frame <= (state->expected_frame + TEST_ERROR_TOLERANCE))
+        {
+          g_test_message ("elapsed milliseconds=%-5li "
+                          "expected frame=%-4i actual frame=%-4i (OK)",
+                          msec_diff,
+                          state->expected_frame,
+                          current_frame);
+        }
+      else
+        {
+          g_test_message ("elapsed milliseconds=%-5li "
+                          "expected frame=%-4i actual frame=%-4i (FAILED)",
+                          msec_diff,
+                          state->expected_frame,
+                          current_frame);
+          g_test_fail ();
+        }
+      break;
+    case 2:
+      g_assert_cmpint (current_frame, ==, TEST_TIMELINE_DURATION);
+      break;
+    default:
+      g_assert_not_reached ();
     }
 
-  if (step>0)
+  /* We already tested that we interpolated when looping, lets stop now. */
+  if (state->completion_count == 1 &&
+      state->cycle_frame_counter == 0)
     {
-      state->expected_frame = current_frame + (TEST_TIMELINE_FPS / 4);
-      g_test_message ("Sleeping for 250ms "
-                     "so next frame should be (%i + %i) = %i",
-                     current_frame,
-                     (TEST_TIMELINE_FPS / 4),
-                     state->expected_frame);
-      g_usleep (250000);
-    }
-  else
-    {
-      state->expected_frame = current_frame + TEST_TIMELINE_FPS;
-      g_test_message ("Sleeping for 1sec "
-                     "so next frame should be (%i + %i) = %i",
-                     current_frame,
-                     TEST_TIMELINE_FPS,
-                     state->expected_frame);
-      g_usleep (1000000);
+      clutter_timeline_stop (timeline);
+      return;
     }
 
-  if (current_frame >= TEST_TIMELINE_DURATION)
+  switch (state->cycle_frame_counter)
     {
-      state->expected_frame += loop_overflow;
-      state->expected_frame -= TEST_TIMELINE_DURATION;
-      g_test_message ("End of timeline reached: "
-                     "Wrapping expected frame too %i",
-                     state->expected_frame);
+    case 0:
+      {
+        /*
+         * First frame, sleep so we're about in the middle of the cycle,
+         * before the end of the timeline cycle.
+         */
+        int delay_ms = ms (1500);
+
+        state->expected_frame = current_frame + delay_ms;
+        g_test_message ("Sleeping for 1.5 seconds "
+                        "so next frame should be (%d + %d) = %d",
+                        current_frame,
+                        delay_ms,
+                        state->expected_frame);
+        g_usleep (ms2us (delay_ms));
+        break;
+      }
+    case 1:
+      {
+        /*
+         * Second frame, we're about in the middle of the cycle; sleep one cycle,
+         * and check that we end up in the middle again.
+         */
+        int delay_ms = TEST_TIMELINE_DURATION;
+
+        state->expected_frame = current_frame + delay_ms;
+        g_test_message ("Sleeping for %d seconds "
+                        "so next frame should be (%d + %d) = %d, "
+                        "which is %d into the next cycle",
+                        TEST_TIMELINE_DURATION / 1000,
+                        current_frame,
+                        delay_ms,
+                        state->expected_frame,
+                        state->expected_frame - TEST_TIMELINE_DURATION);
+        g_usleep (ms2us (delay_ms));
+
+        g_assert_cmpint (state->expected_frame, >, TEST_TIMELINE_DURATION);
+
+        state->expected_frame += loop_overflow;
+        state->expected_frame -= TEST_TIMELINE_DURATION;
+        g_test_message ("End of timeline reached: "
+                        "Wrapping expected frame too %d",
+                        state->expected_frame);
+        break;
+      }
+    case 2:
+    case 3:
+      {
+        break;
+      }
     }
 
   state->new_frame_counter++;
-  step = -step;
+  state->cycle_frame_counter++;
 }
 
 static void
@@ -119,14 +153,20 @@ completed_cb (ClutterTimeline *timeline,
               TestState       *state)
 {
   state->completion_count++;
+  state->cycle_frame_counter = 0;
 
-  if (state->completion_count == 2)
-    {
-      if (state->passed)
-        clutter_test_quit ();
-      else
-        g_assert_not_reached ();
-    }
+  if (state->completion_count >= 2)
+    g_assert_not_reached ();
+}
+
+static void
+stopped_cb (ClutterTimeline *timeline,
+            gboolean         is_finished,
+            TestState       *state)
+{
+  g_assert_cmpint (state->completion_count, ==, 1);
+
+  clutter_test_quit ();
 }
 
 static void
@@ -148,10 +188,14 @@ timeline_interpolation (void)
                     "completed",
                     G_CALLBACK (completed_cb),
                     &state);
+  g_signal_connect (state.timeline,
+                    "stopped",
+                    G_CALLBACK (stopped_cb),
+                    &state);
 
   state.completion_count = 0;
   state.new_frame_counter = 0;
-  state.passed = TRUE;
+  state.cycle_frame_counter = 0;
   state.expected_frame = 0;
 
   clutter_actor_show (stage);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]