Skip to content
Commits on Source (6)
  • Daniel Stone's avatar
    build: Avoid Meson warning for run_command() without check · b510f4a9
    Daniel Stone authored
    
    
    Apparently the old behaviour was to silently succeed if program execution
    failed. Setting check: true not only avoids a Meson deprecation warning
    for not passing it, but gives us a more clear indication what goes on
    when, e.g. breathe doesn't run.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    b510f4a9
  • Daniel Stone's avatar
    tests: Remove single case for device destroy test · ae2c4704
    Daniel Stone authored
    
    
    Currently we have some device tests which run in a single iteration
    once, then in lots of iterations after that.
    
    The single-iteration case is useless, so remove it, which has the happy
    side effect of not breaking when we change the test signature.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    ae2c4704
  • Daniel Stone's avatar
    tests: Pass wet_testsuite_data to test runs · fec581ab
    Daniel Stone authored
    
    
    Make sure every test handler now gets a copy of wet_testsuite_data,
    which we'll later use for client<->compositor synchronisation.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    fec581ab
  • Daniel Stone's avatar
    tests: Track weston_outputs in weston-test plugin · 68cfc121
    Daniel Stone authored
    
    
    Keep a tracking set of every weston_output created by the compositor,
    and use this to listen to the repaint signal.
    
    This currently does nothing, but will later be used to listen to repaint
    signals as a client breakpoint type.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    68cfc121
  • Daniel Stone's avatar
    tests: Add client<->compositor breakpoint support · efde2fa0
    Daniel Stone authored
    
    
    Add support for clients to request the server insert breakpoints at
    various points in its processing. These breakpoints are handled
    internally by semaphores (visible to tests through helpers): when the
    server reaches the specified point, it will pause execution until the
    client allows it to restart.
    
    A weston_compositor pointer returned at each breakpoint allows the
    client to reach across the thread boundary and access the server's
    internal data structures. This can be used to, for example, inspect
    paint nodes, internal damage, or any other work which is not necessarily
    client-visible.
    
    The majority of tests will not need to use this infrastructure; it is
    only intended for tightly-coupled tests which can very specifically
    dictate and anticipate the server's execution flow.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    efde2fa0
  • Daniel Stone's avatar
    tests: Add paint-node test · ce113969
    Daniel Stone authored
    
    
    This is a very simple test, mostly intended as a demonstration of the
    new client<->compositor breakpoint infrastructure. It ensures that for a
    simple test surface, a paint node has been created in the output's
    paint-node list, reflecting the properties of the attached buffer.
    
    This is an example of properties which are not observable by regular
    clients.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    ce113969
