
An action can be performed both on a signal emitted by a window or by a well-known signal name, so that one can do: [window-id]::signal => command And so these would work: w/1::position-changed => resize w/2 20 30 ::monitors-changed => move w/1 30 40 We only support "monitors-changed" global signal name or connecting to window signals for now, but this can be easily expanded adding support for more global signal values or parsing other kinds of object instances Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3701>
2253 lines
66 KiB
C
2253 lines
66 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2014 Red Hat, Inc.
|
|
*
|
|
* 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 <gio/gio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "backends/meta-virtual-monitor.h"
|
|
#include "compositor/meta-window-actor-private.h"
|
|
#include "core/meta-workspace-manager-private.h"
|
|
#include "core/window-private.h"
|
|
#include "meta-test/meta-context-test.h"
|
|
#include "meta/util.h"
|
|
#include "meta/window.h"
|
|
#include "core/meta-workspace-manager-private.h"
|
|
#include "core/workspace-private.h"
|
|
#include "tests/meta-test-utils.h"
|
|
#include "wayland/meta-wayland.h"
|
|
#include "wayland/meta-window-wayland.h"
|
|
#include "x11/meta-x11-display-private.h"
|
|
|
|
typedef struct {
|
|
MetaContext *context;
|
|
GHashTable *clients;
|
|
MetaX11AlarmFilter *alarm_filter;
|
|
MetaAsyncWaiter *waiter;
|
|
GString *warning_messages;
|
|
GMainLoop *loop;
|
|
gulong x11_display_opened_handler_id;
|
|
GHashTable *virtual_monitors;
|
|
ClutterVirtualInputDevice *pointer;
|
|
GHashTable *cloned_windows;
|
|
} TestCase;
|
|
|
|
#define META_SIDE_TEST_CASE_NONE G_MAXINT32
|
|
|
|
static gboolean test_case_do (TestCase *test,
|
|
const char *filename,
|
|
int line_no,
|
|
int argc,
|
|
char **argv,
|
|
GError **error);
|
|
|
|
static void
|
|
set_true_cb (gboolean *value)
|
|
{
|
|
*value = TRUE;
|
|
}
|
|
|
|
static void
|
|
wait_for_signal_emission (gpointer instance,
|
|
const char *signal_name)
|
|
{
|
|
gulong handler_id;
|
|
gboolean changed = FALSE;
|
|
|
|
handler_id = g_signal_connect_swapped (instance, signal_name,
|
|
G_CALLBACK (set_true_cb), &changed);
|
|
while (!changed)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
g_signal_handler_disconnect (instance, handler_id);
|
|
}
|
|
|
|
static gboolean
|
|
test_case_alarm_filter (MetaX11Display *x11_display,
|
|
XSyncAlarmNotifyEvent *event,
|
|
gpointer data)
|
|
{
|
|
TestCase *test = data;
|
|
|
|
if (meta_async_waiter_process_x11_event (test->waiter, x11_display, event))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
on_x11_display_opened (MetaDisplay *display,
|
|
TestCase *test)
|
|
{
|
|
MetaX11Display *x11_display = meta_display_get_x11_display (display);
|
|
|
|
test->alarm_filter =
|
|
meta_x11_display_add_alarm_filter (x11_display,
|
|
test_case_alarm_filter,
|
|
test);
|
|
test->waiter = meta_async_waiter_new (display->x11_display);
|
|
}
|
|
|
|
static TestCase *
|
|
test_case_new (MetaContext *context)
|
|
{
|
|
TestCase *test = g_new0 (TestCase, 1);
|
|
MetaDisplay *display = meta_context_get_display (context);
|
|
MetaBackend *backend = meta_context_get_backend (context);
|
|
ClutterSeat *seat = meta_backend_get_default_seat (backend);
|
|
MetaVirtualMonitor *monitor;
|
|
|
|
if (display->x11_display)
|
|
{
|
|
on_x11_display_opened (display, test);
|
|
}
|
|
else
|
|
{
|
|
test->x11_display_opened_handler_id =
|
|
g_signal_connect (display, "x11-display-opened",
|
|
G_CALLBACK (on_x11_display_opened),
|
|
test);
|
|
}
|
|
|
|
test->context = context;
|
|
test->clients = g_hash_table_new (g_str_hash, g_str_equal);
|
|
test->loop = g_main_loop_new (NULL, FALSE);
|
|
test->pointer = clutter_seat_create_virtual_device (seat,
|
|
CLUTTER_POINTER_DEVICE);
|
|
|
|
test->virtual_monitors = g_hash_table_new_full (g_str_hash,
|
|
g_str_equal,
|
|
g_free,
|
|
g_object_unref);
|
|
monitor = meta_create_test_monitor (context, 800, 600, 60.0);
|
|
g_hash_table_insert (test->virtual_monitors, g_strdup ("default"), monitor);
|
|
|
|
return test;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_loop_quit (gpointer data)
|
|
{
|
|
TestCase *test = data;
|
|
|
|
g_main_loop_quit (test->loop);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_dispatch (TestCase *test,
|
|
GError **error)
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
ClutterActor *stage = meta_backend_get_stage (backend);
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaCompositor *compositor = meta_display_get_compositor (display);
|
|
MetaLaters *laters = meta_compositor_get_laters (compositor);
|
|
|
|
/* Wait until we've done any outstanding queued up work.
|
|
* Though we add this as BEFORE_REDRAW, the iteration that runs the
|
|
* BEFORE_REDRAW idles will proceed on and do the redraw, so we're
|
|
* waiting until after *all* frame processing.
|
|
*/
|
|
meta_laters_add (laters, META_LATER_BEFORE_REDRAW,
|
|
test_case_loop_quit,
|
|
test,
|
|
NULL);
|
|
|
|
clutter_stage_schedule_update (CLUTTER_STAGE (stage));
|
|
g_main_loop_run (test->loop);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_wait (TestCase *test,
|
|
GError **error)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
/* First have each client set a XSync counter, and wait until
|
|
* we receive the resulting event - so we know we've received
|
|
* everything that the client have sent us.
|
|
*/
|
|
g_hash_table_iter_init (&iter, test->clients);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
if (!meta_test_client_wait (value, error))
|
|
return FALSE;
|
|
|
|
/* Then wait until we've done any outstanding queued up work. */
|
|
test_case_dispatch (test, error);
|
|
|
|
/* Then set an XSync counter ourselves and and wait until
|
|
* we receive the resulting event - this makes sure that we've
|
|
* received back any X events we generated.
|
|
*/
|
|
if (test->waiter)
|
|
meta_async_waiter_set_and_wait (test->waiter);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_sleep (TestCase *test,
|
|
uint32_t interval_ms,
|
|
GError **error)
|
|
{
|
|
g_timeout_add_full (G_PRIORITY_LOW, interval_ms,
|
|
test_case_loop_quit, test, NULL);
|
|
g_main_loop_run (test->loop);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define BAD_COMMAND(...) \
|
|
G_STMT_START { \
|
|
g_set_error (error, \
|
|
META_TEST_CLIENT_ERROR, \
|
|
META_TEST_CLIENT_ERROR_BAD_COMMAND, \
|
|
__VA_ARGS__); \
|
|
return FALSE; \
|
|
} G_STMT_END
|
|
|
|
static MetaTestClient *
|
|
test_case_lookup_client (TestCase *test,
|
|
char *client_id,
|
|
GError **error)
|
|
{
|
|
MetaTestClient *client = g_hash_table_lookup (test->clients, client_id);
|
|
if (!client)
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_BAD_COMMAND,
|
|
"No such client %s", client_id);
|
|
}
|
|
|
|
return client;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_parse_window_id (TestCase *test,
|
|
const char *client_and_window_id,
|
|
MetaTestClient **client,
|
|
const char **window_id,
|
|
GError **error)
|
|
{
|
|
const char *slash = strchr (client_and_window_id, '/');
|
|
char *tmp;
|
|
if (slash == NULL)
|
|
BAD_COMMAND ("client/window ID %s doesn't contain a /", client_and_window_id);
|
|
|
|
*window_id = slash + 1;
|
|
|
|
tmp = g_strndup (client_and_window_id, slash - client_and_window_id);
|
|
*client = test_case_lookup_client (test, tmp, error);
|
|
g_free (tmp);
|
|
|
|
return client != NULL;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_assert_stacking (TestCase *test,
|
|
char **expected_windows,
|
|
int n_expected_windows,
|
|
MetaWorkspace *workspace,
|
|
GError **error)
|
|
{
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
guint64 *windows;
|
|
int n_windows;
|
|
GString *stack_string = g_string_new (NULL);
|
|
GString *expected_string = g_string_new (NULL);
|
|
int i;
|
|
|
|
meta_stack_tracker_get_stack (display->stack_tracker, &windows, &n_windows);
|
|
for (i = 0; i < n_windows; i++)
|
|
{
|
|
MetaWindow *window = meta_display_lookup_stack_id (display, windows[i]);
|
|
|
|
if (workspace && !meta_window_located_on_workspace (window, workspace))
|
|
continue;
|
|
|
|
if (window != NULL && window->title)
|
|
{
|
|
if (stack_string->len > 0)
|
|
g_string_append_c (stack_string, ' ');
|
|
|
|
if (g_str_has_prefix (window->title, "test/"))
|
|
g_string_append (stack_string, window->title + 5);
|
|
else
|
|
g_string_append_printf (stack_string, "(%s)", window->title);
|
|
}
|
|
else if (windows[i] == display->x11_display->guard_window)
|
|
{
|
|
if (stack_string->len > 0)
|
|
g_string_append_c (stack_string, ' ');
|
|
|
|
g_string_append_c (stack_string, '|');
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n_expected_windows; i++)
|
|
{
|
|
if (expected_string->len > 0)
|
|
g_string_append_c (expected_string, ' ');
|
|
|
|
g_string_append (expected_string, expected_windows[i]);
|
|
}
|
|
|
|
/* Don't require '| ' as a prefix if there are no hidden windows - we
|
|
* remove the prefix from the actual string instead of adding it to the
|
|
* expected string for clarity of the error message
|
|
*/
|
|
if (index (expected_string->str, '|') == NULL && stack_string->str[0] == '|')
|
|
{
|
|
g_string_erase (stack_string,
|
|
0, stack_string->str[1] == ' ' ? 2 : 1);
|
|
}
|
|
|
|
if (strcmp (expected_string->str, stack_string->str) != 0)
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"stacking: expected='%s', actual='%s'",
|
|
expected_string->str, stack_string->str);
|
|
}
|
|
|
|
g_string_free (stack_string, TRUE);
|
|
g_string_free (expected_string, TRUE);
|
|
|
|
return *error == NULL;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_assert_focused (TestCase *test,
|
|
const char *expected_window,
|
|
GError **error)
|
|
{
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
|
|
if (!display->focus_window)
|
|
{
|
|
if (g_strcmp0 (expected_window, "none") != 0)
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"focus: expected='%s', actual='none'", expected_window);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const char *focused = display->focus_window->title;
|
|
|
|
if (g_str_has_prefix (focused, "test/"))
|
|
focused += 5;
|
|
|
|
if (g_strcmp0 (focused, expected_window) != 0)
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"focus: expected='%s', actual='%s'",
|
|
expected_window, focused);
|
|
}
|
|
|
|
return *error == NULL;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_assert_size (TestCase *test,
|
|
MetaWindow *window,
|
|
int expected_width,
|
|
int expected_height,
|
|
GError **error)
|
|
{
|
|
MtkRectangle frame_rect;
|
|
|
|
meta_window_get_frame_rect (window, &frame_rect);
|
|
|
|
if (frame_rect.width != expected_width ||
|
|
frame_rect.height != expected_height)
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"Expected size %dx%d didn't match actual size %dx%d",
|
|
expected_width, expected_height,
|
|
frame_rect.width, frame_rect.height);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_check_xserver_stacking (TestCase *test,
|
|
GError **error)
|
|
{
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
GString *local_string = g_string_new (NULL);
|
|
GString *x11_string = g_string_new (NULL);
|
|
int i;
|
|
|
|
if (!display->x11_display)
|
|
return TRUE;
|
|
|
|
guint64 *windows;
|
|
int n_windows;
|
|
meta_stack_tracker_get_stack (display->stack_tracker, &windows, &n_windows);
|
|
|
|
for (i = 0; i < n_windows; i++)
|
|
{
|
|
if (META_STACK_ID_IS_X11 (windows[i]))
|
|
{
|
|
if (local_string->len > 0)
|
|
g_string_append_c (local_string, ' ');
|
|
|
|
g_string_append_printf (local_string, "%#lx", (Window)windows[i]);
|
|
}
|
|
}
|
|
|
|
Window root;
|
|
Window parent;
|
|
Window *children;
|
|
unsigned int n_children;
|
|
XQueryTree (display->x11_display->xdisplay,
|
|
display->x11_display->xroot,
|
|
&root, &parent, &children, &n_children);
|
|
|
|
for (i = 0; i < (int)n_children; i++)
|
|
{
|
|
if (x11_string->len > 0)
|
|
g_string_append_c (x11_string, ' ');
|
|
|
|
g_string_append_printf (x11_string, "%#lx", (Window)children[i]);
|
|
}
|
|
|
|
if (strcmp (x11_string->str, local_string->str) != 0)
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"xserver stacking: x11='%s', local='%s'",
|
|
x11_string->str, local_string->str);
|
|
|
|
XFree (children);
|
|
|
|
g_string_free (local_string, TRUE);
|
|
g_string_free (x11_string, TRUE);
|
|
|
|
return *error == NULL;
|
|
}
|
|
|
|
static int
|
|
maybe_multiply (const char *str,
|
|
int value,
|
|
const char **out_str)
|
|
{
|
|
*out_str = str;
|
|
|
|
if (str[0] == '*')
|
|
{
|
|
double multiplier;
|
|
|
|
str += 1;
|
|
multiplier = g_strtod (str, (char **) out_str);
|
|
|
|
value = round (multiplier * value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int
|
|
maybe_divide (const char *str,
|
|
int value,
|
|
const char **out_str)
|
|
{
|
|
*out_str = str;
|
|
|
|
if (str[0] == '/')
|
|
{
|
|
double divider;
|
|
|
|
str += 1;
|
|
divider = g_strtod (str, (char **) out_str);
|
|
|
|
value = round (value / divider);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int
|
|
maybe_do_math (const char *str,
|
|
int value,
|
|
const char **out_str)
|
|
{
|
|
switch (str[0])
|
|
{
|
|
case '*':
|
|
value = maybe_multiply (str, value, &str);
|
|
break;
|
|
case '/':
|
|
value = maybe_divide (str, value, &str);
|
|
break;
|
|
default:
|
|
*out_str = str;
|
|
return value;
|
|
}
|
|
|
|
return maybe_do_math (str, value, out_str);
|
|
}
|
|
|
|
static int
|
|
parse_monitor_size (MtkRectangle *logical_monitor_layout,
|
|
const char *size_str)
|
|
{
|
|
int value;
|
|
|
|
if (strstr (size_str, "MONITOR_WIDTH") == size_str)
|
|
{
|
|
value = logical_monitor_layout->width;
|
|
size_str += strlen ("MONITOR_WIDTH");
|
|
value = maybe_do_math (size_str, value, &size_str);
|
|
}
|
|
else if (strstr (size_str, "MONITOR_HEIGHT") == size_str)
|
|
{
|
|
value = logical_monitor_layout->height;
|
|
size_str += strlen ("MONITOR_HEIGHT");
|
|
value = maybe_do_math (size_str, value, &size_str);
|
|
}
|
|
else
|
|
{
|
|
value = atoi (size_str);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int
|
|
parse_window_size (MetaWindow *window,
|
|
const char *size_str)
|
|
{
|
|
MetaLogicalMonitor *logical_monitor;
|
|
MtkRectangle logical_monitor_layout;
|
|
|
|
logical_monitor = meta_window_find_monitor_from_frame_rect (window);
|
|
g_assert_nonnull (logical_monitor);
|
|
|
|
logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
|
|
|
|
return parse_monitor_size (&logical_monitor_layout, size_str);
|
|
}
|
|
|
|
static MetaLogicalMonitor *
|
|
get_logical_monitor (TestCase *test,
|
|
const char *monitor_id,
|
|
GError **error)
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWindow *focus_window;
|
|
MetaLogicalMonitor *logical_monitor;
|
|
|
|
if (monitor_id)
|
|
{
|
|
MetaVirtualMonitor *virtual_monitor;
|
|
MetaOutput *output;
|
|
MetaMonitor *monitor;
|
|
|
|
virtual_monitor =
|
|
g_hash_table_lookup (test->virtual_monitors, monitor_id);
|
|
|
|
if (!virtual_monitor)
|
|
{
|
|
g_set_error (error, META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_BAD_COMMAND,
|
|
"Unknown monitor %s", monitor_id);
|
|
return NULL;
|
|
}
|
|
|
|
output = meta_virtual_monitor_get_output (virtual_monitor);
|
|
monitor = meta_output_get_monitor (output);
|
|
|
|
return meta_monitor_get_logical_monitor (monitor);
|
|
}
|
|
|
|
focus_window = meta_display_get_focus_window (display);
|
|
logical_monitor = meta_window_get_main_logical_monitor (focus_window);
|
|
|
|
if (logical_monitor)
|
|
return logical_monitor;
|
|
|
|
return meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
|
|
}
|
|
|
|
static gboolean
|
|
str_to_bool (const char *str,
|
|
gboolean *val)
|
|
{
|
|
if (g_ascii_strcasecmp (str, "true") == 0) {
|
|
if (val != NULL)
|
|
*val = true;
|
|
return TRUE;
|
|
}
|
|
|
|
if (g_ascii_strcasecmp (str, "false") == 0) {
|
|
if (val != NULL)
|
|
*val = false;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
str_to_side (const char *str,
|
|
MetaSide *out_side)
|
|
{
|
|
if (g_str_equal (str, "left"))
|
|
{
|
|
*out_side = META_SIDE_LEFT;
|
|
return TRUE;
|
|
}
|
|
|
|
if (g_str_equal (str, "right"))
|
|
{
|
|
*out_side = META_SIDE_RIGHT;
|
|
return TRUE;
|
|
}
|
|
|
|
if (g_str_equal (str, "top"))
|
|
{
|
|
*out_side = META_SIDE_TOP;
|
|
return TRUE;
|
|
}
|
|
|
|
if (g_str_equal (str, "bottom"))
|
|
{
|
|
*out_side = META_SIDE_BOTTOM;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_add_strut (TestCase *test,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height,
|
|
MetaSide side,
|
|
GError **error)
|
|
{
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
MtkRectangle rect = { x, y, width, height };
|
|
MetaStrut strut = { rect, side };
|
|
GList *workspaces =
|
|
meta_workspace_manager_get_workspaces (workspace_manager);
|
|
GList *l;
|
|
|
|
for (l = workspaces; l; l = l->next)
|
|
{
|
|
MetaWorkspace *workspace = l->data;
|
|
g_autoptr (GSList) struts_list = NULL;
|
|
g_autoslist (MetaStrut) struts = NULL;
|
|
|
|
struts_list = meta_workspace_get_builtin_struts (workspace);
|
|
struts = g_slist_append (g_steal_pointer (&struts_list),
|
|
g_memdup2 (&strut, sizeof (MetaStrut)));
|
|
meta_workspace_set_builtin_struts (workspace, struts);
|
|
}
|
|
|
|
wait_for_signal_emission (display, "workareas-changed");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_clear_struts (TestCase *test,
|
|
MetaSide side,
|
|
GError **error)
|
|
{
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
GList *workspaces =
|
|
meta_workspace_manager_get_workspaces (workspace_manager);
|
|
GList *l;
|
|
|
|
for (l = workspaces; l; l = l->next)
|
|
{
|
|
MetaWorkspace *workspace = l->data;
|
|
g_autoslist (MetaStrut) struts = NULL;
|
|
|
|
if (side != META_SIDE_TEST_CASE_NONE)
|
|
{
|
|
GSList *sl;
|
|
|
|
struts = meta_workspace_get_builtin_struts (workspace);
|
|
|
|
for (sl = struts; sl;)
|
|
{
|
|
MetaStrut *strut = sl->data;
|
|
GSList *old;
|
|
|
|
old = sl;
|
|
sl = sl->next;
|
|
|
|
if (strut->side == side)
|
|
{
|
|
struts = g_slist_remove_link (struts, old);
|
|
g_clear_pointer (&strut, g_free);
|
|
}
|
|
}
|
|
}
|
|
|
|
meta_workspace_set_builtin_struts (workspace, struts);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
TestCase *test_case;
|
|
const char *filename;
|
|
int line_no;
|
|
int argc;
|
|
char **argv;
|
|
GError **error;
|
|
GObject *instance;
|
|
gulong handler_id;
|
|
} TestCaseArgs;
|
|
|
|
static void
|
|
test_case_signal_cb (TestCaseArgs *test_case_args)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
g_signal_handler_disconnect (test_case_args->instance,
|
|
test_case_args->handler_id);
|
|
|
|
if (!test_case_do (test_case_args->test_case,
|
|
test_case_args->filename,
|
|
test_case_args->line_no,
|
|
test_case_args->argc,
|
|
test_case_args->argv,
|
|
&error))
|
|
g_warning ("Failed to run test command in signal handler: %s",
|
|
error->message);
|
|
|
|
g_strfreev (test_case_args->argv);
|
|
g_free (test_case_args);
|
|
}
|
|
|
|
static gboolean
|
|
test_case_parse_signal (TestCase *test,
|
|
int argc,
|
|
char **argv,
|
|
char **out_signal_name,
|
|
GObject **out_signal_instance,
|
|
GError **error)
|
|
{
|
|
const char *signal_start;
|
|
GObject *instance_obj = NULL;
|
|
const char *signal_name;
|
|
|
|
*out_signal_instance = NULL;
|
|
*out_signal_name = NULL;
|
|
|
|
if (argc < 3 || !g_str_equal (argv[1], "=>"))
|
|
BAD_COMMAND ("usage: [window-id]::signal => command");
|
|
|
|
signal_start = strstr (argv[0], "::");
|
|
if (!signal_start)
|
|
BAD_COMMAND ("Invalid syntax, no signal parameter");
|
|
|
|
signal_name = signal_start + 2;
|
|
|
|
if (!strlen (signal_name))
|
|
BAD_COMMAND ("Invalid syntax, empty signal name");
|
|
|
|
if (signal_start != argv[0])
|
|
{
|
|
g_autofree char *instance = g_strndup (argv[0], signal_start - argv[0]);
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
MetaWindow *window;
|
|
|
|
if (!test_case_parse_window_id (test, instance, &client,
|
|
&window_id, error))
|
|
BAD_COMMAND ("Cannot find window for instance %s", instance);
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
BAD_COMMAND ("Cannot find window for window id %s", window_id);
|
|
|
|
instance_obj = G_OBJECT (window);
|
|
}
|
|
|
|
if (!instance_obj)
|
|
{
|
|
if (g_str_equal (signal_name, "monitors-changed"))
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
|
|
instance_obj = G_OBJECT (monitor_manager);
|
|
}
|
|
else
|
|
{
|
|
BAD_COMMAND ("Unknown global signal name '%s'", signal_name);
|
|
}
|
|
}
|
|
|
|
if (!g_signal_lookup (signal_name, G_TYPE_FROM_INSTANCE (instance_obj)))
|
|
{
|
|
BAD_COMMAND ("No signal '%s' in object of type %s",
|
|
signal_name,
|
|
g_type_name_from_instance ((GTypeInstance *) instance_obj));
|
|
}
|
|
|
|
*out_signal_instance = g_object_ref (instance_obj);
|
|
*out_signal_name = g_strdup (signal_name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_do (TestCase *test,
|
|
const char *filename,
|
|
int line_no,
|
|
int argc,
|
|
char **argv,
|
|
GError **error)
|
|
{
|
|
g_autofree char *command = NULL;
|
|
|
|
command = g_strjoinv (" ", argv);
|
|
g_debug ("%s:%d: '%s'", filename, line_no, command);
|
|
|
|
if (strcmp (argv[0], "new_client") == 0)
|
|
{
|
|
MetaWindowClientType type;
|
|
MetaTestClient *client;
|
|
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: new_client <client-id> [wayland|x11]");
|
|
|
|
if (strcmp (argv[2], "x11") == 0)
|
|
type = META_WINDOW_CLIENT_TYPE_X11;
|
|
else if (strcmp (argv[2], "wayland") == 0)
|
|
type = META_WINDOW_CLIENT_TYPE_WAYLAND;
|
|
else
|
|
BAD_COMMAND("usage: new_client <client-id> [wayland|x11]");
|
|
|
|
if (g_hash_table_lookup (test->clients, argv[1]))
|
|
BAD_COMMAND("client %s already exists", argv[1]);
|
|
|
|
client = meta_test_client_new (test->context, argv[1], type, error);
|
|
if (!client)
|
|
return FALSE;
|
|
|
|
g_hash_table_insert (test->clients, meta_test_client_get_id (client), client);
|
|
}
|
|
else if (strcmp (argv[0], "quit_client") == 0)
|
|
{
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: quit_client <client-id>");
|
|
|
|
MetaTestClient *client = test_case_lookup_client (test, argv[1], error);
|
|
if (!client)
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_quit (client, error))
|
|
return FALSE;
|
|
|
|
g_hash_table_remove (test->clients, meta_test_client_get_id (client));
|
|
meta_test_client_destroy (client);
|
|
}
|
|
else if (strcmp (argv[0], "create") == 0)
|
|
{
|
|
if (!(argc == 2 ||
|
|
(argc == 3 && strcmp (argv[2], "override") == 0) ||
|
|
(argc == 3 && strcmp (argv[2], "csd") == 0)))
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id > [override|csd]", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error,
|
|
"create", window_id,
|
|
argc == 3 ? argv[2] : NULL,
|
|
NULL))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_wait (client, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "set_parent") == 0 ||
|
|
strcmp (argv[0], "set_parent_exported") == 0)
|
|
{
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> <parent-window-id>",
|
|
argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error,
|
|
argv[0], window_id,
|
|
argv[2],
|
|
NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "accept_focus") == 0)
|
|
{
|
|
if (argc != 3 || !str_to_bool (argv[2], NULL))
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
|
|
argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error,
|
|
argv[0], window_id,
|
|
argv[2],
|
|
NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "can_take_focus") == 0)
|
|
{
|
|
if (argc != 3 || !str_to_bool (argv[2], NULL))
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
|
|
argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error,
|
|
argv[0], window_id,
|
|
argv[2],
|
|
NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "accept_take_focus") == 0)
|
|
{
|
|
if (argc != 3 || !str_to_bool (argv[2], NULL))
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
|
|
argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error,
|
|
argv[0], window_id,
|
|
argv[2],
|
|
NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "show") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
gboolean show_async = FALSE;
|
|
|
|
if (argc != 2 && argc != 3)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> [async]", argv[0]);
|
|
|
|
if (argc == 3 && strcmp (argv[2], "async") == 0)
|
|
show_async = TRUE;
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error, argv[0], window_id, NULL))
|
|
return FALSE;
|
|
|
|
if (!test_case_wait (test, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
if (!show_async)
|
|
meta_test_client_wait_for_window_shown (client, window);
|
|
}
|
|
else if (strcmp (argv[0], "sync_shown") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
meta_test_client_wait_for_window_shown (client, window);
|
|
}
|
|
else if (strcmp (argv[0], "resize") == 0)
|
|
{
|
|
if (argc != 4)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> width height", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error, argv[0], window_id,
|
|
argv[2], argv[3], NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "move") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
if (argc != 4)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> x y", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
meta_window_move_frame (window, TRUE, atoi (argv[2]), atoi (argv[3]));
|
|
}
|
|
else if (strcmp (argv[0], "tile") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> [right|left]", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
MetaTileMode tile_mode;
|
|
if (strcmp (argv[2], "right") == 0)
|
|
{
|
|
tile_mode = META_TILE_RIGHT;
|
|
}
|
|
else if (strcmp (argv[2], "left") == 0)
|
|
{
|
|
tile_mode = META_TILE_LEFT;
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"Invalid tile mode '%s'", argv[2]);
|
|
return FALSE;
|
|
}
|
|
|
|
meta_window_tile (window, tile_mode);
|
|
}
|
|
else if (strcmp (argv[0], "untile") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
meta_window_untile (window);
|
|
}
|
|
else if (strcmp (argv[0], "hide") == 0 ||
|
|
strcmp (argv[0], "activate") == 0 ||
|
|
strcmp (argv[0], "raise") == 0 ||
|
|
strcmp (argv[0], "lower") == 0 ||
|
|
strcmp (argv[0], "minimize") == 0 ||
|
|
strcmp (argv[0], "unminimize") == 0 ||
|
|
strcmp (argv[0], "maximize") == 0 ||
|
|
strcmp (argv[0], "unmaximize") == 0 ||
|
|
strcmp (argv[0], "fullscreen") == 0 ||
|
|
strcmp (argv[0], "unfullscreen") == 0 ||
|
|
strcmp (argv[0], "freeze") == 0 ||
|
|
strcmp (argv[0], "thaw") == 0 ||
|
|
strcmp (argv[0], "destroy") == 0)
|
|
{
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error, argv[0], window_id, NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "local_activate") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
meta_window_activate (window, 0);
|
|
}
|
|
else if (strcmp (argv[0], "wait") == 0)
|
|
{
|
|
if (argc != 1)
|
|
BAD_COMMAND("usage: %s", argv[0]);
|
|
|
|
if (!test_case_wait (test, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "wait_reconfigure") == 0)
|
|
{
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
g_autoptr (GPtrArray) windows = NULL;
|
|
g_autoptr (GArray) serials = NULL;
|
|
int i;
|
|
gboolean has_x11_window = FALSE;
|
|
gboolean has_unfinished_configurations;
|
|
|
|
if (argc < 2)
|
|
BAD_COMMAND("usage: %s [<client-id>/<window-id>..]", argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
/*
|
|
* 1. Wait once to reconfigure
|
|
* 2. Wait for window to receive back any pending configuration
|
|
*/
|
|
|
|
if (!test_case_wait (test, error))
|
|
return FALSE;
|
|
|
|
windows = g_ptr_array_new ();
|
|
serials = g_array_new (FALSE, FALSE, sizeof (uint32_t));
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
if (META_IS_WINDOW_WAYLAND (window))
|
|
{
|
|
MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window);
|
|
uint32_t serial;
|
|
|
|
if (meta_window_wayland_get_pending_serial (wl_window, &serial))
|
|
{
|
|
g_ptr_array_add (windows, window);
|
|
g_array_append_val (serials, serial);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
has_x11_window = TRUE;
|
|
}
|
|
}
|
|
|
|
if (has_x11_window)
|
|
{
|
|
/* There is no reliable configure tracking on X11, just make a
|
|
* genuien attempt, by first making sure pending operations have
|
|
* reached us, that we have flushed any outgoing data, and that any
|
|
* new pending operation from that has reached us. */
|
|
if (!test_case_wait (test, error))
|
|
return FALSE;
|
|
if (!test_case_dispatch (test, error))
|
|
return FALSE;
|
|
if (!test_case_wait (test, error))
|
|
return FALSE;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
has_unfinished_configurations = FALSE;
|
|
for (i = 0; i < windows->len; i++)
|
|
{
|
|
MetaWindowWayland *wl_window = g_ptr_array_index (windows, i);
|
|
uint32_t serial = g_array_index (serials, uint32_t, i);
|
|
|
|
if (meta_window_wayland_peek_configuration (wl_window, serial))
|
|
{
|
|
has_unfinished_configurations = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (has_unfinished_configurations)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else if (strcmp (argv[0], "wait_size") == 0)
|
|
{
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
MetaWindow *window;
|
|
int width, height;
|
|
|
|
if (argc != 4)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> <width> <height>", argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
|
|
width = atoi (argv[2]);
|
|
height = atoi (argv[3]);
|
|
|
|
while (TRUE)
|
|
{
|
|
MtkRectangle rect;
|
|
|
|
meta_window_get_frame_rect (window, &rect);
|
|
if (rect.width == width && rect.height == height)
|
|
break;
|
|
|
|
g_main_context_iteration (NULL, TRUE);
|
|
}
|
|
}
|
|
else if (strcmp (argv[0], "dispatch") == 0)
|
|
{
|
|
if (argc != 1)
|
|
BAD_COMMAND("usage: %s", argv[0]);
|
|
|
|
if (!test_case_dispatch (test, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "sleep") == 0)
|
|
{
|
|
uint64_t interval_ms;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <milliseconds>|<known-time>", argv[0]);
|
|
|
|
if (strcmp (argv[1], "suspend_timeout") == 0)
|
|
interval_ms = s2ms (meta_get_window_suspend_timeout_s ());
|
|
else if (!g_ascii_string_to_unsigned (argv[1], 10, 0, G_MAXUINT32,
|
|
&interval_ms, error))
|
|
return FALSE;
|
|
|
|
if (!test_case_sleep (test, (uint32_t) interval_ms, error))
|
|
return FALSE;
|
|
}
|
|
else if (g_str_equal (argv[0], "add_strut") ||
|
|
g_str_equal (argv[0], "set_strut"))
|
|
{
|
|
if (argc < 6 || argc > 7)
|
|
{
|
|
BAD_COMMAND ("usage: %s <x> <y> <width> <height> <side> [monitor-id]",
|
|
argv[0]);
|
|
}
|
|
|
|
MetaLogicalMonitor *logical_monitor;
|
|
const char *monitor_id = argc > 6 ? argv[6] : NULL;
|
|
|
|
logical_monitor = get_logical_monitor (test, monitor_id, error);
|
|
if (!logical_monitor)
|
|
return FALSE;
|
|
|
|
if (g_str_equal (argv[0], "set_strut"))
|
|
{
|
|
if (!test_case_clear_struts (test, META_SIDE_TEST_CASE_NONE, error))
|
|
return FALSE;
|
|
}
|
|
|
|
MtkRectangle monitor_layout =
|
|
meta_logical_monitor_get_layout (logical_monitor);
|
|
|
|
int x = parse_monitor_size (&monitor_layout, argv[1]);
|
|
int y = parse_monitor_size (&monitor_layout, argv[2]);
|
|
int width = parse_monitor_size (&monitor_layout, argv[3]);
|
|
int height = parse_monitor_size (&monitor_layout, argv[4]);
|
|
|
|
MetaSide side;
|
|
if (!str_to_side (argv[5], &side))
|
|
BAD_COMMAND ("Invalid side: %s", argv[5]);
|
|
|
|
if (!test_case_add_strut (test, x, y, width, height, side, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "clear_struts") == 0)
|
|
{
|
|
MetaSide side = META_SIDE_TEST_CASE_NONE;
|
|
|
|
if (argc < 1 || argc > 2)
|
|
BAD_COMMAND ("usage: %s [side]", argv[0]);
|
|
|
|
if (argc > 1 && !str_to_side (argv[1], &side))
|
|
BAD_COMMAND ("Invalid side: %s", argv[1]);
|
|
|
|
if (!test_case_clear_struts (test, side, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "assert_stacking") == 0)
|
|
{
|
|
if (!test_case_assert_stacking (test, argv + 1, argc - 1, NULL, error))
|
|
return FALSE;
|
|
|
|
if (!test_case_check_xserver_stacking (test, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "assert_focused") == 0)
|
|
{
|
|
if (!test_case_assert_focused (test, argv[1], error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "assert_size") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
if (argc != 4)
|
|
{
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> <width> <height>",
|
|
argv[0]);
|
|
}
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
if (meta_window_get_frame (window))
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"Can only assert size of CSD window");
|
|
return FALSE;
|
|
}
|
|
|
|
int width = parse_window_size (window, argv[2]);
|
|
int height = parse_window_size (window, argv[3]);
|
|
g_autofree char *width_str = g_strdup_printf ("%d", width);
|
|
g_autofree char *height_str = g_strdup_printf ("%d", height);
|
|
|
|
if (!meta_test_client_do (client, error, argv[0],
|
|
window_id,
|
|
width_str,
|
|
height_str,
|
|
NULL))
|
|
return FALSE;
|
|
|
|
if (!test_case_assert_size (test, window,
|
|
width, height,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "assert_position") == 0)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
if (argc != 4)
|
|
{
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> <x> <y>",
|
|
argv[0]);
|
|
}
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
MtkRectangle frame_rect;
|
|
meta_window_get_frame_rect (window, &frame_rect);
|
|
int x = parse_window_size (window, argv[2]);
|
|
int y = parse_window_size (window, argv[3]);
|
|
if (frame_rect.x != x || frame_rect.y != y)
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"Expected window position (%d, %d) doesn't match (%d, %d)",
|
|
x, y, frame_rect.x, frame_rect.y);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (strcmp (argv[0], "stop_after_next") == 0 ||
|
|
strcmp (argv[0], "continue") == 0)
|
|
{
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>", argv[0]);
|
|
|
|
MetaTestClient *client = test_case_lookup_client (test, argv[1], error);
|
|
if (!client)
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error, argv[0], NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "clipboard-set") == 0)
|
|
{
|
|
if (argc != 4)
|
|
BAD_COMMAND("usage: %s <client-id> <mimetype> <text>", argv[0]);
|
|
|
|
MetaTestClient *client = test_case_lookup_client (test, argv[1], error);
|
|
if (!client)
|
|
return FALSE;
|
|
|
|
if (!meta_test_client_do (client, error, argv[0], argv[2], argv[3], NULL))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "resize_monitor") == 0)
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
MetaCrtcMode *crtc_mode;
|
|
const MetaCrtcModeInfo *crtc_mode_info;
|
|
MetaVirtualMonitor *monitor;
|
|
|
|
if (argc != 4)
|
|
BAD_COMMAND ("usage: %s <monitor-id> <width> <height>", argv[0]);
|
|
|
|
monitor = g_hash_table_lookup (test->virtual_monitors, argv[1]);
|
|
if (!monitor)
|
|
BAD_COMMAND ("Unknown monitor %s", argv[1]);
|
|
|
|
crtc_mode = meta_virtual_monitor_get_crtc_mode (monitor);
|
|
crtc_mode_info = meta_crtc_mode_get_info (crtc_mode);
|
|
meta_virtual_monitor_set_mode (monitor,
|
|
atoi (argv[2]),
|
|
atoi (argv[3]),
|
|
crtc_mode_info->refresh_rate);
|
|
meta_monitor_manager_reload (monitor_manager);
|
|
}
|
|
else if (strcmp (argv[0], "add_monitor") == 0)
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
MetaVirtualMonitor *monitor;
|
|
int width, height;
|
|
|
|
if (argc != 4)
|
|
BAD_COMMAND ("usage: %s <monitor-id> <width> <height>", argv[0]);
|
|
|
|
width = atoi (argv[2]);
|
|
height = atoi (argv[3]);
|
|
|
|
monitor = meta_create_test_monitor (test->context, width, height, 60.0);
|
|
meta_monitor_manager_reload (monitor_manager);
|
|
|
|
g_hash_table_insert (test->virtual_monitors, g_strdup (argv[1]), monitor);
|
|
}
|
|
else if (strcmp (argv[0], "assert_primary_monitor") == 0)
|
|
{
|
|
MetaVirtualMonitor *virtual_monitor;
|
|
MetaOutput *output;
|
|
MetaMonitor *monitor;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND ("usage: %s <monitor-id>", argv[0]);
|
|
|
|
virtual_monitor = g_hash_table_lookup (test->virtual_monitors, argv[1]);
|
|
if (!virtual_monitor)
|
|
BAD_COMMAND ("Unknown monitor %s", argv[1]);
|
|
|
|
output = meta_virtual_monitor_get_output (virtual_monitor);
|
|
monitor = meta_output_get_monitor (output);
|
|
|
|
if (!meta_monitor_is_primary (monitor))
|
|
{
|
|
g_set_error (error,
|
|
META_TEST_CLIENT_ERROR,
|
|
META_TEST_CLIENT_ERROR_ASSERTION_FAILED,
|
|
"Monitor %s is not the primary monitor", argv[1]);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (strcmp (argv[0], "reload_monitors") == 0)
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
|
|
if (argc != 1)
|
|
BAD_COMMAND ("usage: %s", argv[0]);
|
|
|
|
meta_monitor_manager_reload (monitor_manager);
|
|
}
|
|
else if (strcmp (argv[0], "num_workspaces") == 0)
|
|
{
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <num>", argv[0]);
|
|
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
uint32_t timestamp = meta_display_get_current_time_roundtrip (display);
|
|
int num = atoi (argv[1]);
|
|
meta_workspace_manager_update_num_workspaces (workspace_manager,
|
|
timestamp, num);
|
|
}
|
|
else if (strcmp (argv[0], "activate_workspace") == 0)
|
|
{
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <workspace-index>", argv[0]);
|
|
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
|
|
int index = atoi (argv[1]);
|
|
if (index >= meta_workspace_manager_get_n_workspaces (workspace_manager))
|
|
return FALSE;
|
|
|
|
MetaWorkspace *workspace =
|
|
meta_workspace_manager_get_workspace_by_index (workspace_manager,
|
|
index);
|
|
uint32_t timestamp = meta_display_get_current_time_roundtrip (display);
|
|
meta_workspace_activate (workspace, timestamp);
|
|
}
|
|
else if (strcmp (argv[0], "activate_workspace_with_focus") == 0)
|
|
{
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: %s <workspace-index> <window-id>", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[2], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
MetaWindow *window;
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
|
|
int index = atoi (argv[1]);
|
|
if (index >= meta_workspace_manager_get_n_workspaces (workspace_manager))
|
|
return FALSE;
|
|
|
|
MetaWorkspace *workspace =
|
|
meta_workspace_manager_get_workspace_by_index (workspace_manager,
|
|
index);
|
|
uint32_t timestamp = meta_display_get_current_time_roundtrip (display);
|
|
meta_workspace_activate_with_focus (workspace, window, timestamp);
|
|
}
|
|
else if (strcmp (argv[0], "assert_stacking_workspace") == 0)
|
|
{
|
|
if (argc < 2)
|
|
BAD_COMMAND("usage: %s <workspace-index> [<window-id1> ...]", argv[0]);
|
|
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
|
|
int index = atoi (argv[1]);
|
|
if (index >= meta_workspace_manager_get_n_workspaces (workspace_manager))
|
|
return FALSE;
|
|
|
|
MetaWorkspace *workspace =
|
|
meta_workspace_manager_get_workspace_by_index (workspace_manager,
|
|
index);
|
|
|
|
if (!test_case_assert_stacking (test, argv + 2, argc - 2, workspace, error))
|
|
return FALSE;
|
|
|
|
if (!test_case_check_xserver_stacking (test, error))
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (argv[0], "window_to_workspace") == 0)
|
|
{
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: %s <window-id> <workspace-index>", argv[0]);
|
|
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
MetaWindow *window;
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
MetaWorkspaceManager *workspace_manager =
|
|
meta_display_get_workspace_manager (display);
|
|
|
|
int index = atoi (argv[2]);
|
|
if (index >= meta_workspace_manager_get_n_workspaces (workspace_manager))
|
|
return FALSE;
|
|
|
|
MetaWorkspace *workspace =
|
|
meta_workspace_manager_get_workspace_by_index (workspace_manager,
|
|
index);
|
|
|
|
meta_window_change_workspace (window, workspace);
|
|
}
|
|
else if (strcmp (argv[0], "make_above") == 0)
|
|
{
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
MetaWindow *window;
|
|
gboolean make_above;
|
|
|
|
if (argc != 3 || !str_to_bool (argv[2], &make_above))
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
|
|
argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
if (make_above)
|
|
meta_window_make_above (window);
|
|
else
|
|
meta_window_unmake_above (window);
|
|
}
|
|
else if (strcmp (argv[0], "focus_default_window") == 0)
|
|
{
|
|
if (argc != 1)
|
|
BAD_COMMAND("usage: %s", argv[0]);
|
|
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
uint32_t timestamp = meta_display_get_current_time_roundtrip (display);
|
|
|
|
meta_display_focus_default_window (display, timestamp);
|
|
}
|
|
else if (strcmp (argv[0], "move_cursor_to") == 0)
|
|
{
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: %s <x> <y>", argv[0]);
|
|
|
|
float x = atof (argv[1]);
|
|
float y = atof (argv[2]);
|
|
|
|
clutter_virtual_input_device_notify_absolute_motion (test->pointer,
|
|
CLUTTER_CURRENT_TIME,
|
|
x, y);
|
|
meta_flush_input (test->context);
|
|
}
|
|
else if (strcmp (argv[0], "click") == 0)
|
|
{
|
|
if (argc != 1)
|
|
BAD_COMMAND("usage: %s", argv[0]);
|
|
|
|
clutter_virtual_input_device_notify_button (test->pointer,
|
|
CLUTTER_CURRENT_TIME,
|
|
CLUTTER_BUTTON_PRIMARY,
|
|
CLUTTER_BUTTON_STATE_PRESSED);
|
|
clutter_virtual_input_device_notify_button (test->pointer,
|
|
CLUTTER_CURRENT_TIME,
|
|
CLUTTER_BUTTON_PRIMARY,
|
|
CLUTTER_BUTTON_STATE_RELEASED);
|
|
meta_flush_input (test->context);
|
|
}
|
|
else if (strcmp (argv[0], "set_pref") == 0)
|
|
{
|
|
GSettings *wm;
|
|
GSettings *mutter;
|
|
|
|
if (argc != 3)
|
|
BAD_COMMAND("usage: %s <KEY> <VALUE>", argv[0]);
|
|
|
|
wm = g_settings_new ("org.gnome.desktop.wm.preferences");
|
|
g_assert_nonnull (wm);
|
|
mutter = g_settings_new ("org.gnome.mutter");
|
|
g_assert_nonnull (mutter);
|
|
|
|
if (strcmp (argv[1], "raise-on-click") == 0)
|
|
{
|
|
gboolean value;
|
|
if (!str_to_bool (argv[2], &value))
|
|
BAD_COMMAND("usage: %s %s [true|false]", argv[0], argv[1]);
|
|
|
|
g_assert_true (g_settings_set_boolean (wm, "raise-on-click", value));
|
|
}
|
|
else if (strcmp (argv[1], "focus-mode") == 0)
|
|
{
|
|
GDesktopFocusMode mode;
|
|
|
|
if (g_ascii_strcasecmp (argv[2], "click") == 0)
|
|
mode = G_DESKTOP_FOCUS_MODE_CLICK;
|
|
else if (g_ascii_strcasecmp (argv[2], "sloppy") == 0)
|
|
mode = G_DESKTOP_FOCUS_MODE_SLOPPY;
|
|
else if (g_ascii_strcasecmp (argv[2], "mouse") == 0)
|
|
mode = G_DESKTOP_FOCUS_MODE_MOUSE;
|
|
else
|
|
BAD_COMMAND("usage: %s %s [click|sloppy|mouse]", argv[0], argv[1]);
|
|
|
|
g_assert_true (g_settings_set_enum (wm, "focus-mode", mode));
|
|
}
|
|
else if (strcmp (argv[1], "workspaces-only-on-primary") == 0)
|
|
{
|
|
gboolean value;
|
|
if (!str_to_bool (argv[2], &value))
|
|
BAD_COMMAND("usage: %s %s [true|false]", argv[0], argv[1]);
|
|
|
|
g_assert_true (g_settings_set_boolean (mutter, "workspaces-only-on-primary", value));
|
|
}
|
|
else if (strcmp (argv[1], "focus-change-on-pointer-rest") == 0)
|
|
{
|
|
gboolean value;
|
|
if (!str_to_bool (argv[2], &value))
|
|
BAD_COMMAND("usage: %s %s [true|false]", argv[0], argv[1]);
|
|
|
|
g_assert_true (g_settings_set_boolean (mutter, "focus-change-on-pointer-rest", value));
|
|
}
|
|
else if (strcmp (argv[1], "auto-raise") == 0)
|
|
{
|
|
gboolean value;
|
|
if (!str_to_bool (argv[2], &value))
|
|
BAD_COMMAND("usage: %s %s [true|false]", argv[0], argv[1]);
|
|
|
|
g_assert_true (g_settings_set_boolean (wm, "auto-raise", value));
|
|
}
|
|
else if (strcmp (argv[1], "auto-raise-delay") == 0)
|
|
{
|
|
int value = atoi (argv[2]);
|
|
|
|
g_assert_true (g_settings_set_int (wm, "auto-raise-delay", value));
|
|
}
|
|
else {
|
|
BAD_COMMAND("Unknown preference %s", argv[1]);
|
|
}
|
|
}
|
|
else if (strcmp (argv[0], "toggle_overview") == 0)
|
|
{
|
|
MetaDisplay *display = meta_context_get_display (test->context);
|
|
|
|
if (argc != 1)
|
|
BAD_COMMAND ("usage: %s", argv[0]);
|
|
|
|
g_signal_emit_by_name (display, "overlay-key", 0);
|
|
}
|
|
else if (strcmp (argv[0], "clone") == 0)
|
|
{
|
|
MetaBackend *backend = meta_context_get_backend (test->context);
|
|
ClutterActor *stage = meta_backend_get_stage (backend);
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
MetaWindow *window;
|
|
MetaWindowActor *window_actor;
|
|
ClutterActor *clone;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
if (g_object_get_data (G_OBJECT (window), "test-clone"))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Already cloned");
|
|
return FALSE;
|
|
}
|
|
|
|
window_actor = meta_window_actor_from_window (window);
|
|
clone = clutter_clone_new (CLUTTER_ACTOR (window_actor));
|
|
clutter_actor_show (clone);
|
|
|
|
clutter_actor_add_child (stage, clone);
|
|
g_object_set_data (G_OBJECT (window), "test-clone", clone);
|
|
|
|
if (!test->cloned_windows)
|
|
{
|
|
test->cloned_windows = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, g_object_unref);
|
|
}
|
|
|
|
g_hash_table_insert (test->cloned_windows,
|
|
g_strdup (argv[1]), g_object_ref (window));
|
|
}
|
|
else if (strcmp (argv[0], "declone") == 0)
|
|
{
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
MetaWindow *live_window;
|
|
MetaWindow *window;
|
|
ClutterActor *clone;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = g_hash_table_lookup (test->cloned_windows, argv[1]);
|
|
g_assert_nonnull (window);
|
|
|
|
live_window = meta_test_client_find_window (client, window_id, NULL);
|
|
if (live_window)
|
|
g_assert_true (live_window == window);
|
|
|
|
if (!g_object_get_data (G_OBJECT (window), "test-clone"))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Wasn't cloned");
|
|
return FALSE;
|
|
}
|
|
|
|
clone = g_object_get_data (G_OBJECT (window), "test-clone");
|
|
clutter_actor_destroy (clone);
|
|
|
|
g_hash_table_remove (test->cloned_windows, argv[1]);
|
|
}
|
|
else if (strcmp (argv[0], "wait_for_effects") == 0)
|
|
{
|
|
MetaTestClient *client;
|
|
const char *window_id;
|
|
MetaWindow *window;
|
|
MetaWindowActor *window_actor;
|
|
|
|
if (argc != 2)
|
|
BAD_COMMAND("usage: %s <client-id>/<window-id>", argv[0]);
|
|
|
|
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
|
|
return FALSE;
|
|
|
|
window = meta_test_client_find_window (client, window_id, error);
|
|
if (!window)
|
|
return FALSE;
|
|
|
|
window_actor = meta_window_actor_from_window (window);
|
|
g_object_add_weak_pointer (G_OBJECT (window_actor),
|
|
(gpointer *) &window_actor);
|
|
while (window_actor && meta_window_actor_effect_in_progress (window_actor))
|
|
g_main_context_iteration (NULL, TRUE);
|
|
if (window_actor)
|
|
{
|
|
g_object_remove_weak_pointer (G_OBJECT (window_actor),
|
|
(gpointer *) &window_actor);
|
|
}
|
|
}
|
|
else if (argc > 2 && g_str_equal (argv[1], "=>"))
|
|
{
|
|
g_autoptr (GObject) signal_instance = NULL;
|
|
g_autofree char *signal_name = NULL;
|
|
TestCaseArgs *test_case_args;
|
|
|
|
if (!test_case_parse_signal (test, argc, argv,
|
|
&signal_name, &signal_instance, error))
|
|
return FALSE;
|
|
|
|
g_debug ("Connected to signal '%s' on object %p (%s)",
|
|
signal_name, signal_instance,
|
|
g_type_name_from_instance ((GTypeInstance *) signal_instance));
|
|
|
|
test_case_args = g_new0 (TestCaseArgs, 1);
|
|
test_case_args->test_case = test;
|
|
test_case_args->filename = filename;
|
|
test_case_args->line_no = line_no;
|
|
test_case_args->argc = argc - 2;
|
|
test_case_args->argv = g_strdupv (&argv[2]);
|
|
test_case_args->instance = signal_instance;
|
|
test_case_args->handler_id =
|
|
g_signal_connect_swapped (signal_instance,
|
|
signal_name,
|
|
G_CALLBACK (test_case_signal_cb),
|
|
test_case_args);
|
|
}
|
|
else
|
|
{
|
|
BAD_COMMAND("Unknown command %s", argv[0]);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
test_case_destroy (TestCase *test,
|
|
GError **error)
|
|
{
|
|
/* Failures when cleaning up the test case aren't recoverable, since we'll
|
|
* pollute the subsequent test cases, so we just return the error, and
|
|
* skip the rest of the cleanup.
|
|
*/
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
MetaDisplay *display;
|
|
|
|
if (test->cloned_windows)
|
|
{
|
|
g_assert_cmpuint (g_hash_table_size (test->cloned_windows), ==, 0);
|
|
g_hash_table_unref (test->cloned_windows);
|
|
}
|
|
|
|
g_hash_table_iter_init (&iter, test->clients);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
if (!meta_test_client_do (value, error, "destroy_all", NULL))
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (!test_case_wait (test, error))
|
|
return FALSE;
|
|
|
|
if (!test_case_assert_stacking (test, NULL, 0, NULL, error))
|
|
return FALSE;
|
|
|
|
g_hash_table_iter_init (&iter, test->clients);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
meta_test_client_destroy (value);
|
|
|
|
g_clear_pointer (&test->waiter, meta_async_waiter_destroy);
|
|
|
|
display = meta_context_get_display (test->context);
|
|
g_clear_signal_handler (&test->x11_display_opened_handler_id, display);
|
|
if (display->x11_display && test->alarm_filter)
|
|
{
|
|
meta_x11_display_remove_alarm_filter (display->x11_display,
|
|
test->alarm_filter);
|
|
}
|
|
|
|
g_hash_table_destroy (test->clients);
|
|
g_hash_table_unref (test->virtual_monitors);
|
|
g_object_unref (test->pointer);
|
|
g_free (test);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
static gboolean
|
|
run_test (MetaContext *context,
|
|
const char *filename,
|
|
int index)
|
|
{
|
|
TestCase *test = test_case_new (context);
|
|
g_autofree char *file_basename = NULL;
|
|
GError *error = NULL;
|
|
|
|
GFile *file = g_file_new_for_path (filename);
|
|
|
|
GDataInputStream *in = NULL;
|
|
|
|
GFileInputStream *in_raw = g_file_read (file, NULL, &error);
|
|
g_object_unref (file);
|
|
if (in_raw == NULL)
|
|
goto out;
|
|
|
|
in = g_data_input_stream_new (G_INPUT_STREAM (in_raw));
|
|
g_object_unref (in_raw);
|
|
|
|
file_basename = g_path_get_basename (filename);
|
|
|
|
int line_no = 0;
|
|
while (error == NULL)
|
|
{
|
|
char *line = g_data_input_stream_read_line_utf8 (in, NULL, NULL, &error);
|
|
if (line == NULL)
|
|
break;
|
|
|
|
line_no++;
|
|
|
|
int argc;
|
|
char **argv = NULL;
|
|
if (!g_shell_parse_argv (line, &argc, &argv, &error))
|
|
{
|
|
if (g_error_matches (error, G_SHELL_ERROR, G_SHELL_ERROR_EMPTY_STRING))
|
|
{
|
|
g_clear_error (&error);
|
|
goto next;
|
|
}
|
|
|
|
goto next;
|
|
}
|
|
|
|
test_case_do (test, file_basename, line_no, argc, argv, &error);
|
|
|
|
next:
|
|
if (error)
|
|
g_prefix_error (&error, "%d: ", line_no);
|
|
|
|
g_free (line);
|
|
g_strfreev (argv);
|
|
}
|
|
|
|
{
|
|
GError *tmp_error = NULL;
|
|
if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, &tmp_error))
|
|
{
|
|
if (error != NULL)
|
|
g_clear_error (&tmp_error);
|
|
else
|
|
g_propagate_error (&error, tmp_error);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (in != NULL)
|
|
g_object_unref (in);
|
|
|
|
GError *cleanup_error = NULL;
|
|
test_case_destroy (test, &cleanup_error);
|
|
|
|
const char *testspos = strstr (filename, "tests/");
|
|
char *pretty_name;
|
|
if (testspos)
|
|
pretty_name = g_strdup (testspos + strlen("tests/"));
|
|
else
|
|
pretty_name = g_strdup (filename);
|
|
|
|
if (error || cleanup_error)
|
|
{
|
|
g_print ("not ok %d %s\n", index, pretty_name);
|
|
|
|
if (error)
|
|
g_print (" %s\n", error->message);
|
|
|
|
if (cleanup_error)
|
|
{
|
|
g_print (" Fatal Error During Cleanup\n");
|
|
g_print (" %s\n", cleanup_error->message);
|
|
exit (1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_print ("ok %d %s\n", index, pretty_name);
|
|
}
|
|
|
|
g_free (pretty_name);
|
|
|
|
gboolean success = error == NULL;
|
|
|
|
g_clear_error (&error);
|
|
g_clear_error (&cleanup_error);
|
|
|
|
return success;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
int n_tests;
|
|
char **tests;
|
|
} RunTestsInfo;
|
|
|
|
static int
|
|
run_tests (MetaContext *context,
|
|
RunTestsInfo *info)
|
|
{
|
|
int i;
|
|
gboolean success = TRUE;
|
|
|
|
g_print ("1..%d\n", info->n_tests);
|
|
|
|
for (i = 0; i < info->n_tests; i++)
|
|
{
|
|
if (!run_test (context, info->tests[i], i + 1))
|
|
success = FALSE;
|
|
}
|
|
|
|
|
|
return success ? 0 : 1;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
static gboolean
|
|
find_metatests_in_directory (GFile *directory,
|
|
GPtrArray *results,
|
|
GError **error)
|
|
{
|
|
GFileEnumerator *enumerator = g_file_enumerate_children (directory,
|
|
"standard::name,standard::type",
|
|
G_FILE_QUERY_INFO_NONE,
|
|
NULL, error);
|
|
if (!enumerator)
|
|
return FALSE;
|
|
|
|
while (*error == NULL)
|
|
{
|
|
GFileInfo *info = g_file_enumerator_next_file (enumerator, NULL, error);
|
|
if (info == NULL)
|
|
break;
|
|
|
|
GFile *child = g_file_enumerator_get_child (enumerator, info);
|
|
switch (g_file_info_get_file_type (info))
|
|
{
|
|
case G_FILE_TYPE_REGULAR:
|
|
{
|
|
const char *name = g_file_info_get_name (info);
|
|
if (g_str_has_suffix (name, ".metatest"))
|
|
g_ptr_array_add (results, g_file_get_path (child));
|
|
break;
|
|
}
|
|
case G_FILE_TYPE_DIRECTORY:
|
|
find_metatests_in_directory (child, results, error);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_object_unref (child);
|
|
g_object_unref (info);
|
|
}
|
|
|
|
{
|
|
GError *tmp_error = NULL;
|
|
if (!g_file_enumerator_close (enumerator, NULL, &tmp_error))
|
|
{
|
|
if (*error != NULL)
|
|
g_clear_error (&tmp_error);
|
|
else
|
|
g_propagate_error (error, tmp_error);
|
|
}
|
|
}
|
|
|
|
g_object_unref (enumerator);
|
|
return *error == NULL;
|
|
}
|
|
|
|
static gboolean all_tests = FALSE;
|
|
|
|
const GOptionEntry options[] = {
|
|
{
|
|
"all", 0, 0, G_OPTION_ARG_NONE,
|
|
&all_tests,
|
|
"Run all installed tests",
|
|
NULL
|
|
},
|
|
{ NULL }
|
|
};
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
g_autoptr (MetaContext) context = NULL;
|
|
g_autoptr (GPtrArray) tests = NULL;
|
|
RunTestsInfo info;
|
|
|
|
context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS,
|
|
META_CONTEXT_TEST_FLAG_TEST_CLIENT);
|
|
|
|
meta_context_add_option_entries (context, options, NULL);
|
|
|
|
g_assert (meta_context_configure (context, &argc, &argv, NULL));
|
|
|
|
tests = g_ptr_array_new_with_free_func (g_free);
|
|
if (all_tests)
|
|
{
|
|
GFile *test_dir = g_file_new_for_path (MUTTER_PKGDATADIR "/tests");
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!find_metatests_in_directory (test_dir, tests, &error))
|
|
{
|
|
g_printerr ("Error enumerating tests: %s\n", error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
char *curdir = g_get_current_dir ();
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (g_path_is_absolute (argv[i]))
|
|
g_ptr_array_add (tests, g_strdup (argv[i]));
|
|
else
|
|
g_ptr_array_add (tests, g_build_filename (curdir, argv[i], NULL));
|
|
}
|
|
|
|
g_free (curdir);
|
|
}
|
|
|
|
info.tests = (char **) tests->pdata;
|
|
info.n_tests = tests->len;
|
|
g_signal_connect (context, "run-tests", G_CALLBACK (run_tests), &info);
|
|
|
|
return meta_context_test_run_tests (META_CONTEXT_TEST (context),
|
|
META_TEST_RUN_FLAG_NONE);
|
|
}
|