WIP: Boostrapped agl-client-shell tests. sandbox/mvlad/libweston10
authorMarius Vlad <marius.vlad@collabora.com>
Wed, 21 Apr 2021 20:27:28 +0000 (23:27 +0300)
committerMarius Vlad <marius.vlad@collabora.com>
Thu, 22 Apr 2021 13:37:10 +0000 (16:37 +0300)
Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
Change-Id: I6dd661c42e947aa1248f430f4c1bf618206fb247

meson.build
tests/agl-client-shell-test.c [new file with mode: 0644]
tests/agl-helper.c [new file with mode: 0644]
tests/agl-helper.h [new file with mode: 0644]
tests/meson.build [new file with mode: 0644]
tests/reference/agl_client_shell-00.png [new file with mode: 0644]

index 5a257b3..c3cb834 100644 (file)
@@ -281,3 +281,4 @@ install_data(
 
 common_inc = [ include_directories('src'), include_directories('.') ]
 subdir('clients')
+subdir('tests')
diff --git a/tests/agl-client-shell-test.c b/tests/agl-client-shell-test.c
new file mode 100644 (file)
index 0000000..045fbcd
--- /dev/null
@@ -0,0 +1,425 @@
+#include "config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <libweston-test/weston-test-client-helper.h>
+#include <libweston-test/weston-test-fixture-compositor.h>
+
+#include "agl-shell-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+#include "agl-screenshooter-client-protocol.h"
+
+#define WINDOW_WIDTH_SIZE      200
+#define WINDOW_HEIGHT_SIZE     200
+
+enum window_type {
+       BACKGROUND      = -1,
+       PANEL_TOP       = 0,
+       PANEL_BOTTOM    = 1,
+       PANEL_LEFT      = 2,
+       PANEL_RIGHT     = 3
+};
+
+pixman_color_t bg_color = {
+       .red   = 0x0000,
+       .green = 0x0000,
+       .blue  = 0xffff,
+       .alpha = 0xffff
+};
+
+pixman_color_t panel_top_color = {
+       .red   = 0xffff,
+       .green = 0x0000,
+       .blue  = 0x0000,
+       .alpha = 0xffff
+};
+
+pixman_color_t panel_bottom_color = {
+       .red   = 0x0000,
+       .green = 0xffff,
+       .blue  = 0x0000,
+       .alpha = 0xffff
+};
+
+struct display {
+       struct agl_shell *agl_shell;
+       struct xdg_wm_base *wm_base;
+       struct agl_screenshooter *screenshooter;
+       struct client *client;
+       struct wl_list win_list;
+       bool buffer_copy_done;
+};
+
+struct window {
+       struct display *display;
+       struct xdg_toplevel *xdg_toplevel;
+       struct xdg_surface *xdg_surface;
+       struct wl_surface *surface;
+       struct buffer *buffer;
+
+       bool wait_for_configure;
+
+       int width;
+       int height;
+       bool maximized;
+       bool fullscreen;
+       enum window_type w_type;
+
+       struct wl_list link;
+};
+
+static struct buffer *
+agl_capture_screenshot_of_output(void *data);
+
+static enum test_result_code
+fixture_setup(struct weston_test_harness *harness)
+{
+       struct compositor_setup setup;
+
+       compositor_setup_defaults(&setup);
+       setup.renderer = RENDERER_PIXMAN;
+       setup.width = 1920;
+       setup.height = 1080;
+       setup.shell = SHELL_EMBEDDED;
+       setup.extra_args = "--debug";
+
+       return weston_test_harness_execute_as_client(harness, &setup);
+}
+
+DECLARE_FIXTURE_SETUP(fixture_setup);
+
+static struct window *
+create_window(int width, int height)
+{
+       struct window *window = calloc(1, sizeof(*window));
+
+       window->width = width;
+       window->height = height;
+
+       return window;
+}
+
+static struct display *
+create_display(struct client *client, struct xdg_wm_base *wm_base,
+              struct agl_shell *agl_shell, struct agl_screenshooter *screenshooter)
+{
+       struct display *display = calloc(1, sizeof(*display));
+
+       display->client = client;
+       display->wm_base = wm_base;
+       display->agl_shell = agl_shell;
+       display->screenshooter = screenshooter;
+
+       return display;
+}
+
+static void
+xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
+{
+       xdg_wm_base_pong(shell, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+       xdg_wm_base_ping,
+};
+
+static void
+draw(struct window *window, pixman_color_t color)
+{
+       struct client *client = window->display->client;
+
+       testlog("Creating a buffer with %dx%d\n", window->width, window->height);
+       window->buffer =
+               create_shm_buffer_a8r8g8b8(client, window->width, window->height);
+       fill_image_with_color(window->buffer->image, &color);
+
+       wl_surface_attach(window->surface, window->buffer->proxy, 0, 0);
+       wl_surface_damage(window->surface, 0, 0, window->width, window->height);
+
+       wl_surface_commit(window->surface);
+}
+
+static void
+handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial)
+{
+        struct window *window = data;
+        xdg_surface_ack_configure(surface, serial);
+
+       if (window->wait_for_configure) {
+               switch (window->w_type) {
+               case BACKGROUND:
+                       draw(window, bg_color);
+                       break;
+               case PANEL_TOP:
+                       draw(window, panel_top_color);
+                       break;
+               case PANEL_BOTTOM:
+                       draw(window, panel_bottom_color);
+                       break;
+               case PANEL_LEFT:
+               case PANEL_RIGHT:
+                       break;
+               }
+               window->wait_for_configure = false;
+       }
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+       handle_xdg_surface_configure,
+};
+
+static void
+handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
+                              int32_t width, int32_t height, struct wl_array *states)
+{
+       struct window *window = data;
+       uint32_t *p;
+
+       window->fullscreen = 0;
+       window->maximized = 0;
+
+       wl_array_for_each(p, states) {
+               uint32_t state = *p;
+               switch (state) {
+               case XDG_TOPLEVEL_STATE_FULLSCREEN:
+                       window->fullscreen = 1;
+                       break;
+               case XDG_TOPLEVEL_STATE_MAXIMIZED:
+                       window->maximized = 1;
+                       break;
+               }
+       }
+
+       if (width > 0 && height > 0) {
+               if (!window->fullscreen && !window->maximized) {
+                       window->width = width;
+                       window->height = height;
+               }
+               window->width = width;
+               window->height = height;
+       } else if (!window->fullscreen && !window->maximized) {
+               if (width == 0)
+                       window->width = WINDOW_WIDTH_SIZE;
+               else
+                       window->width = width;
+
+               if (height == 0)
+                       window->height = WINDOW_HEIGHT_SIZE;
+               else
+                       window->height = height;
+       }
+
+       /* if we've been resized set wait_for_configure to adjust the fb size
+        * in the frame callback handler, which will also clear this up */
+       if ((window->width > 0 && window->width != WINDOW_WIDTH_SIZE) &&
+           (window->height > 0 && window->height != WINDOW_HEIGHT_SIZE)) {
+               window->wait_for_configure = true;
+       }
+}
+
+static void
+handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
+{
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+        handle_xdg_toplevel_configure,
+        handle_xdg_toplevel_close,
+};
+
+
+static struct window *
+setup_agl_shell_client_bg(struct display *display)
+{
+       struct window *window;
+       window = create_window(200, 200);
+
+       window->display = display;
+
+       xdg_wm_base_add_listener(display->wm_base, &xdg_wm_base_listener, display);
+
+       window->surface =
+               wl_compositor_create_surface(display->client->wl_compositor);
+       window->xdg_surface =
+               xdg_wm_base_get_xdg_surface(display->wm_base, window->surface);
+       assert(window->xdg_surface);
+
+       xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
+       window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
+       assert(window->xdg_toplevel);
+
+       xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, window);
+
+       xdg_toplevel_set_title(window->xdg_toplevel, "bg");
+       xdg_toplevel_set_app_id(window->xdg_toplevel, "bg");
+
+       wl_surface_commit(window->surface);
+
+       window->wait_for_configure = true;
+
+       agl_shell_set_background(display->agl_shell, window->surface,
+                                display->client->output->wl_output);
+
+       window->w_type = BACKGROUND;
+       return window;
+}
+
+static struct window *
+setup_agl_shell_client_panel(struct display *display, enum agl_shell_edge edge)
+{
+       struct window *window;
+       window = create_window(200, 200);
+
+       window->display = display;
+
+       xdg_wm_base_add_listener(display->wm_base,
+                                &xdg_wm_base_listener, display);
+
+       window->surface =
+               wl_compositor_create_surface(display->client->wl_compositor);
+       window->xdg_surface =
+               xdg_wm_base_get_xdg_surface(display->wm_base, window->surface);
+       assert(window->xdg_surface);
+
+       xdg_surface_add_listener(window->xdg_surface,
+                                &xdg_surface_listener, window);
+       window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
+       assert(window->xdg_toplevel);
+
+       xdg_toplevel_add_listener(window->xdg_toplevel,
+                                 &xdg_toplevel_listener, window);
+
+       switch (edge) {
+       case AGL_SHELL_EDGE_TOP:
+               xdg_toplevel_set_title(window->xdg_toplevel, "panel top");
+               xdg_toplevel_set_app_id(window->xdg_toplevel, "panel top");
+               break;
+       case AGL_SHELL_EDGE_BOTTOM:
+               xdg_toplevel_set_title(window->xdg_toplevel, "panel bottom");
+               xdg_toplevel_set_app_id(window->xdg_toplevel, "panel bottom");
+               break;
+       case AGL_SHELL_EDGE_LEFT:
+       case AGL_SHELL_EDGE_RIGHT:
+               break;
+       }
+
+       wl_surface_commit(window->surface);
+
+       window->wait_for_configure = true;
+
+       agl_shell_set_panel(display->agl_shell, window->surface,
+                           display->client->output->wl_output, edge);
+
+       window->w_type = (enum window_type) edge;
+       return window;
+}
+
+static void
+screenshot_done(void *data, struct agl_screenshooter *screenshooter, uint32_t status)
+{
+       struct display *display = data;
+       display->buffer_copy_done = true;
+}
+
+static const struct agl_screenshooter_listener screenshooter_listener = {
+       screenshot_done
+};
+
+static struct buffer *
+agl_capture_screenshot_of_output(void *data)
+{
+       struct display *display = data;
+       struct client *client = display->client;
+       struct buffer *buffer;
+
+       buffer = create_shm_buffer_a8r8g8b8(client,
+                                           client->output->width,
+                                           client->output->height);
+
+       display->buffer_copy_done = false;
+       agl_screenshooter_take_shot(display->screenshooter,
+                                   client->output->wl_output,
+                                   buffer->proxy);
+
+       while (display->buffer_copy_done == false)
+               assert(wl_display_dispatch(client->wl_display) >= 0);
+
+       return buffer;
+}
+
+static struct display *
+setup_agl_shell_client(struct client *client)
+{
+       struct display *display;
+       struct agl_shell *agl_shell;
+       struct xdg_wm_base *wm_base;
+       struct agl_screenshooter *screenshooter;
+
+       setenv("WESTON_TEST_REFERENCE_PATH", "../tests/reference", 1);
+       wm_base = bind_to_singleton_global(client, &xdg_wm_base_interface, 1);
+       assert(wm_base);
+
+       agl_shell = bind_to_singleton_global(client, &agl_shell_interface, 1);
+       assert(agl_shell);
+
+       screenshooter = bind_to_singleton_global(client, &agl_screenshooter_interface, 1);
+       assert(screenshooter);
+
+       display = create_display(client, wm_base, agl_shell, screenshooter);
+       wl_list_init(&display->win_list);
+
+       client_set_screenshoot(client, agl_capture_screenshot_of_output, display);
+       agl_screenshooter_add_listener(screenshooter, &screenshooter_listener, display);
+
+       struct window *win_bg = setup_agl_shell_client_bg(display);
+       wl_list_insert(&display->win_list, &win_bg->link);
+
+       struct window *win_panel_top =
+               setup_agl_shell_client_panel(display, AGL_SHELL_EDGE_TOP);
+       wl_list_insert(&display->win_list, &win_panel_top->link);
+
+       struct window *win_panel_bottom =
+               setup_agl_shell_client_panel(display, AGL_SHELL_EDGE_BOTTOM);
+       wl_list_insert(&display->win_list, &win_panel_bottom->link);
+
+       client_roundtrip(client);
+
+       /* send ready() */
+       agl_shell_ready(agl_shell);
+       return display;
+}
+
+static void
+display_destroy(struct display *display)
+{
+       struct window *win, *win_next;
+
+       wl_list_for_each_safe(win, win_next, &display->win_list, link) {
+               wl_list_remove(&win->link);
+               free(win);
+       }
+}
+
+TEST(agl_client_shell)
+{
+       struct display *display;
+       struct client *client = create_client();
+       bool match;
+
+       assert(client);
+
+       /* Create the client */
+       testlog("Creating client shell for agl-shell\n");
+       display = setup_agl_shell_client(client);
+
+       client_roundtrip(client);
+
+       /* take a screenshot and compare it with reference ->
+        * agl-shell client shell works */
+       match = verify_screen_content(client, "agl_client_shell", 0, NULL, 0);
+       assert(match);
+
+       client_destroy(client);
+       display_destroy(display);
+}
diff --git a/tests/agl-helper.c b/tests/agl-helper.c
new file mode 100644 (file)
index 0000000..94cd290
--- /dev/null
@@ -0,0 +1 @@
+#include "agl-helper.h"
diff --git a/tests/agl-helper.h b/tests/agl-helper.h
new file mode 100644 (file)
index 0000000..af4756a
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef __AGL_HELPER_C
+#define __AGL_HELPER_C
+
+#endif
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644 (file)
index 0000000..90ff73d
--- /dev/null
@@ -0,0 +1,35 @@
+dep_test_client = dependency('libweston-test-10')
+dep_test_client_runner = dependency('libweston-test-runner-10')
+
+# FIXME: extra depends should be actually be added here
+tests = [
+    { 'name': 'agl-client-shell' },
+]
+
+foreach t : tests
+       t_name = 'test-' + t.get('name')
+       t_sources = t.get('sources', [t.get('name') + '-test.c'])
+       t_sources += [ agl_shell_client_protocol_h, agl_shell_protocol_c,
+                      xdg_shell_client_protocol_h, xdg_shell_protocol_c,
+                      agl_screenshooter_client_protocol_h,
+                      agl_screenshooter_protocol_c ]
+
+       t_deps = [ dep_test_client, dep_test_client_runner,
+                   libweston_dep, dep_libexec_compositor ]
+       t_deps += t.get('dep_objs', [])
+
+       t_exe = executable(
+               t_name,
+               t_sources,
+               c_args: [
+                       '-DUNIT_TEST',
+                       '-DTHIS_TEST_NAME="' + t_name + '"',
+               ],
+               build_by_default: true,
+               include_directories: common_inc,
+               dependencies: t_deps,
+               install: false,
+       )
+
+       test(t.get('name'), t_exe, depends: t.get('test_deps', []))
+endforeach
diff --git a/tests/reference/agl_client_shell-00.png b/tests/reference/agl_client_shell-00.png
new file mode 100644 (file)
index 0000000..80dcaf9
Binary files /dev/null and b/tests/reference/agl_client_shell-00.png differ