mutter/src/tests/stage-view-tests.c

625 lines
20 KiB
C
Raw Normal View History

/*
* 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 "compositor/meta-plugin-manager.h"
#include "core/main-private.h"
#include "meta/main.h"
#include "tests/meta-backend-test.h"
#include "tests/monitor-test-utils.h"
#include "tests/test-utils.h"
#define FRAME_WARNING "Frame has assigned frame counter but no frame drawn time"
static gboolean
run_tests (gpointer data)
{
MetaBackend *backend = meta_get_backend ();
MetaSettings *settings = meta_backend_get_settings (backend);
gboolean ret;
g_test_log_set_fatal_handler (NULL, NULL);
meta_settings_override_experimental_features (settings);
meta_settings_enable_experimental_feature (
settings,
META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER);
ret = g_test_run ();
meta_quit (ret != 0);
return G_SOURCE_REMOVE;
}
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 = meta_get_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,
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, FALSE);
g_signal_handler_disconnect (stage, was_painted_id);
}
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 = meta_get_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 = meta_get_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 = meta_get_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 = meta_get_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 = meta_get_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_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 (void)
{
return create_monitor_test_setup (&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 = meta_get_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);
test_setup = create_monitor_test_setup (&hotplug_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
prev_stage_views = stage_views;
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);
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
init_tests (int argc, char **argv)
{
meta_monitor_manager_test_init_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);
}
int
main (int argc, char *argv[])
{
test_init (&argc, &argv);
init_tests (argc, argv);
meta_plugin_manager_load (test_get_plugin_name ());
meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND,
META_TYPE_BACKEND_TEST);
meta_init ();
meta_register_with_session ();
g_idle_add (run_tests, NULL);
return meta_run ();
}