......@@ -3,9 +3,9 @@ doxygen = find_program('doxygen', required: true)
dot = find_program('dot', required: true)
breathe = find_program('breathe-apidoc', required: true)
sphinx_c = run_command(sphinx, '--version')
breathe_c = run_command(breathe, '--version')
doxygen_c = run_command(doxygen, '--version')
sphinx_c = run_command(sphinx, '--version', check: true)
breathe_c = run_command(breathe, '--version', check: true)
doxygen_c = run_command(doxygen, '--version', check: true)
sphinx_v = sphinx_c.stdout().split(' ')[1].strip()
breathe_v = breathe_c.stdout().split(' ')[2].strip()
......
......@@ -95,6 +95,23 @@
<arg name="y" type="fixed"/>
<arg name="touch_type" type="uint"/>
</request>
<enum name="breakpoint">
<entry name="post_repaint" value="0"
summary="after output repaint (filter type: wl_output)"/>
</enum>
<request name="client_break">
<description summary="request compositor pause at a certain point">
Request that the compositor pauses execution at a certain point. When
execution is paused, the compositor will signal the shared semaphore
to the client.
</description>
<arg name="breakpoint" type="uint" enum="breakpoint"
summary="event type to wait for" />
<arg name="resource_id" type="uint"
summary="optional Wayland resource ID to filter for (type-specific)"/>
</request>
</interface>
<interface name="weston_test_runner" version="1">
......
......@@ -152,7 +152,8 @@ TEST(multiple_device_add_and_remove)
client_destroy(cl);
}
TEST(device_release_before_destroy)
static void
device_release_before_destroy(void)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
......@@ -195,15 +196,12 @@ TEST(device_release_before_destroy_multiple)
{
int i;
/* if weston crashed during this test, then there is
* some inconsistency */
for (i = 0; i < 30; ++i) {
for (i = 0; i < 30; ++i)
device_release_before_destroy();
}
}
/* normal work-flow test */
TEST(device_release_after_destroy)
static void
device_release_after_destroy(void)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
......@@ -247,17 +245,15 @@ TEST(device_release_after_destroy_multiple)
{
int i;
/* if weston crashed during this test, then there is
* some inconsistency */
for (i = 0; i < 30; ++i) {
for (i = 0; i < 30; ++i)
device_release_after_destroy();
}
}
/* see https://bugzilla.gnome.org/show_bug.cgi?id=745008
* It is a mutter bug, but highly relevant. Weston does not
* suffer from this bug atm, but it is worth of testing. */
TEST(get_device_after_destroy)
static void
get_device_after_destroy(void)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
struct wl_pointer *wl_pointer;
......
......@@ -15,6 +15,7 @@ lib_test_runner = static_library(
dep_libweston_private_h_deps,
dep_wayland_client,
dep_libdl,
dep_threads,
],
include_directories: common_inc,
install: false,
......@@ -46,6 +47,7 @@ lib_test_client = static_library(
dep_libweston_private,
dep_libdrm_headers,
dep_pixman,
dep_threads,
dependency('cairo'),
],
install: false,
......@@ -60,6 +62,7 @@ dep_test_client = declare_dependency(
dep_test_runner,
dep_pixman,
dep_libdrm_headers,
dep_threads,
dependency('libudev', version: '>= 136'),
]
)
......@@ -204,6 +207,7 @@ tests = [
{ 'name': 'output-decorations', },
{ 'name': 'output-transforms', },
{ 'name': 'plugin-registry', },
{ 'name': 'paint-node', },
{
'name': 'pointer',
'sources': [
......
/*
* Copyright © 2016-2023 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include "libweston-internal.h"
#include "weston-test-client-helper.h"
#include "weston-test-fixture-compositor.h"
struct setup_args {
struct fixture_metadata meta;
enum weston_renderer_type renderer;
};
static const struct setup_args my_setup_args[] = {
{
.renderer = WESTON_RENDERER_PIXMAN,
.meta.name = "pixman"
},
{
.renderer = WESTON_RENDERER_GL,
.meta.name = "GL"
},
};
static enum test_result_code
fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.renderer = arg->renderer;
setup.width = 320;
setup.height = 240;
setup.shell = SHELL_TEST_DESKTOP;
setup.logging_scopes = "log,test-harness-plugin";
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta);
static struct buffer *
surface_commit_color(struct client *client, struct wl_surface *surface,
pixman_color_t *color, int width, int height)
{
struct buffer *buf;
buf = create_shm_buffer_a8r8g8b8(client, width, height);
fill_image_with_color(buf->image, color);
wl_surface_attach(surface, buf->proxy, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, width, height);
wl_surface_commit(surface);
return buf;
}
#define DECLARE_LIST_ITERATOR(name, parent, list, child, link) \
static child * \
next_##name(parent *from, child *pos) \
{ \
struct wl_list *entry = pos ? &pos->link : &from->list; \
if (entry->next == &from->list) \
return NULL; \
return container_of(entry->next, child, link); \
}
DECLARE_LIST_ITERATOR(output, struct weston_compositor, output_list,
struct weston_output, link);
DECLARE_LIST_ITERATOR(pnode_from_z, struct weston_output, paint_node_z_order_list,
struct weston_paint_node, z_order_link);
TEST(top_surface_present_in_output_repaint)
{
struct wet_testsuite_data *suite_data = TEST_GET_SUITE_DATA();
struct client *client;
struct buffer *buf;
pixman_color_t red;
color_rgb888(&red, 255, 0, 0);
client = create_client_and_test_surface(100, 50, 100, 100);
assert(client);
/* move the pointer clearly away from our screenshooting area */
weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 2, 30);
client_push_breakpoint(client, suite_data,
WESTON_TEST_BREAKPOINT_POST_REPAINT,
(struct wl_proxy *) client->output->wl_output);
buf = surface_commit_color(client, client->surface->wl_surface, &red, 100, 100);
RUN_INSIDE_BREAKPOINT(client, suite_data) {
struct weston_compositor *compositor;
struct weston_output *output;
struct weston_head *head;
struct weston_paint_node *pnode;
struct weston_view *view;
struct weston_surface *surface;
struct weston_buffer *buffer;
assert(breakpoint->template_->breakpoint ==
WESTON_TEST_BREAKPOINT_POST_REPAINT);
compositor = breakpoint->compositor;
head = breakpoint->resource;
output = next_output(compositor, NULL);
assert(output == head->output);
assert(strcmp(output->name, "headless") == 0);
assert(!next_output(compositor, output));
/* check that our surface is top of the paint node list */
pnode = next_pnode_from_z(output, NULL);
assert(pnode);
view = pnode->view;
surface = view->surface;
buffer = surface->buffer_ref.buffer;
assert(surface->resource);
assert(wl_resource_get_client(surface->resource) ==
suite_data->wl_client);
assert(weston_view_is_mapped(view));
assert(weston_surface_is_mapped(surface));
assert(surface->width == 100);
assert(surface->height == 100);
assert(buffer->width == surface->width);
assert(buffer->height == surface->height);
assert(buffer->type == WESTON_BUFFER_SHM);
}
buffer_destroy(buf);
client_destroy(client);
}
......@@ -27,6 +27,7 @@
#include "config.h"
#include <semaphore.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
......@@ -2085,3 +2086,162 @@ color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b)
return tmp;
}
/**
* Asks the server to wait for a specified breakpoint the next time it occurs,
* synchronized as part of the protocol stream.
*
* \param client Client structure
* \param suite_data Test suite data structure
* \param breakpoint Breakpoint to stop at
* \param proxy Optional breakpoint-specific object to filter by
*/
void
client_push_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
enum weston_test_breakpoint breakpoint,
struct wl_proxy *proxy)
{
weston_test_client_break(client->test->weston_test, breakpoint,
proxy ? wl_proxy_get_id(proxy) : 0);
}
/**
* Waits for the server's next breakpoint and returns control to the client.
* Must have had a corresponding client_push_breakpoint() call made before.
*
* May only be called after a weston_test.breakpoint request has been issued,
* or within a break, before client_release_breakpoint() has been called.
*
* \param client Client structure
* \param suite_data Test suite data structure
* \return Information about the active breakpoint
*/
struct wet_test_active_breakpoint *
client_wait_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data)
{
struct wet_test_active_breakpoint *active_bp;
assert(suite_data);
assert(!suite_data->breakpoints.in_client_break);
wl_display_flush(client->wl_display);
wet_test_wait_sem(&suite_data->breakpoints.client_break);
active_bp = suite_data->breakpoints.active_bp;
assert(active_bp);
suite_data->breakpoints.in_client_break = true;
return active_bp;
}
static void *
get_resource_data_from_proxy(struct wet_testsuite_data *suite_data,
struct wl_proxy *proxy)
{
struct wl_resource *resource;
assert(suite_data->breakpoints.in_client_break);
if (!proxy)
return NULL;
resource = wl_client_get_object(suite_data->wl_client,
wl_proxy_get_id(proxy));
assert(resource);
return wl_resource_get_user_data(resource);
}
/**
* Asks the server to wait for a specified breakpoint the next time it occurs,
* inserted immediately into the wait list with no synchronization to the
* protocol stream.
*
* Must only be called between client_wait_breakpoint() and
* client_release_breakpoint().
*
* \param client Client structure
* \param suite_data Test suite data structure
* \param breakpoint Breakpoint to stop at
* \param proxy Optional breakpoint-specific object to filter by
*/
void
client_insert_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
enum weston_test_breakpoint breakpoint,
struct wl_proxy *proxy)
{
struct wet_test_pending_breakpoint *bp;
assert(suite_data->breakpoints.in_client_break);
bp = xzalloc(sizeof(*bp));
bp->breakpoint = breakpoint;
bp->resource = get_resource_data_from_proxy(suite_data, proxy);
wl_list_insert(&suite_data->breakpoints.list, &bp->link);
}
/**
* Removes a specified breakpoint from the server's breakpoint list,
* with no synchronization to the protocol stream.
*
* Must only be called between client_wait_breakpoint() and
* client_release_breakpoint().
*
* \param client Client structure
* \param suite_data Test suite data structure
* \param breakpoint Breakpoint to remove
* \param proxy Optional breakpoint-specific object to filter by
*/
void
client_remove_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
enum weston_test_breakpoint breakpoint,
struct wl_proxy *proxy)
{
struct wet_test_pending_breakpoint *bp, *tmp;
void *resource = get_resource_data_from_proxy(suite_data, proxy);
assert(suite_data->breakpoints.in_client_break);
wl_list_for_each_safe(bp, tmp, &suite_data->breakpoints.list,
link) {
if (bp->breakpoint != breakpoint)
continue;
if (bp->resource != resource)
continue;
assert(bp != suite_data->breakpoints.active_bp->template_);
wl_list_remove(&bp->link);
free(bp);
return;
}
assert(!"couldn't find breakpoint to remove");
}
/**
* Continues server execution after a breakpoint.
*
* \param client Client structure
* \param suite_data Test suite data structure
* \param active_bp Data structure returned from client_wait_breakpoint()
*/
void
client_release_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
struct wet_test_active_breakpoint *active_bp)
{
assert(suite_data->breakpoints.active_bp == active_bp);
if (active_bp->rearm_on_release) {
wl_list_insert(&suite_data->breakpoints.list,
&active_bp->template_->link);
} else {
free(active_bp->template_);
}
free(active_bp);
suite_data->breakpoints.active_bp = NULL;
suite_data->breakpoints.in_client_break = false;
wet_test_post_sem(&suite_data->breakpoints.server_release);
}
......@@ -39,6 +39,7 @@
#include "weston-test-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "weston-output-capture-client-protocol.h"
#include "weston-testsuite-data.h"
struct client {
struct wl_display *wl_display;
......@@ -320,4 +321,42 @@ fill_image_with_color(pixman_image_t *image, const pixman_color_t *color);
pixman_color_t *
color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b);
/* Helper to wait for the next breakpoint and execute inside it; as this is a
* for loop, continue/break/etc will not affect an enclosing scope! */
#define RUN_INSIDE_BREAKPOINT(client_, suite_data_) \
for (struct wet_test_active_breakpoint *breakpoint = \
client_wait_breakpoint(client_, suite_data_); \
suite_data_->breakpoints.in_client_break; \
client_release_breakpoint(client_, suite_data_, breakpoint))
/* Specifies that the currently-executing breakpoint should be rearmed */
#define REARM_BREAKPOINT(breakpoint_) breakpoint_->rearm_on_release = true
void
client_push_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
enum weston_test_breakpoint breakpoint,
struct wl_proxy *proxy);
struct wet_test_active_breakpoint *
client_wait_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data);
void
client_insert_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
enum weston_test_breakpoint breakpoint,
struct wl_proxy *proxy);
void
client_remove_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
enum weston_test_breakpoint breakpoint,
struct wl_proxy *proxy);
void
client_release_breakpoint(struct client *client,
struct wet_testsuite_data *suite_data,
struct wet_test_active_breakpoint *active_bp);
#endif
......@@ -150,7 +150,8 @@ find_test(const char *name)
}
static enum test_result_code
run_test(int fixture_nr, const struct weston_test_entry *t, void *data,
run_test(struct wet_testsuite_data *suite_data, int fixture_nr,
const struct weston_test_entry *t, void *data,
int iteration)
{
struct weston_test_run_info info;
......@@ -165,7 +166,7 @@ run_test(int fixture_nr, const struct weston_test_entry *t, void *data,
info.fixture_nr = fixture_nr;
test_run_info_ = &info;
t->run(data);
t->run(suite_data, data);
test_run_info_ = NULL;
/*
......@@ -277,10 +278,11 @@ run_case(struct wet_testsuite_data *suite_data,
suite_data->fixture_name, t->name, iteration_nr);
if (suite_data->type == TEST_TYPE_PLUGIN) {
ret = run_test(fixture_nr, t, suite_data->compositor,
ret = run_test(suite_data, fixture_nr, t, suite_data->compositor,
iteration);
} else {
ret = run_test(fixture_nr, t, (void *)test_data, iteration);
ret = run_test(suite_data, fixture_nr, t, (void *)test_data,
iteration);
}
switch (ret) {
......
......@@ -29,6 +29,7 @@
#include "config.h"
#include <semaphore.h>
#include <stdlib.h>
#include <wayland-util.h>
......@@ -51,17 +52,20 @@
*/
struct weston_test_entry {
const char *name;
void (*run)(void *);
void (*run)(struct wet_testsuite_data *, void *);
const void *table_data;
size_t element_size;
int n_elements;
} __attribute__ ((aligned (64)));
#define TEST_BEGIN(name, arg) \
static void name(arg)
static void name(struct wet_testsuite_data *_wet_suite_data, arg)
#define TEST_BEGIN_NO_ARG(name) \
static void name(struct wet_testsuite_data *_wet_suite_data)
#define TEST_COMMON(func, name, data, size, n_elem) \
static void func(void *); \
static void func(struct wet_testsuite_data *, void *); \
\
const struct weston_test_entry test##name \
__attribute__ ((used, section ("test_section"))) = \
......@@ -71,14 +75,15 @@ struct weston_test_entry {
#define NO_ARG_TEST(name) \
TEST_COMMON(wrap##name, name, NULL, 0, 1) \
static void name(void); \
static void wrap##name(void *data) \
static void name(struct wet_testsuite_data *); \
static void wrap##name(struct wet_testsuite_data *_wet_suite_data,\
void *data) \
{ \
(void) data; \
name(); \
name(_wet_suite_data); \
} \
\
TEST_BEGIN(name, void)
TEST_BEGIN_NO_ARG(name)
#define ARG_TEST(name, test_data) \
TEST_COMMON(name, name, test_data, \
......@@ -128,15 +133,27 @@ struct weston_test_entry {
*
* \ingroup testharness
*/
#define PLUGIN_TEST(name) \
#define PLUGIN_TEST(name) \
TEST_COMMON(wrap##name, name, NULL, 0, 1) \
static void name(struct weston_compositor *); \
static void wrap##name(void *compositor) \
static void name(struct wet_testsuite_data *, \
struct weston_compositor *); \
static void wrap##name(struct wet_testsuite_data *_wet_suite_data,\
void *compositor) \
{ \
name(compositor); \
name(_wet_suite_data, compositor); \
} \
TEST_BEGIN(name, struct weston_compositor *compositor)
/** Get test suite data structure
*
* This returns the shared test suite data structure, to be used in
* any test which is declared with TEST(), TEST_P(), or PLUGIN_TEST().
*
* \return Test suite data structure
* \ingroup testharness
*/
#define TEST_GET_SUITE_DATA() _wet_suite_data
void
testlog(const char *fmt, ...) WL_PRINTF(1, 2);
......
......@@ -34,6 +34,7 @@
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <libweston/libweston.h>
#include <libweston/weston-log.h>
......@@ -46,6 +47,7 @@
#include "shared/helpers.h"
#include "shared/timespec-util.h"
#include "shared/xalloc.h"
#define MAX_TOUCH_DEVICES 32
......@@ -63,6 +65,10 @@ struct weston_test {
pthread_t client_thread;
struct wl_event_source *client_source;
struct wl_list output_list;
struct wl_listener output_created_listener;
struct wl_listener output_destroyed_listener;
};
struct weston_test_surface {
......@@ -73,6 +79,100 @@ struct weston_test_surface {
struct weston_test *test;
};
struct weston_test_output {
struct weston_test *test;
struct weston_output *output;
struct wl_listener repaint_listener;
struct wl_list link;
};
static void
maybe_breakpoint(struct weston_test *test,
enum weston_test_breakpoint breakpoint,
void *resource)
{
struct wet_test_pending_breakpoint *bp, *tmp;
struct wet_testsuite_data *tsd = weston_compositor_get_test_data(test->compositor);
wl_list_for_each_safe(bp, tmp, &tsd->breakpoints.list, link) {
struct wet_test_active_breakpoint *active_bp;
if (breakpoint != bp->breakpoint)
continue;
if (bp->resource && resource != bp->resource)
continue;
/* Remove this breakpoint from the list; ownership passes to
* the active breakpoint */
wl_list_remove(&bp->link);
/* The active breakpoint and the pending one which triggered it
* are now owned by the client */
active_bp = xzalloc(sizeof(*active_bp));
active_bp->compositor = test->compositor;
active_bp->resource = resource;
active_bp->template_ = bp;
/* Wake the client with the active breakpoint, and wait for it
* to return control */
tsd->breakpoints.active_bp = active_bp;
wet_test_post_sem(&tsd->breakpoints.client_break);
wet_test_wait_sem(&tsd->breakpoints.server_release);
/* Only ever trigger a single breakpoint at a time */
return;
}
}
static void
output_repaint_listener(struct wl_listener *listener, void *data)
{
struct weston_test_output *to =
container_of(listener, struct weston_test_output,
repaint_listener);
struct weston_head *head;
wl_list_for_each(head, &to->output->head_list, output_link) {
maybe_breakpoint(to->test, WESTON_TEST_BREAKPOINT_POST_REPAINT,
head);
}
}
static void
output_created_listener(struct wl_listener *listener, void *data)
{
struct weston_output *output = data;
struct weston_test_output *to = xzalloc(sizeof(*to));
struct weston_test *test =
container_of(listener, struct weston_test,
output_created_listener);
to->test = test;
to->output = output;
to->repaint_listener.notify = output_repaint_listener;
wl_signal_add(&output->frame_signal, &to->repaint_listener);
wl_list_insert(&test->output_list, &to->link);
}
static void
output_destroyed_listener(struct wl_listener *listener, void *data)
{
struct weston_output *output = data;
struct weston_test_output *to, *tmp;
struct weston_test *test =
container_of(listener, struct weston_test,
output_destroyed_listener);
wl_list_for_each_safe(to, tmp, &test->output_list, link) {
if (to->output != output)
continue;
wl_list_remove(&to->repaint_listener.link);
wl_list_remove(&to->link);
free(to);
}
}
static void
touch_device_add(struct weston_test *test)
{
......@@ -449,6 +549,28 @@ send_touch(struct wl_client *client, struct wl_resource *resource,
}
}
static void
client_break(struct wl_client *client, struct wl_resource *resource,
uint32_t _breakpoint, uint32_t resource_id)
{
struct weston_test *test = wl_resource_get_user_data(resource);
struct wet_testsuite_data *tsd = weston_compositor_get_test_data(test->compositor);
struct wet_test_pending_breakpoint *bp;
enum weston_test_breakpoint breakpoint = _breakpoint;
bp = calloc(1, sizeof(*bp));
bp->breakpoint = breakpoint;
if (resource_id != 0) {
struct wl_resource *resource =
wl_client_get_object(client, resource_id);
assert(resource);
bp->resource = wl_resource_get_user_data(resource);
}
wl_list_insert(&tsd->breakpoints.list, &bp->link);
}
static const struct weston_test_interface test_implementation = {
move_surface,
move_pointer,
......@@ -459,12 +581,24 @@ static const struct weston_test_interface test_implementation = {
device_release,
device_add,
send_touch,
client_break,
};
static void
destroy_test(struct wl_resource *resource)
{
struct weston_test *test = wl_resource_get_user_data(resource);
struct wet_testsuite_data *tsd = weston_compositor_get_test_data(test->compositor);
assert(tsd->wl_client);
tsd->wl_client = NULL;
}
static void
bind_test(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct weston_test *test = data;
struct wet_testsuite_data *tsd = weston_compositor_get_test_data(test->compositor);
struct wl_resource *resource;
resource = wl_resource_create(client, &weston_test_interface, 1, id);
......@@ -474,8 +608,12 @@ bind_test(struct wl_client *client, void *data, uint32_t version, uint32_t id)
}
wl_resource_set_implementation(resource,
&test_implementation, test, NULL);
&test_implementation, test,
destroy_test);
/* There can only be one wl_client bound */
assert(!tsd->wl_client);
tsd->wl_client = client;
notify_pointer_position(test, resource);
}
......@@ -561,6 +699,23 @@ create_client_thread(struct weston_test *test, struct wet_testsuite_data *data)
data->thread_event_pipe = pipefd[1];
wl_list_init(&data->breakpoints.list);
ret = sem_init(&data->breakpoints.client_break, 0, 0);
if (ret != 0) {
weston_log("Creating breakpoint semaphore failed: %s (%d)\n",
strerror(errno), errno);
goto out_source;
}
ret = sem_init(&data->breakpoints.server_release, 0, 0);
if (ret != 0) {
weston_log("Creating release semaphore failed: %s (%d)\n",
strerror(errno), errno);
goto out_source;
}
/* Ensure we don't accidentally get signals to the thread. */
sigfillset(&blocked);
sigdelset(&blocked, SIGSEGV);
......@@ -633,11 +788,21 @@ static void
handle_compositor_destroy(struct wl_listener *listener,
void *weston_compositor)
{
struct weston_compositor *compositor = weston_compositor;
struct wet_testsuite_data *data;
struct weston_test *test;
struct weston_output *output;
test = wl_container_of(listener, test, destroy_listener);
data = weston_compositor_get_test_data(test->compositor);
wl_list_remove(&test->destroy_listener.link);
wl_list_remove(&test->output_created_listener.link);
wl_list_remove(&test->output_destroyed_listener.link);
wl_list_for_each(output, &compositor->output_list, link) {
output_destroyed_listener(&test->output_destroyed_listener,
output);
}
if (test->client_source) {
weston_log_scope_printf(test->log, "Cancelling client thread...\n");
......@@ -648,6 +813,8 @@ handle_compositor_destroy(struct wl_listener *listener,
if (test->is_seat_initialized)
test_seat_release(test);
data->wl_client = NULL;
wl_list_remove(&test->layer.view_list.link);
wl_list_remove(&test->layer.link);
......@@ -660,6 +827,7 @@ wet_module_init(struct weston_compositor *ec,
int *argc, char *argv[])
{
struct weston_test *test;
struct weston_output *output;
struct wl_event_loop *loop;
test = zalloc(sizeof *test);
......@@ -677,6 +845,15 @@ wet_module_init(struct weston_compositor *ec,
weston_layer_init(&test->layer, ec);
weston_layer_set_position(&test->layer, WESTON_LAYER_POSITION_CURSOR - 1);
wl_list_init(&test->output_list);
wl_list_for_each(output, &ec->output_list, link)
output_created_listener(&test->output_created_listener, output);
test->output_created_listener.notify = output_created_listener;
wl_signal_add(&ec->output_created_signal, &test->output_created_listener);
test->output_destroyed_listener.notify = output_destroyed_listener;
wl_signal_add(&ec->output_destroyed_signal,
&test->output_destroyed_listener);
test->log = weston_compositor_add_log_scope(ec, "test-harness-plugin",
"weston-test plugin's own actions",
NULL, NULL, NULL);
......
......@@ -26,6 +26,10 @@
#ifndef WESTON_TESTSUITE_DATA_H
#define WESTON_TESTSUITE_DATA_H
#include <assert.h>
#include <errno.h>
#include <semaphore.h>
/** Standard return codes
*
* Both Autotools and Meson use these codes as test program exit codes
......@@ -57,6 +61,98 @@ enum test_type {
TEST_TYPE_CLIENT,
};
/** Safely handle posting a semaphore to wake a waiter
*
* \ingroup testharness_private
*/
static inline void wet_test_post_sem(sem_t *sem)
{
int ret = sem_post(sem);
assert(ret == 0); /* only fails on programming errors */
}
/** Safely handle waiting on a semaphore
*
* \ingroup testharness_private
*/
static inline void wet_test_wait_sem(sem_t *sem)
{
int ret;
do {
ret = sem_wait(sem);
} while (ret != 0 && errno == EINTR);
assert(ret == 0); /* all other failures are programming errors */
}
/** An individual breakpoint set for the server
*
* This breakpoint data is created and placed in a list by either the server
* (when handling protocol messages) or the client (when directly manipulating
* the list during a breakpoint).
*
* It must be freed by the client.
*
* \ingroup testharness_private
*/
struct wet_test_pending_breakpoint {
/** breakpoint type - enum weston_test_breakpoint from protocol */
uint32_t breakpoint;
/** type-specific resource to filter on (optional) */
void *resource;
struct wl_list link; /**< wet_testsuite_breakpoints.list */
};
/** Information about the server's active breakpoint
*
* This breakpoint data is created by the server and passed to the client when
* the server enters a breakpoint.
*
* It must be freed by the client.
*
* \ingroup testharness_private
*/
struct wet_test_active_breakpoint {
/** libweston compositor instance in use */
struct weston_compositor *compositor;
/** type-specific pointer to resource which triggered this breakpoint */
void *resource;
/** on release, reinsert the template to trigger next time */
bool rearm_on_release;
/** client's original breakpoint request */
struct wet_test_pending_breakpoint *template_;
};
/** Client/compositor synchronisation for breakpoint state
*
* Manages the set of active breakpoints placed for the server, as well as
* signalling the pausing/continuing of server actions.
*
* \ingroup testharness_private
*/
struct wet_testsuite_breakpoints {
/** signalled by the server when it reaches a breakpoint */
sem_t client_break;
/** signalled by the client to resume server execution */
sem_t server_release;
/** Pushed by the server when a breakpoint is triggered, immediately
* before it signals the client_break semaphore. Client consumes this
* and takes ownership after the wait succeeds. */
struct wet_test_active_breakpoint *active_bp;
/** client-internal state; set by consuming active_bp, cleared by
* signalling server_release */
bool in_client_break;
/** list of pending breakpoints: owned by the server during normal
* execution (ordinarily added to by a protocol request, and
* traversed to find a possible breakpoint to trigger), and owned by
* the client wtihin a breakpoint (pending breakpoints may be added
* or removed). Members are wet_test_pending_breakpoint.link */
struct wl_list list;
};
/** Test harness specific data for running tests
*
* \ingroup testharness_private
......@@ -64,6 +160,8 @@ enum test_type {
struct wet_testsuite_data {
void (*run)(struct wet_testsuite_data *);
void *wl_client;
/* test definitions */
const struct weston_test_entry *tests;
unsigned tests_count;
......@@ -73,6 +171,7 @@ struct wet_testsuite_data {
/* client thread control */
int thread_event_pipe;
struct wet_testsuite_breakpoints breakpoints;
/* informational run state */
int fixture_iteration;
......