mutter/src/tests/stage-view-tests.c
Jonas Ådahl 08b0e563d4 clutter: Pass 'ClutterFrame' in all stage update signals
That means before-update, prepare-paint, before-paint, paint-view, after-paint,
after-update. While yet to be used, it will be used as a transient frame
book keeping object, to maintain object and state that is only valid
during a frame dispatch.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2795>
2023-01-23 15:57:50 +01:00

1376 lines
47 KiB
C

/*
* Copyright (C) 2020 Jonas Dreßler
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "clutter/clutter.h"
#include "clutter/clutter-stage-view-private.h"
#include "compositor/meta-window-actor-private.h"
#include "meta-test/meta-context-test.h"
#include "meta/meta-window-actor.h"
#include "tests/meta-backend-test.h"
#include "tests/meta-monitor-test-utils.h"
#include "tests/meta-test-utils.h"
#include "x11/meta-x11-display-private.h"
#define X11_TEST_CLIENT_NAME "x11_test_client"
#define X11_TEST_CLIENT_WINDOW "window1"
static MetaContext *test_context;
static MetaBackend *test_backend;
static MonitorTestCaseSetup initial_test_case_setup = {
.modes = {
{
.width = 1024,
.height = 768,
.refresh_rate = 60.0
}
},
.n_modes = 1,
.outputs = {
{
.crtc = 0,
.modes = { 0 },
.n_modes = 1,
.preferred_mode = 0,
.possible_crtcs = { 0 },
.n_possible_crtcs = 1,
.width_mm = 222,
.height_mm = 125
},
{
.crtc = 1,
.modes = { 0 },
.n_modes = 1,
.preferred_mode = 0,
.possible_crtcs = { 1 },
.n_possible_crtcs = 1,
.width_mm = 220,
.height_mm = 124
}
},
.n_outputs = 2,
.crtcs = {
{
.current_mode = 0
},
{
.current_mode = 0
}
},
.n_crtcs = 2
};
static void
meta_test_stage_views_exist (void)
{
MetaBackend *backend = test_backend;
ClutterActor *stage;
GList *stage_views;
stage = meta_backend_get_stage (backend);
g_assert_cmpint (clutter_actor_get_width (stage), ==, 1024 * 2);
g_assert_cmpint (clutter_actor_get_height (stage), ==, 768);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 2);
}
static void
on_after_paint (ClutterStage *stage,
ClutterStageView *view,
ClutterFrame *frame,
gboolean *was_painted)
{
*was_painted = TRUE;
}
static void
wait_for_paint (ClutterActor *stage)
{
gboolean was_painted = FALSE;
gulong was_painted_id;
was_painted_id = g_signal_connect (CLUTTER_STAGE (stage),
"after-paint",
G_CALLBACK (on_after_paint),
&was_painted);
while (!was_painted)
g_main_context_iteration (NULL, TRUE);
g_signal_handler_disconnect (stage, was_painted_id);
}
static void
wait_for_window_map (ClutterActor *stage,
ClutterActor *window_actor)
{
while (!clutter_actor_is_mapped (window_actor))
wait_for_paint (stage);
}
static void
on_stage_views_changed (ClutterActor *actor,
gboolean *stage_views_changed)
{
*stage_views_changed = TRUE;
}
static void
is_on_stage_views (ClutterActor *actor,
unsigned int n_views,
...)
{
va_list valist;
int i = 0;
GList *stage_views = clutter_actor_peek_stage_views (actor);
va_start (valist, n_views);
for (i = 0; i < n_views; i++)
{
ClutterStageView *view = va_arg (valist, ClutterStageView*);
g_assert_nonnull (g_list_find (stage_views, view));
}
va_end (valist);
g_assert (g_list_length (stage_views) == n_views);
}
static void
meta_test_actor_stage_views (void)
{
MetaBackend *backend = test_backend;
ClutterActor *stage, *container, *test_actor;
GList *stage_views;
gboolean stage_views_changed_container = FALSE;
gboolean stage_views_changed_test_actor = FALSE;
gboolean *stage_views_changed_container_ptr =
&stage_views_changed_container;
gboolean *stage_views_changed_test_actor_ptr =
&stage_views_changed_test_actor;
stage = meta_backend_get_stage (backend);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
container = clutter_actor_new ();
clutter_actor_set_size (container, 100, 100);
clutter_actor_add_child (stage, container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 50, 50);
clutter_actor_add_child (container, test_actor);
g_signal_connect (container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_container_ptr);
g_signal_connect (test_actor, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_test_actor_ptr);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (container, 1, stage_views->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for the initial change */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Move the container to the second stage view */
clutter_actor_set_x (container, 1040);
wait_for_paint (stage);
is_on_stage_views (container, 1, stage_views->next->data);
is_on_stage_views (test_actor, 1, stage_views->next->data);
/* The signal was emitted again */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Move the container so it's on both stage views while the test_actor
* is only on the first one.
*/
clutter_actor_set_x (container, 940);
wait_for_paint (stage);
is_on_stage_views (container, 2, stage_views->data, stage_views->next->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted again */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
g_signal_handlers_disconnect_by_func (container, on_stage_views_changed,
stage_views_changed_container_ptr);
g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed,
stage_views_changed_test_actor_ptr);
clutter_actor_destroy (container);
}
static void
on_relayout_actor_frame (ClutterTimeline *timeline,
int msec,
ClutterActor *actor)
{
MetaBackend *backend = test_backend;
ClutterActor *stage = meta_backend_get_stage (backend);
clutter_stage_clear_stage_views (CLUTTER_STAGE (stage));
}
static void
meta_test_actor_stage_views_relayout (void)
{
MetaBackend *backend = test_backend;
ClutterActor *stage, *actor;
ClutterTransition *transition;
GMainLoop *main_loop;
stage = meta_backend_get_stage (backend);
actor = clutter_actor_new ();
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_easing_duration (actor, 100);
clutter_actor_add_child (stage, actor);
clutter_actor_show (stage);
wait_for_paint (stage);
clutter_actor_set_position (actor, 1000.0, 0.0);
transition = clutter_actor_get_transition (actor, "position");
g_signal_connect_after (transition, "new-frame",
G_CALLBACK (on_relayout_actor_frame),
actor);
main_loop = g_main_loop_new (NULL, FALSE);
g_signal_connect_swapped (transition, "stopped",
G_CALLBACK (g_main_loop_quit),
main_loop);
g_main_loop_run (main_loop);
clutter_actor_destroy (actor);
g_main_loop_unref (main_loop);
}
static void
meta_test_actor_stage_views_reparent (void)
{
MetaBackend *backend = test_backend;
ClutterActor *stage, *container, *test_actor;
GList *stage_views;
gboolean stage_views_changed_container = FALSE;
gboolean stage_views_changed_test_actor = FALSE;
gboolean *stage_views_changed_container_ptr =
&stage_views_changed_container;
gboolean *stage_views_changed_test_actor_ptr =
&stage_views_changed_test_actor;
stage = meta_backend_get_stage (backend);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
container = clutter_actor_new ();
clutter_actor_set_size (container, 100, 100);
clutter_actor_set_x (container, 1020);
clutter_actor_add_child (stage, container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 20, 20);
clutter_actor_add_child (container, test_actor);
g_signal_connect (container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_container_ptr);
g_signal_connect (test_actor, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_test_actor_ptr);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (container, 2, stage_views->data, stage_views->next->data);
is_on_stage_views (test_actor, 2, stage_views->data, stage_views->next->data);
/* The signal was emitted for both actors */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Remove the test_actor from the scene-graph */
g_object_ref (test_actor);
clutter_actor_remove_child (container, test_actor);
/* While the test_actor is not on stage, it must be on no stage views */
is_on_stage_views (test_actor, 0);
/* When the test_actor left the stage, the signal was emitted */
g_assert (!stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_test_actor = FALSE;
/* Add the test_actor again as a child of the stage */
clutter_actor_add_child (stage, test_actor);
g_object_unref (test_actor);
wait_for_paint (stage);
/* The container is still on both stage views... */
is_on_stage_views (container, 2, stage_views->data, stage_views->next->data);
/* ...while the test_actor is only on the first one now */
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for the test_actor again */
g_assert (!stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_test_actor = FALSE;
/* Move the container out of the stage... */
clutter_actor_set_y (container, 2000);
g_object_ref (test_actor);
clutter_actor_remove_child (stage, test_actor);
/* When the test_actor left the stage, the signal was emitted */
g_assert (!stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_test_actor = FALSE;
/* ...and reparent the test_actor to the container again */
clutter_actor_add_child (container, test_actor);
g_object_unref (test_actor);
wait_for_paint (stage);
/* Now both actors are on no stage views */
is_on_stage_views (container, 0);
is_on_stage_views (test_actor, 0);
/* The signal was emitted only for the container, the test_actor already
* has no stage-views.
*/
g_assert (stage_views_changed_container);
g_assert (!stage_views_changed_test_actor);
g_signal_handlers_disconnect_by_func (container, on_stage_views_changed,
stage_views_changed_container_ptr);
g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed,
stage_views_changed_test_actor_ptr);
clutter_actor_destroy (container);
}
static void
meta_test_actor_stage_views_hide_parent (void)
{
MetaBackend *backend = test_backend;
ClutterActor *stage, *outer_container, *inner_container, *test_actor;
GList *stage_views;
gboolean stage_views_changed_outer_container = FALSE;
gboolean stage_views_changed_inner_container = FALSE;
gboolean stage_views_changed_test_actor = FALSE;
gboolean *stage_views_changed_outer_container_ptr =
&stage_views_changed_outer_container;
gboolean *stage_views_changed_inner_container_ptr =
&stage_views_changed_inner_container;
gboolean *stage_views_changed_test_actor_ptr =
&stage_views_changed_test_actor;
stage = meta_backend_get_stage (backend);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
outer_container = clutter_actor_new ();
clutter_actor_set_size (outer_container, 50, 50);
clutter_actor_add_child (stage, outer_container);
inner_container = clutter_actor_new ();
clutter_actor_add_child (outer_container, inner_container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 20, 20);
clutter_actor_add_child (inner_container, test_actor);
g_signal_connect (outer_container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_outer_container_ptr);
g_signal_connect (inner_container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_inner_container_ptr);
g_signal_connect (test_actor, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_test_actor_ptr);
clutter_actor_show (stage);
wait_for_paint (stage);
/* The containers and the test_actor are on all on the first view */
is_on_stage_views (outer_container, 1, stage_views->data);
is_on_stage_views (inner_container, 1, stage_views->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for all three */
g_assert (stage_views_changed_outer_container);
g_assert (stage_views_changed_inner_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_outer_container = FALSE;
stage_views_changed_inner_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Hide the inner_container */
clutter_actor_hide (inner_container);
/* Move the outer_container so it's still on the first view */
clutter_actor_set_x (outer_container, 1023);
wait_for_paint (stage);
/* The outer_container is still expanded so it should be on both views */
is_on_stage_views (outer_container, 2,
stage_views->data, stage_views->next->data);
/* The inner_container and test_actor aren't updated because they're hidden */
is_on_stage_views (inner_container, 1, stage_views->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for the outer_container */
g_assert (stage_views_changed_outer_container);
g_assert (!stage_views_changed_inner_container);
g_assert (!stage_views_changed_test_actor);
stage_views_changed_outer_container = FALSE;
/* Show the inner_container again */
clutter_actor_show (inner_container);
wait_for_paint (stage);
/* All actors are on both views now */
is_on_stage_views (outer_container, 2,
stage_views->data, stage_views->next->data);
is_on_stage_views (inner_container, 2,
stage_views->data, stage_views->next->data);
is_on_stage_views (test_actor, 2,
stage_views->data, stage_views->next->data);
/* The signal was emitted for the inner_container and test_actor */
g_assert (!stage_views_changed_outer_container);
g_assert (stage_views_changed_inner_container);
g_assert (stage_views_changed_test_actor);
g_signal_handlers_disconnect_by_func (outer_container, on_stage_views_changed,
stage_views_changed_outer_container_ptr);
g_signal_handlers_disconnect_by_func (inner_container, on_stage_views_changed,
stage_views_changed_inner_container_ptr);
g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed,
stage_views_changed_test_actor_ptr);
clutter_actor_destroy (outer_container);
}
static MetaMonitorTestSetup *
create_stage_view_test_setup (MetaBackend *backend)
{
return meta_create_monitor_test_setup (backend,
&initial_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
}
static void
assert_is_stage_view (ClutterStageView *stage_view,
int x,
int y,
int width,
int height)
{
cairo_rectangle_int_t layout;
g_assert_nonnull (stage_view);
g_assert_true (CLUTTER_IS_STAGE_VIEW (stage_view));
clutter_stage_view_get_layout (stage_view, &layout);
g_assert_cmpint (layout.x, ==, x);
g_assert_cmpint (layout.y, ==, y);
g_assert_cmpint (layout.width, ==, width);
g_assert_cmpint (layout.height, ==, height);
}
static void
meta_test_actor_stage_views_hot_plug (void)
{
MetaBackend *backend = test_backend;
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
ClutterActor *stage = meta_backend_get_stage (backend);
ClutterActor *actor_1;
ClutterActor *actor_2;
GList *stage_views;
GList *prev_stage_views;
MonitorTestCaseSetup hotplug_test_case_setup = initial_test_case_setup;
MetaMonitorTestSetup *test_setup;
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 2);
assert_is_stage_view (stage_views->data, 0, 0, 1024, 768);
assert_is_stage_view (stage_views->next->data, 1024, 0, 1024, 768);
actor_1 = clutter_actor_new ();
clutter_actor_set_size (actor_1, 100, 100);
clutter_actor_set_position (actor_1, 100, 100);
clutter_actor_add_child (stage, actor_1);
actor_2 = clutter_actor_new ();
clutter_actor_set_size (actor_2, 100, 100);
clutter_actor_set_position (actor_2, 1100, 100);
clutter_actor_add_child (stage, actor_2);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (actor_1, 1, stage_views->data);
is_on_stage_views (actor_2, 1, stage_views->next->data);
prev_stage_views = g_list_copy_deep (stage_views,
(GCopyFunc) g_object_ref, NULL);
test_setup = meta_create_monitor_test_setup (test_backend,
&hotplug_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert (stage_views != prev_stage_views);
g_assert_cmpint (g_list_length (stage_views), ==, 2);
g_assert (prev_stage_views->data != stage_views->data);
g_assert (prev_stage_views->next->data != stage_views->next->data);
assert_is_stage_view (stage_views->data, 0, 0, 1024, 768);
assert_is_stage_view (stage_views->next->data, 1024, 0, 1024, 768);
g_list_free_full (prev_stage_views, (GDestroyNotify) g_object_unref);
is_on_stage_views (actor_1, 0);
is_on_stage_views (actor_2, 0);
wait_for_paint (stage);
is_on_stage_views (actor_1, 1, stage_views->data);
is_on_stage_views (actor_2, 1, stage_views->next->data);
clutter_actor_destroy (actor_1);
clutter_actor_destroy (actor_2);
}
static void
meta_test_actor_stage_views_frame_clock (void)
{
MetaBackend *backend = test_backend;
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
ClutterActor *stage = meta_backend_get_stage (backend);
ClutterActor *actor_1;
ClutterActor *actor_2;
ClutterActor *actor_3;
GList *stage_views;
MonitorTestCaseSetup frame_clock_test_setup = initial_test_case_setup;
MetaMonitorTestSetup *test_setup;
ClutterFrameClock *frame_clock;
frame_clock_test_setup.modes[1].width = 1024;
frame_clock_test_setup.modes[1].height = 768;
frame_clock_test_setup.modes[1].refresh_rate = 30.0;
frame_clock_test_setup.n_modes = 2;
frame_clock_test_setup.outputs[1].modes[0] = 1;
frame_clock_test_setup.outputs[1].preferred_mode = 1;
test_setup = meta_create_monitor_test_setup (test_backend,
&frame_clock_test_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpfloat (clutter_stage_view_get_refresh_rate (stage_views->data),
==,
60.0);
g_assert_cmpfloat (clutter_stage_view_get_refresh_rate (stage_views->next->data),
==,
30.0);
actor_1 = clutter_actor_new ();
clutter_actor_set_size (actor_1, 100, 100);
clutter_actor_set_position (actor_1, 100, 100);
clutter_actor_add_child (stage, actor_1);
actor_2 = clutter_actor_new ();
clutter_actor_set_size (actor_2, 100, 100);
clutter_actor_set_position (actor_2, 1100, 100);
clutter_actor_add_child (stage, actor_2);
actor_3 = clutter_actor_new ();
clutter_actor_set_size (actor_3, 100, 100);
clutter_actor_set_position (actor_3, 1000, 400);
clutter_actor_add_child (stage, actor_3);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (actor_1, 1, stage_views->data);
is_on_stage_views (actor_2, 1, stage_views->next->data);
is_on_stage_views (actor_3, 2,
stage_views->data,
stage_views->next->data);
frame_clock = clutter_actor_pick_frame_clock (actor_1, NULL);
g_assert_cmpfloat (clutter_frame_clock_get_refresh_rate (frame_clock),
==,
60.0);
frame_clock = clutter_actor_pick_frame_clock (actor_2, NULL);
g_assert_cmpfloat (clutter_frame_clock_get_refresh_rate (frame_clock),
==,
30.0);
frame_clock = clutter_actor_pick_frame_clock (actor_3, NULL);
g_assert_cmpfloat (clutter_frame_clock_get_refresh_rate (frame_clock),
==,
60.0);
clutter_actor_destroy (actor_1);
clutter_actor_destroy (actor_2);
clutter_actor_destroy (actor_3);
}
typedef struct _TimelineTest
{
GMainLoop *main_loop;
ClutterFrameClock *frame_clock_1;
ClutterFrameClock *frame_clock_2;
int phase;
int frame_counter[2];
} TimelineTest;
static void
on_transition_stopped (ClutterTransition *transition,
gboolean is_finished,
TimelineTest *test)
{
g_assert_true (is_finished);
g_assert_cmpint (test->phase, ==, 2);
test->phase = 3;
g_main_loop_quit (test->main_loop);
}
static void
on_transition_new_frame (ClutterTransition *transition,
int elapsed_time_ms,
TimelineTest *test)
{
ClutterTimeline *timeline = CLUTTER_TIMELINE (transition);
if (test->phase == 1)
{
g_assert (clutter_timeline_get_frame_clock (timeline) ==
test->frame_clock_1);
test->frame_counter[0]++;
}
else if (test->phase == 2)
{
g_assert (clutter_timeline_get_frame_clock (timeline) ==
test->frame_clock_2);
test->frame_counter[1]++;
}
else
{
g_assert_not_reached ();
}
}
static void
on_transition_frame_clock_changed (ClutterTimeline *timeline,
GParamSpec *pspec,
TimelineTest *test)
{
ClutterFrameClock *frame_clock;
frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert (frame_clock == test->frame_clock_2);
g_assert_cmpint (test->phase, ==, 1);
test->phase = 2;
}
static void
meta_test_actor_stage_views_timeline (void)
{
TimelineTest test = { 0 };
MetaBackend *backend = test_backend;
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
ClutterActor *stage = meta_backend_get_stage (backend);
MonitorTestCaseSetup frame_clock_test_setup;
ClutterActor *actor;
GList *stage_views;
ClutterStageView *stage_view_1;
ClutterStageView *stage_view_2;
MetaMonitorTestSetup *test_setup;
ClutterTransition *transition;
frame_clock_test_setup = initial_test_case_setup;
frame_clock_test_setup.modes[1].width = 1024;
frame_clock_test_setup.modes[1].height = 768;
frame_clock_test_setup.modes[1].refresh_rate = 30.0;
frame_clock_test_setup.n_modes = 2;
frame_clock_test_setup.outputs[1].modes[0] = 1;
frame_clock_test_setup.outputs[1].preferred_mode = 1;
test_setup = meta_create_monitor_test_setup (test_backend,
&frame_clock_test_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
stage_view_1 = stage_views->data;
stage_view_2 = stage_views->next->data;
g_assert_nonnull (stage_view_1);
g_assert_nonnull (stage_view_2);
test.frame_clock_1 = clutter_stage_view_get_frame_clock (stage_view_1);
test.frame_clock_2 = clutter_stage_view_get_frame_clock (stage_view_2);
g_assert_nonnull (test.frame_clock_1);
g_assert_nonnull (test.frame_clock_2);
actor = clutter_actor_new ();
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 800, 100);
clutter_actor_add_child (stage, actor);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (actor, 1, stage_views->data);
clutter_actor_set_easing_duration (actor, 1000);
clutter_actor_set_position (actor, 1200, 300);
transition = clutter_actor_get_transition (actor, "position");
g_assert_nonnull (transition);
g_assert (clutter_timeline_get_frame_clock (CLUTTER_TIMELINE (transition)) ==
test.frame_clock_1);
test.main_loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (transition, "stopped",
G_CALLBACK (on_transition_stopped),
&test);
g_signal_connect (transition, "new-frame",
G_CALLBACK (on_transition_new_frame),
&test);
g_signal_connect (transition, "notify::frame-clock",
G_CALLBACK (on_transition_frame_clock_changed),
&test);
test.phase = 1;
g_main_loop_run (test.main_loop);
g_assert_cmpint (test.phase, ==, 3);
g_assert_cmpint (test.frame_counter[0], >, 0);
g_assert_cmpint (test.frame_counter[1], >, 0);
clutter_actor_destroy (actor);
g_main_loop_unref (test.main_loop);
}
static void
meta_test_actor_stage_views_parent_views_rebuilt (void)
{
MetaBackend *backend = test_backend;
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
MonitorTestCaseSetup frame_clock_test_setup;
MetaMonitorTestSetup *test_setup;
ClutterActor *stage, *container, *test_actor;
GList *stage_views;
ClutterTimeline *timeline;
ClutterFrameClock *timeline_frame_clock;
ClutterFrameClock *view_frame_clock;
ClutterStageView *old_stage_view;
ClutterFrameClock *old_frame_clock;
stage = meta_backend_get_stage (backend);
frame_clock_test_setup = initial_test_case_setup;
frame_clock_test_setup.n_outputs = 1;
frame_clock_test_setup.n_crtcs = 1;
test_setup = meta_create_monitor_test_setup (test_backend,
&frame_clock_test_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 1);
container = clutter_actor_new ();
clutter_actor_set_size (container, 100, 100);
clutter_actor_set_position (container, 0, 0);
clutter_actor_add_child (stage, container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 0, 0);
clutter_actor_add_child (container, test_actor);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (test_actor, 0);
is_on_stage_views (container, 1, stage_views->data);
is_on_stage_views (stage, 1, stage_views->data);
timeline = clutter_timeline_new_for_actor (test_actor, 100);
clutter_timeline_start (timeline);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
view_frame_clock = clutter_stage_view_get_frame_clock (stage_views->data);
g_assert_nonnull (timeline_frame_clock);
g_assert_nonnull (view_frame_clock);
g_assert (timeline_frame_clock == view_frame_clock);
/* Keep the stage view alive so it can be used to compare with later. */
old_stage_view = g_object_ref (stage_views->data);
old_frame_clock =
g_object_ref (clutter_stage_view_get_frame_clock (old_stage_view));
test_setup = meta_create_monitor_test_setup (test_backend,
&frame_clock_test_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
wait_for_paint (stage);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 1);
g_assert (stage_views->data != old_stage_view);
view_frame_clock = clutter_stage_view_get_frame_clock (stage_views->data);
g_assert_nonnull (view_frame_clock);
g_assert (view_frame_clock != old_frame_clock);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert_nonnull (timeline_frame_clock);
g_assert (timeline_frame_clock == view_frame_clock);
g_object_unref (old_stage_view);
g_object_unref (old_frame_clock);
clutter_actor_destroy (test_actor);
clutter_actor_destroy (container);
}
static void
meta_test_actor_stage_views_parent_views_changed (void)
{
MetaBackend *backend = test_backend;
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
MonitorTestCaseSetup frame_clock_test_setup;
MetaMonitorTestSetup *test_setup;
ClutterActor *stage, *container, *test_actor;
GList *stage_views;
ClutterTimeline *timeline;
ClutterFrameClock *timeline_frame_clock;
ClutterFrameClock *first_view_frame_clock;
ClutterFrameClock *second_view_frame_clock;
stage = meta_backend_get_stage (backend);
frame_clock_test_setup = initial_test_case_setup;
test_setup = meta_create_monitor_test_setup (test_backend,
&frame_clock_test_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 2);
container = clutter_actor_new ();
clutter_actor_set_size (container, 100, 100);
clutter_actor_set_position (container, 0, 0);
clutter_actor_add_child (stage, container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 0, 0);
clutter_actor_add_child (container, test_actor);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 2);
clutter_actor_show (stage);
wait_for_paint (stage);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 2);
is_on_stage_views (test_actor, 0);
is_on_stage_views (container, 1, stage_views->data);
is_on_stage_views (stage, 2,
stage_views->data,
stage_views->next->data);
timeline = clutter_timeline_new_for_actor (test_actor, 100);
clutter_timeline_start (timeline);
first_view_frame_clock =
clutter_stage_view_get_frame_clock (stage_views->data);
second_view_frame_clock =
clutter_stage_view_get_frame_clock (stage_views->next->data);
g_assert_nonnull (first_view_frame_clock);
g_assert_nonnull (second_view_frame_clock);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert_nonnull (timeline_frame_clock);
g_assert (timeline_frame_clock == first_view_frame_clock);
clutter_actor_set_x (container, 1200);
wait_for_paint (stage);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert_nonnull (timeline_frame_clock);
g_assert (timeline_frame_clock == second_view_frame_clock);
clutter_actor_destroy (test_actor);
clutter_actor_destroy (container);
}
static void
meta_test_actor_stage_views_and_frame_clocks_freed (void)
{
MetaBackend *backend = test_backend;
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
ClutterActor *stage = meta_backend_get_stage (backend);
ClutterActor *actor_1;
ClutterActor *actor_2;
GList *stage_views;
ClutterStageView *first_view;
ClutterStageView *second_view;
ClutterTimeline *timeline;
ClutterFrameClock *timeline_frame_clock;
ClutterFrameClock *first_view_frame_clock;
ClutterFrameClock *second_view_frame_clock;
MonitorTestCaseSetup frame_clock_test_setup;
MetaMonitorTestSetup *test_setup;
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
first_view = stage_views->data;
second_view = stage_views->next->data;
g_object_add_weak_pointer (G_OBJECT (first_view), (gpointer *) &first_view);
g_object_add_weak_pointer (G_OBJECT (second_view), (gpointer *) &second_view);
/* Create two actors, one on the first stage view, another one on the
* second view.
*/
actor_1 = clutter_actor_new ();
clutter_actor_set_size (actor_1, 100, 100);
clutter_actor_set_position (actor_1, 100, 100);
clutter_actor_add_child (stage, actor_1);
actor_2 = clutter_actor_new ();
clutter_actor_set_size (actor_2, 100, 100);
clutter_actor_set_position (actor_2, 1100, 100);
clutter_actor_add_child (stage, actor_2);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (actor_1, 1, first_view);
is_on_stage_views (actor_2, 1, second_view);
/* Now create a timeline for the first actor and make sure its using the
* frame clock of the first view.
*/
timeline = clutter_timeline_new_for_actor (actor_1, 100);
clutter_timeline_start (timeline);
first_view_frame_clock =
clutter_stage_view_get_frame_clock (first_view);
second_view_frame_clock =
clutter_stage_view_get_frame_clock (second_view);
g_assert_nonnull (first_view_frame_clock);
g_assert_nonnull (second_view_frame_clock);
g_object_add_weak_pointer (G_OBJECT (first_view_frame_clock),
(gpointer *) &first_view_frame_clock);
g_object_add_weak_pointer (G_OBJECT (second_view_frame_clock),
(gpointer *) &second_view_frame_clock);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert_nonnull (timeline_frame_clock);
g_assert (timeline_frame_clock == first_view_frame_clock);
/* Now set the timeline actor to actor_2 and make sure the timeline is
* using the second frame clock.
*/
clutter_timeline_set_actor (timeline, actor_2);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert_nonnull (timeline_frame_clock);
g_assert (timeline_frame_clock == second_view_frame_clock);
/* Trigger a hotplug and remove both monitors, after that the timeline
* should have no frame clock set and both stage views and their
* frame clocks should have been freed.
*/
frame_clock_test_setup = initial_test_case_setup;
frame_clock_test_setup.n_outputs = 0;
frame_clock_test_setup.n_crtcs = 0;
test_setup = meta_create_monitor_test_setup (test_backend,
&frame_clock_test_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
timeline_frame_clock = clutter_timeline_get_frame_clock (timeline);
g_assert_null (timeline_frame_clock);
g_assert_null (first_view);
g_assert_null (first_view_frame_clock);
g_assert_null (second_view);
g_assert_null (second_view_frame_clock);
clutter_actor_destroy (actor_1);
clutter_actor_destroy (actor_2);
}
static void
ensure_view_count (int n_views)
{
MetaBackend *backend = test_backend;
ClutterActor *stage = meta_backend_get_stage (backend);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
MonitorTestCaseSetup test_case_setup;
MetaMonitorTestSetup *test_setup;
GList *stage_views;
test_case_setup = initial_test_case_setup;
test_case_setup.n_outputs = n_views;
test_case_setup.n_crtcs = n_views;
test_setup = meta_create_monitor_test_setup (test_backend,
&test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpuint (g_list_length (stage_views), ==, n_views);
}
static void
check_test_client_state (MetaTestClient *test_client)
{
GError *error = NULL;
if (!meta_test_client_wait (test_client, &error))
{
g_error ("Failed to sync test client '%s': %s",
meta_test_client_get_id (test_client), error->message);
}
}
static void
meta_test_actor_stage_views_queue_frame_drawn (void)
{
MetaBackend *backend = meta_context_get_backend (test_context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerTest *monitor_manager_test =
META_MONITOR_MANAGER_TEST (monitor_manager);
ClutterActor *stage = meta_backend_get_stage (backend);
MetaTestClient *x11_test_client;
MonitorTestCaseSetup hotplug_test_case_setup = initial_test_case_setup;
MetaMonitorTestSetup *test_setup;
GError *error = NULL;
MetaWindow *test_window;
ClutterActor *window_actor;
x11_test_client = meta_test_client_new (test_context,
X11_TEST_CLIENT_NAME,
META_WINDOW_CLIENT_TYPE_X11,
&error);
if (!x11_test_client)
g_error ("Failed to launch X11 test client: %s", error->message);
if (!meta_test_client_do (x11_test_client, &error,
"create", X11_TEST_CLIENT_WINDOW,
NULL))
g_error ("Failed to create X11 window: %s", error->message);
if (!meta_test_client_do (x11_test_client, &error,
"show", X11_TEST_CLIENT_WINDOW,
NULL))
g_error ("Failed to show the window: %s", error->message);
check_test_client_state (x11_test_client);
/* Make sure we have a single output. */
hotplug_test_case_setup.n_outputs = 1;
hotplug_test_case_setup.n_crtcs = 1;
test_setup = meta_create_monitor_test_setup (test_backend,
&hotplug_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
wait_for_paint (stage);
g_assert_cmpint (g_list_length (clutter_actor_peek_stage_views (stage)),
==,
1);
/* Find client window actor and ensure it's on a stage view. */
test_window = meta_test_client_find_window (x11_test_client,
X11_TEST_CLIENT_WINDOW,
&error);
if (!test_window)
g_error ("Failed to find the window: %s", error->message);
window_actor = CLUTTER_ACTOR (meta_window_actor_from_window (test_window));
wait_for_window_map (stage, window_actor);
g_assert_nonnull (clutter_actor_peek_stage_views (window_actor));
/* Queue an X11 _NET_WM_FRAME_DRAWN event; this will find the frame clock via
* the actor stage view list.
*/
meta_window_actor_queue_frame_drawn (META_WINDOW_ACTOR (window_actor), TRUE);
/* Hotplug to rebuild the views, will clear the window actor view list. */
test_setup = meta_create_monitor_test_setup (test_backend,
&hotplug_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
g_assert_null (clutter_actor_peek_stage_views (window_actor));
/* Queue an X11 _NET_WM_FRAME_DRAWN event; this will find the frame clock via
* the stage's frame clock, as the actor hasn't been been through relayout.
*/
meta_window_actor_queue_frame_drawn (META_WINDOW_ACTOR (window_actor), TRUE);
/* Hotplug again to re-rebuild the views, will again clear the window actor
* view list, which will be a no-op. */
test_setup = meta_create_monitor_test_setup (test_backend,
&hotplug_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
/* Make sure we're not using some old frame clock when queuing another
* _NET_WM_FRAME_DRAWN event. */
meta_window_actor_queue_frame_drawn (META_WINDOW_ACTOR (window_actor), TRUE);
wait_for_paint (stage);
if (!meta_test_client_quit (x11_test_client, &error))
g_error ("Failed to quit X11 test client: %s", error->message);
meta_test_client_destroy (x11_test_client);
}
static void
meta_test_timeline_actor_destroyed (void)
{
MetaBackend *backend = test_backend;
ClutterActor *stage;
GList *stage_views;
ClutterActor *persistent_actor;
ClutterActor *actor;
ClutterTimeline *timeline;
gboolean did_stage_views_changed = FALSE;
ensure_view_count (0);
stage = meta_backend_get_stage (backend);
clutter_actor_show (stage);
persistent_actor = clutter_actor_new ();
clutter_actor_add_child (stage, persistent_actor);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_null (stage_views);
stage_views = clutter_actor_peek_stage_views (stage);
g_assert_null (stage_views);
g_assert_null (clutter_actor_pick_frame_clock (stage, NULL));
actor = clutter_actor_new ();
clutter_actor_add_child (stage, actor);
g_assert_null (clutter_actor_pick_frame_clock (actor, NULL));
timeline = clutter_timeline_new_for_actor (actor, 100);
clutter_timeline_start (timeline);
g_signal_connect (stage, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
&did_stage_views_changed);
clutter_actor_destroy (actor);
g_object_unref (timeline);
ensure_view_count (1);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 1);
g_assert_false (did_stage_views_changed);
clutter_actor_queue_redraw (persistent_actor);
clutter_stage_schedule_update (CLUTTER_STAGE (stage));
wait_for_paint (stage);
g_assert_true (did_stage_views_changed);
g_signal_handlers_disconnect_by_func (stage, on_stage_views_changed,
&did_stage_views_changed);
clutter_actor_destroy (persistent_actor);
}
static void
meta_test_timeline_actor_tree_clear (void)
{
ClutterActor *stage;
ClutterActor *container1;
ClutterActor *container2;
g_autoptr (ClutterActor) floating = NULL;
g_autoptr (ClutterTimeline) timeline = NULL;
GList *stage_views;
stage = meta_backend_get_stage (meta_context_get_backend (test_context));
ensure_view_count (1);
container1 = clutter_actor_new ();
clutter_actor_set_size (container1, 100, 100);
clutter_actor_add_child (stage, container1);
wait_for_paint (stage);
container2 = clutter_actor_new ();
clutter_actor_set_size (container2, 100, 100);
clutter_actor_add_child (stage, container2);
floating = g_object_ref_sink (clutter_actor_new ());
clutter_actor_set_size (floating, 100, 100);
clutter_actor_add_child (container2, floating);
timeline = clutter_timeline_new_for_actor (floating, 100);
clutter_actor_remove_child (container2, floating);
clutter_actor_add_child (container1, floating);
ensure_view_count (1);
is_on_stage_views (container1, 0);
is_on_stage_views (container2, 0);
is_on_stage_views (floating, 0);
wait_for_paint (stage);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
is_on_stage_views (container1, 1, stage_views->data);
is_on_stage_views (container2, 1, stage_views->data);
is_on_stage_views (floating, 1, stage_views->data);
clutter_actor_destroy (floating);
clutter_actor_destroy (container1);
clutter_actor_destroy (container2);
}
static void
on_before_tests (MetaContext *context)
{
test_backend = meta_context_get_backend (context);
}
static void
init_tests (void)
{
meta_init_monitor_test_setup (create_stage_view_test_setup);
g_test_add_func ("/stage-view/stage-views-exist",
meta_test_stage_views_exist);
g_test_add_func ("/stage-views/actor-stage-views",
meta_test_actor_stage_views);
g_test_add_func ("/stage-views/actor-stage-views-relayout",
meta_test_actor_stage_views_relayout);
g_test_add_func ("/stage-views/actor-stage-views-reparent",
meta_test_actor_stage_views_reparent);
g_test_add_func ("/stage-views/actor-stage-views-hide-parent",
meta_test_actor_stage_views_hide_parent);
g_test_add_func ("/stage-views/actor-stage-views-hot-plug",
meta_test_actor_stage_views_hot_plug);
g_test_add_func ("/stage-views/actor-stage-views-frame-clock",
meta_test_actor_stage_views_frame_clock);
g_test_add_func ("/stage-views/actor-stage-views-timeline",
meta_test_actor_stage_views_timeline);
g_test_add_func ("/stage-views/actor-stage-views-parent-rebuilt",
meta_test_actor_stage_views_parent_views_rebuilt);
g_test_add_func ("/stage-views/actor-stage-views-parent-changed",
meta_test_actor_stage_views_parent_views_changed);
g_test_add_func ("/stage-views/actor-stage-views-and-frame-clocks-freed",
meta_test_actor_stage_views_and_frame_clocks_freed);
g_test_add_func ("/stage-views/actor-stage-viwes-queue-frame-drawn",
meta_test_actor_stage_views_queue_frame_drawn);
g_test_add_func ("/stage-views/timeline/actor-destroyed",
meta_test_timeline_actor_destroyed);
g_test_add_func ("/stage-views/timeline/tree-clear",
meta_test_timeline_actor_tree_clear);
}
int
main (int argc, char *argv[])
{
g_autoptr (MetaContext) context = NULL;
g_setenv ("MUTTER_DEBUG_DISABLE_ANIMATIONS", "1", TRUE);
context = meta_create_test_context (META_CONTEXT_TEST_TYPE_NESTED,
META_CONTEXT_TEST_FLAG_TEST_CLIENT);
g_assert (meta_context_configure (context, &argc, &argv, NULL));
test_context = context;
init_tests ();
g_signal_connect (context, "before-tests",
G_CALLBACK (on_before_tests), NULL);
return meta_context_test_run_tests (META_CONTEXT_TEST (context),
META_TEST_RUN_FLAG_NONE);
}