1
0
Fork 0

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>
This commit is contained in:
Jonas Ådahl 2021-03-01 14:38:32 +01:00 committed by Marge Bot
parent 0d3840b056
commit 6f30764320

View file

@ -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);