forked from 12to11/12to11
Check in new files for selection tests
* tests/select_helper.c: * tests/select_test.c: New files.
This commit is contained in:
parent
be117ac93d
commit
be955e3c79
2 changed files with 512 additions and 0 deletions
201
tests/select_helper.c
Normal file
201
tests/select_helper.c
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
/* Tests for the Wayland compositor running on the X server.
|
||||||
|
|
||||||
|
Copyright (C) 2022 to various contributors.
|
||||||
|
|
||||||
|
This file is part of 12to11.
|
||||||
|
|
||||||
|
12to11 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 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
12to11 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 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
|
||||||
|
/* select_helper.c -- Read the data of the clipboard selection and
|
||||||
|
print them to stdout.
|
||||||
|
|
||||||
|
There must be three arguments: the name of the display, the
|
||||||
|
timestamp at which the selection was acquired, and the target. */
|
||||||
|
|
||||||
|
|
||||||
|
/* The display connected to. */
|
||||||
|
static Display *display;
|
||||||
|
|
||||||
|
/* The selection transfer window. */
|
||||||
|
static Window selection_transfer_window;
|
||||||
|
|
||||||
|
/* Various atoms. */
|
||||||
|
static Atom CLIPBOARD, target_atom, INCR;
|
||||||
|
|
||||||
|
static void
|
||||||
|
wait_for_selection_notify (XEvent *event)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
XNextEvent (display, event);
|
||||||
|
|
||||||
|
if (event->type == SelectionNotify
|
||||||
|
&& (event->xselection.requestor
|
||||||
|
== selection_transfer_window)
|
||||||
|
&& (event->xselection.selection
|
||||||
|
== CLIPBOARD)
|
||||||
|
&& (event->xselection.property
|
||||||
|
== target_atom)
|
||||||
|
&& (event->xselection.target
|
||||||
|
== target_atom))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wait_for_new_value (XEvent *event, Atom property)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
XNextEvent (display, event);
|
||||||
|
|
||||||
|
if (event->type == PropertyNotify
|
||||||
|
&& event->xproperty.atom == property
|
||||||
|
&& event->xproperty.state == PropertyNewValue)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
get_size_for_format (int format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case 32:
|
||||||
|
return sizeof (long);
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
return sizeof (short int);
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
return sizeof (char);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should not actually happen. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
XSetWindowAttributes attrs;
|
||||||
|
unsigned long flags, timestamp;
|
||||||
|
char *atom_names[2];
|
||||||
|
Atom atoms[3], actual_type, property;
|
||||||
|
XEvent event;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long nitems, bytes_after;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
if (argc < 4)
|
||||||
|
/* Not enough arguments were specified. */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
display = XOpenDisplay (argv[1]);
|
||||||
|
|
||||||
|
if (!display)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Make the window used to transfer selection data. */
|
||||||
|
attrs.override_redirect = True;
|
||||||
|
attrs.event_mask = PropertyChangeMask;
|
||||||
|
flags = CWEventMask | CWOverrideRedirect;
|
||||||
|
|
||||||
|
selection_transfer_window
|
||||||
|
= XCreateWindow (display, DefaultRootWindow (display),
|
||||||
|
-1, -1, 1, 1, 0, CopyFromParent, InputOnly,
|
||||||
|
CopyFromParent, flags, &attrs);
|
||||||
|
|
||||||
|
/* Get the time. */
|
||||||
|
timestamp = strtoul (argv[2], NULL, 10);
|
||||||
|
|
||||||
|
atom_names[0] = argv[3];
|
||||||
|
atom_names[1] = (char *) "CLIPBOARD";
|
||||||
|
atom_names[2] = (char *) "CLIPBOARD";
|
||||||
|
XInternAtoms (display, atom_names, 3, False, atoms);
|
||||||
|
target_atom = atoms[0];
|
||||||
|
CLIPBOARD = atoms[1];
|
||||||
|
INCR = atoms[2];
|
||||||
|
|
||||||
|
/* Now ask for CLIPBOARD. */
|
||||||
|
XConvertSelection (display, CLIPBOARD, target_atom,
|
||||||
|
target_atom, selection_transfer_window,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
/* And wait for the SelectionNotify event. */
|
||||||
|
wait_for_selection_notify (&event);
|
||||||
|
|
||||||
|
/* Selection conversion failed. */
|
||||||
|
if (event.xselection.property == None)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
property = event.xselection.property;
|
||||||
|
|
||||||
|
XGetWindowProperty (display, selection_transfer_window,
|
||||||
|
property, 0, 0xffffffff, True, AnyPropertyType,
|
||||||
|
&actual_type, &actual_format, &nitems, &bytes_after,
|
||||||
|
&data);
|
||||||
|
|
||||||
|
if (!data || bytes_after)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (actual_type == INCR)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
XFree (data);
|
||||||
|
|
||||||
|
wait_for_new_value (&event, property);
|
||||||
|
XGetWindowProperty (display, selection_transfer_window, property, 0,
|
||||||
|
0xffffffff, True, AnyPropertyType, &actual_type,
|
||||||
|
&actual_format, &nitems, &bytes_after, &data);
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (nitems)
|
||||||
|
{
|
||||||
|
/* Write the selection data to stdout. */
|
||||||
|
if (fwrite (data, get_size_for_format (actual_format),
|
||||||
|
nitems, stdout) != nitems)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection transfer is complete. */
|
||||||
|
fflush (stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Write the selection data to stdout. */
|
||||||
|
if (fwrite (data, get_size_for_format (actual_format),
|
||||||
|
nitems, stdout) != nitems)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Return success. */
|
||||||
|
fflush (stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
311
tests/select_test.c
Normal file
311
tests/select_test.c
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
/* Tests for the Wayland compositor running on the X server.
|
||||||
|
|
||||||
|
Copyright (C) 2022 to various contributors.
|
||||||
|
|
||||||
|
This file is part of 12to11.
|
||||||
|
|
||||||
|
12to11 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 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
12to11 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 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include "test_harness.h"
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
|
||||||
|
enum test_kind
|
||||||
|
{
|
||||||
|
SELECT_STRING_KIND,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *test_names[] =
|
||||||
|
{
|
||||||
|
"select_string",
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LAST_TEST SELECT_STRING_KIND
|
||||||
|
|
||||||
|
/* The display. */
|
||||||
|
static struct test_display *display;
|
||||||
|
|
||||||
|
/* The data device manager. */
|
||||||
|
static struct wl_data_device_manager *data_device_manager;
|
||||||
|
|
||||||
|
/* Test interfaces. */
|
||||||
|
static struct test_interface test_interfaces[] =
|
||||||
|
{
|
||||||
|
{ "wl_data_device_manager", &data_device_manager,
|
||||||
|
&wl_data_device_manager_interface, 3, },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The data device. */
|
||||||
|
static struct wl_data_device *data_device;
|
||||||
|
|
||||||
|
/* Whether or not the data source's send listener was called. */
|
||||||
|
static bool send_called;
|
||||||
|
|
||||||
|
/* Sample text used. */
|
||||||
|
|
||||||
|
#define SAMPLE_TEXT \
|
||||||
|
("Lorem ipsum dolor sit amet, consectetur adipiscing elit" \
|
||||||
|
", sed do eiusmod tempor incididunt ut labore et dolore" \
|
||||||
|
" magna aliqua. Ut enim ad minim veniam, quis nostrud" \
|
||||||
|
" exercitation ullamco laboris nisi ut aliquip ex ea commodo" \
|
||||||
|
" consequat. Duis aute irure dolor in reprehenderit in" \
|
||||||
|
" voluptate velit esse cillum dolore eu fugiat nulla pariatur." \
|
||||||
|
" Excepteur sint occaecat cupidatat non proident, sunt in" \
|
||||||
|
" culpa qui officia deserunt mollit anim id est laborum.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
test_get_time_1 (Display *display, XEvent *event, XPointer arg)
|
||||||
|
{
|
||||||
|
Atom *atom;
|
||||||
|
|
||||||
|
atom = (Atom *) arg;
|
||||||
|
|
||||||
|
if (event->type == PropertyNotify
|
||||||
|
&& event->xproperty.atom == *atom)
|
||||||
|
return True;
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a timestamp suitable for use in events dispatched to the test
|
||||||
|
seat. */
|
||||||
|
|
||||||
|
static Time
|
||||||
|
test_get_time (void)
|
||||||
|
{
|
||||||
|
Atom property_atom;
|
||||||
|
XEvent event;
|
||||||
|
Window window;
|
||||||
|
unsigned char unused;
|
||||||
|
XSetWindowAttributes attrs;
|
||||||
|
|
||||||
|
attrs.event_mask = PropertyChangeMask;
|
||||||
|
window = XCreateWindow (display->x_display,
|
||||||
|
DefaultRootWindow (display->x_display),
|
||||||
|
0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent,
|
||||||
|
CWEventMask, &attrs);
|
||||||
|
unused = '\0';
|
||||||
|
property_atom = XInternAtom (display->x_display,
|
||||||
|
"_INTERNAL_SERVER_TIME_PROP",
|
||||||
|
False);
|
||||||
|
XChangeProperty (display->x_display, window, property_atom,
|
||||||
|
XA_CARDINAL, 8, PropModeReplace, &unused, 1);
|
||||||
|
XIfEvent (display->x_display, &event, test_get_time_1,
|
||||||
|
(XPointer) &property_atom);
|
||||||
|
XDestroyWindow (display->x_display, window);
|
||||||
|
return event.xproperty.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_data_source_target (void *data, struct wl_data_source *data_source,
|
||||||
|
const char *mime_type)
|
||||||
|
{
|
||||||
|
/* Nothing to do here. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
handle_data_source_send_1 (void *data)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = (intptr_t) data;
|
||||||
|
|
||||||
|
write (fd, SAMPLE_TEXT, sizeof SAMPLE_TEXT - 1);
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_data_source_send (void *data, struct wl_data_source *data_source,
|
||||||
|
const char *mime_type, int fd)
|
||||||
|
{
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
send_called = true;
|
||||||
|
|
||||||
|
if (pthread_create (&thread, NULL, handle_data_source_send_1,
|
||||||
|
(void *) (intptr_t) fd))
|
||||||
|
die ("pthread_create");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_data_source_cancelled (void *data, struct wl_data_source *data_source)
|
||||||
|
{
|
||||||
|
report_test_failure ("data source cancelled");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_data_source_listener data_source_listener =
|
||||||
|
{
|
||||||
|
handle_data_source_target,
|
||||||
|
handle_data_source_send,
|
||||||
|
handle_data_source_cancelled,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
own_sample_text (void)
|
||||||
|
{
|
||||||
|
struct wl_data_source *source;
|
||||||
|
uint32_t display_serial;
|
||||||
|
|
||||||
|
source = wl_data_device_manager_create_data_source (data_device_manager);
|
||||||
|
display_serial = test_get_serial (display);
|
||||||
|
|
||||||
|
if (!source)
|
||||||
|
report_test_failure ("failed to create data source");
|
||||||
|
|
||||||
|
wl_data_source_offer (source, "text/plain");
|
||||||
|
wl_data_source_offer (source, "text/plain;charset=utf-8");
|
||||||
|
wl_data_source_add_listener (source, &data_source_listener, NULL);
|
||||||
|
wl_data_device_set_selection (data_device, source, display_serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
verify_sample_text (Time time)
|
||||||
|
{
|
||||||
|
int pipefds[2], wstatus;
|
||||||
|
pid_t pid;
|
||||||
|
char *display_string;
|
||||||
|
char time_buffer[45], buffer[sizeof SAMPLE_TEXT];
|
||||||
|
ssize_t bytes_read;
|
||||||
|
const char *sample_text_buffer;
|
||||||
|
|
||||||
|
/* Run select_helper with the specified timestamp. Wait until
|
||||||
|
handle_data_source_send is called, and then begin reading from
|
||||||
|
the pipe. */
|
||||||
|
|
||||||
|
if (pipe (pipefds) < 0)
|
||||||
|
die ("pipe");
|
||||||
|
|
||||||
|
display_string = DisplayString (display->x_display);
|
||||||
|
time = sprintf (time_buffer, "%lu", time);
|
||||||
|
pid = fork ();
|
||||||
|
|
||||||
|
if (pid == -1)
|
||||||
|
die ("fork");
|
||||||
|
else if (!pid)
|
||||||
|
{
|
||||||
|
close (pipefds[0]);
|
||||||
|
|
||||||
|
if (!dup2 (pipefds[1], 1))
|
||||||
|
exit (1);
|
||||||
|
|
||||||
|
execlp ("./select_helper", "./select_helper",
|
||||||
|
display_string, time_buffer, "STRING",
|
||||||
|
NULL);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_called = false;
|
||||||
|
|
||||||
|
while (!send_called)
|
||||||
|
{
|
||||||
|
if (wl_display_dispatch (display->display) == -1)
|
||||||
|
die ("wl_display_dispatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now, start reading from the pipe and comparing the contents. */
|
||||||
|
sample_text_buffer = SAMPLE_TEXT;
|
||||||
|
bytes_read = read (pipefds[0], buffer, sizeof SAMPLE_TEXT - 1);
|
||||||
|
|
||||||
|
if (bytes_read != sizeof SAMPLE_TEXT - 1)
|
||||||
|
report_test_failure ("wanted %zu bytes, but got %zd",
|
||||||
|
sizeof SAMPLE_TEXT - 1, bytes_read);
|
||||||
|
|
||||||
|
waitpid (pid, &wstatus, 0);
|
||||||
|
|
||||||
|
if (WEXITSTATUS (wstatus))
|
||||||
|
report_test_failure ("child exited with failure: %d",
|
||||||
|
WEXITSTATUS (wstatus));
|
||||||
|
|
||||||
|
/* Now compare the text. */
|
||||||
|
if (memcmp (buffer, sample_text_buffer, bytes_read))
|
||||||
|
report_test_failure ("read text differs from sample text!");
|
||||||
|
|
||||||
|
close (pipefds[0]);
|
||||||
|
close (pipefds[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_single_step (enum test_kind kind)
|
||||||
|
{
|
||||||
|
Time time;
|
||||||
|
|
||||||
|
test_log ("running test step: %s", test_names[kind]);
|
||||||
|
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case SELECT_STRING_KIND:
|
||||||
|
/* Set the last user time of the seat to the current X server
|
||||||
|
time. */
|
||||||
|
time = test_get_time ();
|
||||||
|
test_seat_controller_set_last_user_time (display->seat->controller,
|
||||||
|
0, time);
|
||||||
|
own_sample_text ();
|
||||||
|
|
||||||
|
/* Do a roundtrip. If selection ownership changes, then the
|
||||||
|
protocol translator will wait for selection ownership to be
|
||||||
|
confirmed. */
|
||||||
|
wl_display_roundtrip (display->display);
|
||||||
|
|
||||||
|
/* Now, verify the selection contents. */
|
||||||
|
verify_sample_text (time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind == LAST_TEST)
|
||||||
|
test_complete ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_test (void)
|
||||||
|
{
|
||||||
|
test_single_step (SELECT_STRING_KIND);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (wl_display_dispatch (display->display) == -1)
|
||||||
|
die ("wl_display_dispatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
test_init ();
|
||||||
|
display = open_test_display (test_interfaces,
|
||||||
|
ARRAYELTS (test_interfaces));
|
||||||
|
|
||||||
|
if (!display)
|
||||||
|
report_test_failure ("failed to open display");
|
||||||
|
|
||||||
|
test_init_seat (display);
|
||||||
|
data_device
|
||||||
|
= wl_data_device_manager_get_data_device (data_device_manager,
|
||||||
|
display->seat->seat);
|
||||||
|
run_test ();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue