Files
strans/tests/ibus_live_test.c
Hojun-Cho 059e8bbc75 ibus: SetCursorLocationRelative is not a caret
A GTK client on the Wayland backend sends this where an X11 one sends
SetCursorLocation, and ictab did not list it, so libdbus answered
UnknownMethod.  Accept it and throw it away: the absolute translation
sits inside GDK_IS_X11_DISPLAY, so the coordinates are surface local,
and ibus's own daemon will not hand them to an engine either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 18:38:04 +09:00

262 lines
6.2 KiB
C

#define _GNU_SOURCE
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "live.h"
#include "livebus.h"
enum
{
Maxconnections = 64,
};
static int
invalidcall(DBusConnection *conn, char *path, char *member)
{
DBusMessage *reply;
const char *name;
int ok;
reply = sendcall(conn, contextcall(path, member), member);
if(reply == NULL)
return 0;
name = dbus_message_get_error_name(reply);
ok = dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR &&
name != NULL && strcmp(name, DBUS_ERROR_INVALID_ARGS) == 0;
if(!ok)
fail("malformed %s returned %s, expected %s", member,
name != NULL ? name : "a non-error reply",
DBUS_ERROR_INVALID_ARGS);
dbus_message_unref(reply);
return ok;
}
/* libibus destroys through the Service interface; the path must go away. */
static int
destroyed(DBusConnection *conn, char *path)
{
DBusMessage *m, *reply;
const char *name;
int ok;
m = dbus_message_new_method_call("org.freedesktop.IBus", path,
"org.freedesktop.IBus.Service", "Destroy");
reply = callret(conn, m, "Service.Destroy");
if(reply == NULL)
return 0;
dbus_message_unref(reply);
reply = sendcall(conn, contextcall(path, "FocusIn"), "FocusIn");
if(reply == NULL)
return 0;
name = dbus_message_get_error_name(reply);
ok = name != NULL && strcmp(name, DBUS_ERROR_UNKNOWN_OBJECT) == 0;
if(!ok)
fail("context survived Service.Destroy");
dbus_message_unref(reply);
return ok;
}
static int
overflowrejected(char *address)
{
DBusConnection *conn;
DBusError err;
struct pollfd pfd;
int fd, n, ok, timeout;
int64_t deadline;
dbus_error_init(&err);
conn = dbus_connection_open_private(address, &err);
if(conn == NULL){
ok = dbus_error_is_set(&err);
dbus_error_free(&err);
return ok || fail("overflow connection failed without a D-Bus error");
}
dbus_error_free(&err);
dbus_connection_set_exit_on_disconnect(conn, FALSE);
if(!dbus_connection_get_unix_fd(conn, &fd)){
closebus(&conn);
return fail("overflow connection has no Unix descriptor");
}
ok = 0;
deadline = nowms() + Calltimeout;
for(;;){
if(!dbus_connection_get_is_connected(conn)){
ok = 1;
break;
}
timeout = leftms(deadline);
if(timeout == 0){
fail("65th IBus connection was not rejected");
break;
}
pfd.fd = fd;
pfd.events = POLLIN;
pfd.revents = 0;
n = poll(&pfd, 1, timeout);
if(n < 0 && errno == EINTR)
continue;
if(n < 0){
fail("poll overflow IBus connection: %s", strerror(errno));
break;
}
if(n == 0)
continue;
if(pfd.revents & POLLNVAL){
fail("overflow IBus descriptor became invalid");
break;
}
dbus_connection_read_write_dispatch(conn, 0);
}
closebus(&conn);
return ok;
}
static int
runcontract(char *address)
{
static char *bad[] = {
"ProcessKeyEvent",
"SetEngine",
"SetCapabilities",
"SetCursorLocation",
"SetCursorLocationRelative",
};
DBusConnection *conn;
char path[96];
int i, ok;
conn = openbus(address, "contract");
if(conn == NULL)
return 0;
ok = hello(conn, NULL, 0, "contract Hello") &&
createcontext(conn, path, sizeof path, "contract context");
for(i = 0; ok && i < (int)(sizeof bad / sizeof bad[0]); i++)
ok = invalidcall(conn, path, bad[i]);
ok = ok && destroyed(conn, path);
closebus(&conn);
return ok;
}
static int
runcapacity(char *address)
{
DBusConnection *conn[Maxconnections];
int i, ok;
for(i = 0; i < Maxconnections; i++)
conn[i] = NULL;
ok = 0;
for(i = 0; i < Maxconnections; i++){
conn[i] = openbus(address, "capacity");
if(conn[i] == NULL || !hello(conn[i], NULL, 0, "capacity Hello"))
goto out;
}
if(!overflowrejected(address))
goto out;
closebus(&conn[Maxconnections-1]);
if(!createcontext(conn[0], NULL, 0, "capacity context"))
goto out;
conn[Maxconnections-1] = openbus(address, "replacement");
if(conn[Maxconnections-1] == NULL ||
!hello(conn[Maxconnections-1], NULL, 0, "replacement Hello") ||
!createcontext(conn[Maxconnections-1], NULL, 0, "replacement context"))
goto out;
ok = 1;
out:
for(i = 0; i < Maxconnections; i++)
closebus(&conn[i]);
return ok;
}
static int
runclient(Live *l, char *address, char *program)
{
pid_t pid, n;
int status;
int64_t deadline;
pid = fork();
if(pid < 0)
return fail("fork official libibus client: %s", strerror(errno));
if(pid == 0){
if(setenv("XDG_RUNTIME_DIR", l->runtime, 1) < 0 ||
setenv("XDG_CONFIG_HOME", l->config, 1) < 0 ||
setenv("HOME", l->home, 1) < 0 ||
unsetenv("IBUS_ADDRESS_FILE") < 0)
_exit(126);
execl(program, program, address, (char*)0);
dprintf(STDERR_FILENO, "exec %s: %s\n", program, strerror(errno));
_exit(127);
}
deadline = nowms() + Starttimeout;
for(;;){
n = waitpid(pid, &status, WNOHANG);
if(n == pid)
break;
if(n < 0 && errno == EINTR)
continue;
if(n < 0)
return fail("wait official libibus client: %s", strerror(errno));
if(leftms(deadline) == 0){
fail("official libibus client timed out");
kill(pid, SIGKILL);
do
n = waitpid(pid, &status, 0);
while(n < 0 && errno == EINTR);
return 0;
}
pausems(10);
}
if(!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return fail("official libibus client exited with wait status %#x",
status);
return 1;
}
int
main(int argc, char **argv)
{
Daemon daemon;
Live live;
char address[512];
int capacity, ok;
testname = "ibus_live_test";
capacity = argc == 4 && strcmp(argv[1], "--capacity") == 0;
if(argc != 4){
fprintf(stderr, "usage: ibus_live_test strans mapdir libibus-client\n");
fprintf(stderr, " ibus_live_test --capacity strans mapdir\n");
return 2;
}
daemoninit(&daemon, "daemon");
ok = livesetup(&live, "ibus") &&
startdaemon(&live, &daemon, argv[capacity ? 2 : 1],
argv[capacity ? 3 : 2]) &&
waitready(&live, &daemon, address, sizeof address);
if(ok)
ok = capacity ? runcapacity(address) :
runclient(&live, address, argv[3]) && runcontract(address);
if(!ok)
showerrors(&daemon);
if(!stopdaemon(&daemon))
ok = 0;
if(!closeerrors(&daemon))
ok = 0;
if(!liveclean(&live))
ok = 0;
if(!ok)
return 1;
printf(capacity ? "ibus connection capacity: ok\n" :
"ibus official client and malformed call: ok\n");
return 0;
}