From 6f30764320f6674814bae230e6965dc323437930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 1 Mar 2021 14:38:32 +0100 Subject: [PATCH] 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: --- .../clutter/conform/timeline-interpolate.c | 172 +++++++++++------- 1 file changed, 108 insertions(+), 64 deletions(-) diff --git a/src/tests/clutter/conform/timeline-interpolate.c b/src/tests/clutter/conform/timeline-interpolate.c index 6167f1e9f..4c9d058c5 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 +#define TEST_TIMELINE_DURATION 3000 -/* 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. +/* + * 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)) + switch (state->cycle_frame_counter) { - 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); - 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);