weston/tests/devices-test.c
Pekka Paalanen 591fa3b95b tests: fix all leaks in devices-test
This fixes all ASan reported leaks in this test.

This test program has several tests named *_multiple that just run
another test function 30 times. Previously without cleanup all the
created clients would be left lingering, but now they are torn down. Ths
might cause a change in test behaviour, although that was never the
intention:

> It is intentional to run it so many times, but it is not intentional
> to run a hundred clients at a time.  The problem is that currently we
> have no destroy function for client.  However, the clients do not run
> simultaneously but serially, so the effect should be the same as if
> we'd destroy them (after the client finishes its body, it just 'is'
> and does nothing until the process exits)

- the original review discussion in
https://lists.freedesktop.org/archives/wayland-devel/2015-March/020957.html

The intention for the repeat testing is that as the Weston instance
remains from test to another, each test needs to undo its changes to the
devices. Failing to correcntly undo would accumulate devices.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
2021-06-28 14:21:43 +03:00

365 lines
11 KiB
C

/*
* Copyright © 2015 Red Hat, Inc.
*
* 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 <string.h>
#include "weston-test-client-helper.h"
#include "weston-test-fixture-compositor.h"
static enum test_result_code
fixture_setup(struct weston_test_harness *harness)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP(fixture_setup);
/**
* Test (un)plugging devices
*
* At the end of each test we must return Weston to the previous state
* (add all removed devices and remove extra devices), so that
* the environment is prepared for the other tests too
*/
#define WL_SEAT_CAPABILITY_ALL (WL_SEAT_CAPABILITY_KEYBOARD |\
WL_SEAT_CAPABILITY_POINTER |\
WL_SEAT_CAPABILITY_TOUCH)
/* simply test if weston sends the right capabilities when
* some devices are removed */
TEST(seat_capabilities_test)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
assert(cl->input->pointer);
weston_test_device_release(cl->test->weston_test, "pointer");
client_roundtrip(cl);
assert(!cl->input->pointer);
assert(!(cl->input->caps & WL_SEAT_CAPABILITY_POINTER));
assert(cl->input->keyboard);
weston_test_device_release(cl->test->weston_test, "keyboard");
client_roundtrip(cl);
assert(!cl->input->keyboard);
assert(!(cl->input->caps & WL_SEAT_CAPABILITY_KEYBOARD));
assert(cl->input->touch);
weston_test_device_release(cl->test->weston_test, "touch");
client_roundtrip(cl);
assert(!cl->input->touch);
assert(!(cl->input->caps & WL_SEAT_CAPABILITY_TOUCH));
/* restore previous state */
weston_test_device_add(cl->test->weston_test, "keyboard");
weston_test_device_add(cl->test->weston_test, "pointer");
weston_test_device_add(cl->test->weston_test, "touch");
client_roundtrip(cl);
assert(cl->input->pointer);
assert(cl->input->keyboard);
assert(cl->input->touch);
/* add extra devices */
weston_test_device_add(cl->test->weston_test, "keyboard");
weston_test_device_add(cl->test->weston_test, "pointer");
weston_test_device_add(cl->test->weston_test, "touch");
client_roundtrip(cl);
/* remove extra devices */
weston_test_device_release(cl->test->weston_test, "keyboard");
weston_test_device_release(cl->test->weston_test, "pointer");
weston_test_device_release(cl->test->weston_test, "touch");
client_roundtrip(cl);
/* we still should have all the capabilities, since the devices
* were doubled */
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
assert(cl->input->pointer);
assert(cl->input->keyboard);
assert(cl->input->touch);
client_destroy(cl);
}
#define COUNT 15
TEST(multiple_device_add_and_remove)
{
int i;
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
/* add device a lot of times */
for (i = 0; i < COUNT; ++i) {
weston_test_device_add(cl->test->weston_test, "keyboard");
weston_test_device_add(cl->test->weston_test, "pointer");
weston_test_device_add(cl->test->weston_test, "touch");
}
client_roundtrip(cl);
assert(cl->input->pointer);
assert(cl->input->keyboard);
assert(cl->input->touch);
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
/* release all new devices */
for (i = 0; i < COUNT; ++i) {
weston_test_device_release(cl->test->weston_test, "keyboard");
weston_test_device_release(cl->test->weston_test, "pointer");
weston_test_device_release(cl->test->weston_test, "touch");
}
client_roundtrip(cl);
/* there is still one from each device left */
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
assert(cl->input->pointer);
assert(cl->input->keyboard);
assert(cl->input->touch);
client_destroy(cl);
}
TEST(device_release_before_destroy)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
/* we can release pointer when we won't be using it anymore.
* Do it and see what happens if the device is destroyed
* right after that */
wl_pointer_release(cl->input->pointer->wl_pointer);
/* we must free and set to NULL the structures, otherwise
* seat capabilities will double-free them */
free(cl->input->pointer);
cl->input->pointer = NULL;
wl_keyboard_release(cl->input->keyboard->wl_keyboard);
free(cl->input->keyboard);
cl->input->keyboard = NULL;
wl_touch_release(cl->input->touch->wl_touch);
free(cl->input->touch);
cl->input->touch = NULL;
weston_test_device_release(cl->test->weston_test, "pointer");
weston_test_device_release(cl->test->weston_test, "keyboard");
weston_test_device_release(cl->test->weston_test, "touch");
client_roundtrip(cl);
assert(cl->input->caps == 0);
/* restore previous state */
weston_test_device_add(cl->test->weston_test, "pointer");
weston_test_device_add(cl->test->weston_test, "keyboard");
weston_test_device_add(cl->test->weston_test, "touch");
client_roundtrip(cl);
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
client_destroy(cl);
}
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) {
device_release_before_destroy();
}
}
/* normal work-flow test */
TEST(device_release_after_destroy)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
weston_test_device_release(cl->test->weston_test, "pointer");
wl_pointer_release(cl->input->pointer->wl_pointer);
/* we must free the memory manually, otherwise seat.capabilities
* will try to free it and will use invalid proxy */
free(cl->input->pointer);
cl->input->pointer = NULL;
client_roundtrip(cl);
weston_test_device_release(cl->test->weston_test, "keyboard");
wl_keyboard_release(cl->input->keyboard->wl_keyboard);
free(cl->input->keyboard);
cl->input->keyboard = NULL;
client_roundtrip(cl);
weston_test_device_release(cl->test->weston_test, "touch");
wl_touch_release(cl->input->touch->wl_touch);
free(cl->input->touch);
cl->input->touch = NULL;
client_roundtrip(cl);
assert(cl->input->caps == 0);
/* restore previous state */
weston_test_device_add(cl->test->weston_test, "pointer");
weston_test_device_add(cl->test->weston_test, "keyboard");
weston_test_device_add(cl->test->weston_test, "touch");
client_roundtrip(cl);
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
client_destroy(cl);
}
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) {
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)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
struct wl_pointer *wl_pointer;
struct wl_keyboard *wl_keyboard;
struct wl_touch *wl_touch;
/* There's a race:
* 1) compositor destroys device
* 2) client asks for the device, because it has not received
* the new capabilities yet
* 3) compositor gets the request with a new_id for the
* destroyed device
* 4) client uses the new_id
* 5) client gets new capabilities, destroying the objects
*
* If the compositor just bails out in step 3) and does not
* create the resource, then the client gets an error in step 4)
* - even though it followed the protocol (it just didn't know
* about new capabilities).
*
* This test simulates this situation
*/
/* connection is buffered, so after calling client_roundtrip(),
* this whole batch will be delivered to compositor and will
* exactly simulate our situation */
weston_test_device_release(cl->test->weston_test, "pointer");
wl_pointer = wl_seat_get_pointer(cl->input->wl_seat);
assert(wl_pointer);
/* this should be ignored */
wl_pointer_set_cursor(wl_pointer, 0, NULL, 0, 0);
/* this should not be ignored */
wl_pointer_release(wl_pointer);
client_roundtrip(cl);
weston_test_device_release(cl->test->weston_test, "keyboard");
wl_keyboard = wl_seat_get_keyboard(cl->input->wl_seat);
assert(wl_keyboard);
wl_keyboard_release(wl_keyboard);
client_roundtrip(cl);
weston_test_device_release(cl->test->weston_test, "touch");
wl_touch = wl_seat_get_touch(cl->input->wl_seat);
assert(wl_touch);
wl_touch_release(wl_touch);
client_roundtrip(cl);
/* get weston to the previous state */
weston_test_device_add(cl->test->weston_test, "pointer");
weston_test_device_add(cl->test->weston_test, "keyboard");
weston_test_device_add(cl->test->weston_test, "touch");
client_roundtrip(cl);
assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);
client_destroy(cl);
}
TEST(get_device_after_destroy_multiple)
{
int i;
/* if weston crashed during this test, then there is
* some inconsistency */
for (i = 0; i < 30; ++i) {
get_device_after_destroy();
}
}
TEST(seats_have_names)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
struct input *input;
wl_list_for_each(input, &cl->inputs, link) {
assert(input->seat_name);
}
client_destroy(cl);
}
TEST(seat_destroy_and_recreate)
{
struct client *cl = create_client_and_test_surface(100, 100, 100, 100);
weston_test_device_release(cl->test->weston_test, "seat");
/* Roundtrip to receive and handle the seat global removal event */
client_roundtrip(cl);
assert(!cl->input);
weston_test_device_add(cl->test->weston_test, "seat");
/* First roundtrip to send request and receive new seat global */
client_roundtrip(cl);
/* Second roundtrip to handle seat events and set up input devices */
client_roundtrip(cl);
assert(cl->input);
assert(cl->input->pointer);
assert(cl->input->keyboard);
assert(cl->input->touch);
client_destroy(cl);
}