From 516ec98a707afb0d6e298af35418f28decbcaaa9 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 14 Aug 2026 23:13:32 +0900 Subject: [PATCH] test: separate quick, live, and stress checks --- Makefile | 26 +- tests/Makefile | 45 +- tests/daemon_collision_test.c | 247 ++----- tests/daemon_failure_test.c | 466 +++---------- tests/daemon_restart_test.c | 379 +---------- tests/ibus_client_smoke.c | 113 ++-- tests/ibus_live_test.c | 1195 +++++---------------------------- tests/ipc_live_test.c | 82 ++- tests/unit_test.c | 7 +- 9 files changed, 469 insertions(+), 2091 deletions(-) diff --git a/Makefile b/Makefile index dbf69ef..c8649fb 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,14 @@ docker-build: $(DOCKER_RUN) make -B all docker-check: - $(DOCKER_RUN) make -B check TESTARGS="$(TESTARGS)" + $(DOCKER_RUN) make -B check check-live check-stress \ + UNITARGS="$(UNITARGS)" + +docker-check-live: + $(DOCKER_RUN) make -B check-live + +docker-check-stress: + $(DOCKER_RUN) make -B check-stress docker-bench: $(DOCKER_RUN) make -B bench @@ -59,11 +66,17 @@ docker-bench: docker: docker-image $(MAKE) docker-build -check: verify-map test +check: verify-map + $(MAKE) -C tests check UNITARGS="$(UNITARGS)" $(MAKE) -C xim check -test: $(PROG) gtk - $(MAKE) -C tests check TESTARGS="$(TESTARGS)" +check-live: $(PROG) gtk + $(MAKE) -C tests check-live + +check-stress: $(PROG) + $(MAKE) -C tests check-stress + +test: check verify-map: python3 map/mktelex.py | cmp - map/telex.map @@ -74,5 +87,6 @@ verify-map: python3 -B tests/mkhanja_test.py python3 -B tests/skk2ktrans_test.py -.PHONY: all check test verify-map clean gtk bench docker docker-image \ - docker-build docker-check docker-bench +.PHONY: all check check-live check-stress test verify-map clean gtk bench \ + docker docker-image docker-build docker-check docker-check-live \ + docker-check-stress docker-bench diff --git a/tests/Makefile b/tests/Makefile index 3e4ad44..4874ee5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,9 +19,12 @@ XIM_LIBS = $(shell pkg-config --libs xcb-imdkit xkbcommon-x11) LIBS = -lthread -lbio $(TEXT_LIBS) $(IBUS_LIBS) $(XIM_LIBS) PROG = unit_test -LIVE = ibus_live_test ibus_client_smoke gtk_live_test xim_live_test \ - ipc_live_test daemon_collision_test daemon_failure_test daemon_restart_test -TESTSRC = unit_test.c test_util.c str_test.c hash_test.c trie_test.c \ +STRESS = stress_test +SMOKE = ibus_live_test ibus_client_smoke gtk_live_test xim_live_test \ + ipc_live_test +FAULT = daemon_collision_test daemon_failure_test daemon_restart_test +LIVE = $(SMOKE) $(FAULT) +TESTSRC = test_util.c str_test.c hash_test.c trie_test.c \ ko_test.c vi_test.c engine_test.c dict_test.c ipc_test.c \ popup_test.c font_test.c ibus_test.c server_test.c xim_adapter_test.c TESTOBJ = $(TESTSRC:.c=.o) @@ -29,22 +32,36 @@ PARENTSRC = str.c hash.c trie.c dict.c ko.c vi.c ipc.c popup_layout.c \ font.c PARENTOBJ = $(PARENTSRC:%.c=unit_%.o) XIMOBJ = unit_xim_keymap.o unit_ximtext.o -OBJS = $(TESTOBJ) $(PARENTOBJ) $(XIMOBJ) +COMMONOBJ = $(TESTOBJ) $(PARENTOBJ) $(XIMOBJ) +OBJS = unit_test.o stress_test.o $(COMMONOBJ) -all: $(PROG) $(LIVE) +all: $(PROG) $(STRESS) $(LIVE) -check test: $(PROG) $(LIVE) ../gtk/im-strans.so - ./$(PROG) $(TESTARGS) +check test: $(PROG) + ./$(PROG) $(UNITARGS) + +check-live: $(SMOKE) ../strans ../gtk/im-strans.so ./ibus_live_test ../strans ../map ./ibus_client_smoke ./gtk_live_test ../gtk/im-strans.so ./xim_live_test ../strans ../map ./ipc_live_test ../strans ../map + +check-stress: $(STRESS) ibus_live_test ipc_live_test $(FAULT) ../strans + ./$(STRESS) + ./ibus_live_test --capacity ../strans ../map + ./ipc_live_test --capacity ../strans ../map ./daemon_collision_test ../strans ../map ./daemon_failure_test ../strans ./daemon_restart_test ../strans ../map -$(PROG): $(OBJS) - $(LD) -o $@ $(OBJS) $(LIBS) +$(PROG): unit_test.o $(COMMONOBJ) + $(LD) -o $@ unit_test.o $(COMMONOBJ) $(LIBS) + +$(STRESS): stress_test.o $(COMMONOBJ) + $(LD) -o $@ stress_test.o $(COMMONOBJ) $(LIBS) + +stress_test.o: unit_test.c + $(CC) $(CFLAGS) -DSTRESS -c -o $@ $< ibus_live_test: ibus_live_test.c $(HOSTCC) -std=c99 -Wall -Wextra -O2 -g $(DBUS_CFLAGS) -o $@ $< $(DBUS_LIBS) @@ -60,6 +77,9 @@ gtk_live_test: gtk_live_test.c ../ipc.c ../ipc.h ../gtk/im-strans.so: ../gtk/main.c ../ipc.c ../ipc.h $(MAKE) -C ../gtk +../strans: + $(MAKE) -C .. strans + xim_live_test: xim_live_test.c ../ipc.c ../ipc.h $(HOSTCC) -std=c99 -Wall -Wextra -O2 -g -I.. $(X11_CFLAGS) -o $@ \ xim_live_test.c ../ipc.c $(X11_LIBS) @@ -78,7 +98,8 @@ daemon_restart_test: daemon_restart_test.c ../ipc.c ../ipc.h $(HOSTCC) -std=c99 -Wall -Wextra -O2 -g -I.. $(DBUS_CFLAGS) -o $@ \ daemon_restart_test.c ../ipc.c $(DBUS_LIBS) -$(TESTOBJ): test.h ../dat.h ../fn.h ../ipc.h ../cutest/cutest.h +unit_test.o stress_test.o $(TESTOBJ): test.h ../dat.h ../fn.h ../ipc.h \ + ../cutest/cutest.h engine_test.o: ../strans.c ibus_test.o: CFLAGS += $(IBUS_CFLAGS) ibus_test.o: ../ibus.c @@ -100,6 +121,6 @@ unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h $(CC) $(CFLAGS) -c -o $@ $< clean: - rm -f $(OBJS) $(PROG) $(LIVE) + rm -f $(OBJS) $(PROG) $(STRESS) $(LIVE) -.PHONY: all check test clean +.PHONY: all check check-live check-stress test clean diff --git a/tests/daemon_collision_test.c b/tests/daemon_collision_test.c index a24c0e3..bffa628 100644 --- a/tests/daemon_collision_test.c +++ b/tests/daemon_collision_test.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -37,8 +36,6 @@ struct Test pid_t second; int firsterrfd; int seconderrfd; - int notifyfd; - int buswd; int ipcfirst; int ipcnew; DBusConnection *busfirst; @@ -52,10 +49,6 @@ struct Test char socket[384]; char addrfile[512]; char address[512]; - char addrcontents[2048]; - size_t naddrcontents; - struct stat socketst; - struct stat addrst; char firsterr[8192]; size_t nfirsterr; char seconderr[8192]; @@ -149,8 +142,6 @@ setup(Test *t) t->second = -1; t->firsterrfd = -1; t->seconderrfd = -1; - t->notifyfd = -1; - t->buswd = -1; t->ipcfirst = -1; t->ipcnew = -1; snprintf(t->root, sizeof t->root, "/tmp/strans-collision.XXXXXX"); @@ -174,13 +165,6 @@ setup(Test *t) if(!makedir(t->runtime) || !makedir(t->config) || !makedir(t->ibus) || !makedir(t->bus) || !makedir(t->home)) return 0; - t->notifyfd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if(t->notifyfd < 0) - return fail("inotify_init1: %s", strerror(errno)); - t->buswd = inotify_add_watch(t->notifyfd, t->bus, - IN_CREATE|IN_MOVED_TO|IN_ATTRIB|IN_DELETE); - if(t->buswd < 0) - return fail("watch IBus directory: %s", strerror(errno)); return 1; } @@ -216,7 +200,8 @@ startchild(Test *t, char *program, char *mapdir, pid_t *child, int *errfd) setenv("XDG_CONFIG_HOME", t->config, 1) < 0 || setenv("HOME", t->home, 1) < 0 || unsetenv("DISPLAY") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || - unsetenv("IBUS_ADDRESS") < 0){ + unsetenv("IBUS_ADDRESS") < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0){ dprintf(STDERR_FILENO, "set daemon environment: %s\n", strerror(errno)); _exit(126); @@ -231,40 +216,6 @@ startchild(Test *t, char *program, char *mapdir, pid_t *child, int *errfd) return 1; } -static int -drainnotify(Test *t, int trackbus, int *changed) -{ - char buf[4096]; - struct inotify_event *ev; - ssize_t n; - size_t off; - - for(;;){ - n = read(t->notifyfd, buf, sizeof buf); - if(n < 0 && errno == EINTR) - continue; - if(n < 0 && errno == EAGAIN) - return 1; - if(n < 0) - return fail("read endpoint watches: %s", strerror(errno)); - if(n == 0) - return 1; - for(off = 0; off + sizeof *ev <= (size_t)n; - off += sizeof *ev + ev->len){ - ev = (struct inotify_event*)(buf + off); - if(off + sizeof *ev + ev->len > (size_t)n) - return fail("truncated inotify event"); - if(ev->mask & IN_Q_OVERFLOW) - return fail("endpoint watch queue overflowed"); - if(trackbus && changed != NULL && ev->wd == t->buswd && - ev->len != 0 && - (ev->mask & (IN_CREATE|IN_MOVED_TO|IN_DELETE|IN_ATTRIB))){ - *changed = 1; - } - } - } -} - static int findaddress(Test *t) { @@ -381,6 +332,19 @@ parseaddress(char *contents, size_t ncontents, char *address, size_t naddress, return 1; } +static int +addressunchanged(Test *t) +{ + char contents[2048], address[512]; + size_t ncontents; + + if(!readfile(t->addrfile, contents, sizeof contents, &ncontents) || + !parseaddress(contents, ncontents, address, sizeof address, t->first)) + return 0; + return strcmp(address, t->address) == 0 || + fail("first private IBus address changed after collision"); +} + static int childalive(pid_t *pid, char *which) { @@ -410,9 +374,9 @@ childalive(pid_t *pid, char *which) static int waitready(Test *t) { - struct pollfd pfd[2]; + struct pollfd pfd; struct stat st; - char contents[2048], address[512]; + char contents[2048]; size_t ncontents; int n, count, socketready, timeout; int64_t deadline; @@ -426,7 +390,7 @@ waitready(Test *t) return 0; if(socketready && count == 1 && readfile(t->addrfile, contents, sizeof contents, &ncontents) && - parseaddress(contents, ncontents, address, sizeof address, + parseaddress(contents, ncontents, t->address, sizeof t->address, t->first)) return 1; if(!childalive(&t->first, "first")) @@ -434,43 +398,20 @@ waitready(Test *t) timeout = leftms(deadline); if(timeout == 0) return fail("timed out waiting for both daemon endpoints"); - pfd[0].fd = t->notifyfd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = t->firsterrfd; - pfd[1].events = POLLIN|POLLHUP; - pfd[1].revents = 0; - n = poll(pfd, 2, timeout); + pfd.fd = t->firsterrfd; + pfd.events = POLLIN|POLLHUP; + pfd.revents = 0; + n = poll(&pfd, 1, timeout > 20 ? 20 : timeout); if(n < 0 && errno == EINTR) continue; if(n < 0) return fail("poll daemon readiness: %s", strerror(errno)); - if(pfd[1].revents != 0) + if(pfd.revents != 0) readpipe(t->firsterrfd, t->firsterr, sizeof t->firsterr, &t->nfirsterr); - if((pfd[0].revents & POLLIN) && !drainnotify(t, 0, NULL)) - return 0; } } -static int -snapshot(Test *t) -{ - if(lstat(t->socket, &t->socketst) < 0) - return fail("stat first IPC socket: %s", strerror(errno)); - if(!S_ISSOCK(t->socketst.st_mode) || (t->socketst.st_mode & 0777) != 0600) - return fail("first IPC endpoint is not a protected socket"); - if(lstat(t->addrfile, &t->addrst) < 0) - return fail("stat first IBus address file: %s", strerror(errno)); - if(!S_ISREG(t->addrst.st_mode) || (t->addrst.st_mode & 0777) != 0600) - return fail("first IBus address is not a protected regular file"); - if(!readfile(t->addrfile, t->addrcontents, sizeof t->addrcontents, - &t->naddrcontents)) - return 0; - return parseaddress(t->addrcontents, t->naddrcontents, t->address, - sizeof t->address, t->first); -} - static int connectsocket(char *path, int64_t deadline) { @@ -750,6 +691,8 @@ killowned(pid_t *pid, int *status) if(n != *pid){ fail("reap %ld after SIGKILL: %s", (long)*pid, n < 0 ? strerror(errno) : "wrong child"); + if(n < 0 && errno == ECHILD) + *pid = -1; ok = 0; }else *pid = -1; @@ -757,9 +700,9 @@ killowned(pid_t *pid, int *status) } static int -waitsecond(Test *t, int *changed) +waitsecond(Test *t) { - struct pollfd pfd[2]; + struct pollfd pfd; int n, status, timeout; int64_t deadline; @@ -772,8 +715,6 @@ waitsecond(Test *t, int *changed) t->second = -1; readpipe(t->seconderrfd, t->seconderr, sizeof t->seconderr, &t->nseconderr); - if(!drainnotify(t, 1, changed)) - return 0; if(!WIFEXITED(status) || WEXITSTATUS(status) == 0) return fail("second daemon did not exit unsuccessfully: %#x", status); @@ -797,118 +738,28 @@ waitsecond(Test *t, int *changed) return 0; return 0; } - pfd[0].fd = t->notifyfd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = t->seconderrfd; - pfd[1].events = POLLIN|POLLHUP; - pfd[1].revents = 0; - n = poll(pfd, 2, timeout); + pfd.fd = t->seconderrfd; + pfd.events = POLLIN|POLLHUP; + pfd.revents = 0; + n = poll(&pfd, 1, timeout > 20 ? 20 : timeout); if(n < 0 && errno == EINTR) continue; if(n < 0) return fail("poll second daemon: %s", strerror(errno)); - if(pfd[1].revents != 0) + if(pfd.revents != 0) readpipe(t->seconderrfd, t->seconderr, sizeof t->seconderr, &t->nseconderr); - if((pfd[0].revents & POLLIN) && - !drainnotify(t, 1, changed)) - return 0; } } -static int -checksocket(Test *t) -{ - struct stat st; - - if(lstat(t->socket, &st) < 0) - return fail("first IPC socket disappeared: %s", strerror(errno)); - if(!S_ISSOCK(st.st_mode) || st.st_mode != t->socketst.st_mode || - st.st_dev != t->socketst.st_dev || st.st_ino != t->socketst.st_ino) - return fail("first IPC socket identity or protection changed"); - return 1; -} - -static int -checkaddress(Test *t) -{ - struct stat st; - char contents[2048], address[512]; - size_t ncontents; - - if(lstat(t->addrfile, &st) < 0) - return fail("first IBus address file disappeared: %s", - strerror(errno)); - if(st.st_mode != t->addrst.st_mode || st.st_dev != t->addrst.st_dev || - st.st_ino != t->addrst.st_ino) - return fail("first IBus address file identity or protection changed"); - if(!readfile(t->addrfile, contents, sizeof contents, &ncontents)) - return 0; - if(ncontents != t->naddrcontents || - memcmp(contents, t->addrcontents, ncontents) != 0) - return fail("first IBus address file contents changed"); - if(!parseaddress(contents, ncontents, address, sizeof address, t->first)) - return 0; - return strcmp(address, t->address) == 0 || - fail("first private IBus address changed"); -} - -static int -onlyaddress(Test *t) -{ - DIR *dir; - struct dirent *de; - char expected[512]; - char *base; - int count, ok; - - base = strrchr(t->addrfile, '/'); - if(base == NULL) - return fail("invalid recorded IBus address path"); - if(snprintf(expected, sizeof expected, "%s", base + 1) - >= (int)sizeof expected) - return fail("recorded IBus address name is too long"); - dir = opendir(t->bus); - if(dir == NULL) - return fail("open IBus directory after collision: %s", - strerror(errno)); - count = 0; - ok = 1; - errno = 0; - while((de = readdir(dir)) != NULL){ - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - count++; - if(strcmp(de->d_name, expected) != 0){ - fail("unexpected IBus directory entry %s", de->d_name); - ok = 0; - } - } - if(errno != 0){ - fail("read IBus directory after collision: %s", strerror(errno)); - ok = 0; - } - if(closedir(dir) < 0){ - fail("close IBus directory after collision: %s", strerror(errno)); - ok = 0; - } - if(count != 1){ - fail("IBus directory contains %d entries after collision", count); - ok = 0; - } - return ok; -} - static int runcollision(Test *t, char *program, char *mapdir) { char firsthello[32], newhello[32]; - char firstpath[96], persistentpath[96], newpath[96]; - int changed; + char persistentpath[96], newpath[96]; if(!startchild(t, program, mapdir, &t->first, &t->firsterrfd) || - !waitready(t) || !snapshot(t)) + !waitready(t)) return 0; t->ipcfirst = connectsocket(t->socket, nowms() + Calltimeout); if(t->ipcfirst < 0) @@ -916,22 +767,12 @@ runcollision(Test *t, char *program, char *mapdir) if(!ipcprobe(t->ipcfirst, "persistent pre-collision")) return 0; t->busfirst = openbus(t->address); - if(t->busfirst == NULL || - !hello(t->busfirst, firsthello, sizeof firsthello, - "persistent pre-collision Hello") || - !createcontext(t->busfirst, firstpath, sizeof firstpath, - "persistent pre-collision context")) + if(t->busfirst == NULL || !hello(t->busfirst, firsthello, + sizeof firsthello, "persistent pre-collision Hello")) return 0; - if(!drainnotify(t, 0, NULL)) - return 0; - changed = 0; if(!startchild(t, program, mapdir, &t->second, &t->seconderrfd) || - !waitsecond(t, &changed)) - return 0; - if(changed) - return fail("second daemon touched the first IBus address directory"); - if(!childalive(&t->first, "first") || !checksocket(t) || - !checkaddress(t) || !onlyaddress(t)) + !waitsecond(t) || !childalive(&t->first, "first") || + !addressunchanged(t)) return 0; if(!ipcprobe(t->ipcfirst, "persistent post-collision")) return 0; @@ -1131,18 +972,6 @@ cleanup(Test *t) } t->seconderrfd = -1; } - if(t->notifyfd >= 0){ - if(t->buswd >= 0 && - inotify_rm_watch(t->notifyfd, t->buswd) < 0 && errno != EINVAL){ - fail("remove IBus watch: %s", strerror(errno)); - ok = 0; - } - if(close(t->notifyfd) < 0){ - fail("close endpoint watches: %s", strerror(errno)); - ok = 0; - } - t->notifyfd = -1; - } if(t->socket[0] != '\0' && unlink(t->socket) < 0 && errno != ENOENT){ fail("remove IPC socket %s: %s", t->socket, strerror(errno)); ok = 0; diff --git a/tests/daemon_failure_test.c b/tests/daemon_failure_test.c index 7df7a33..128ea9e 100644 --- a/tests/daemon_failure_test.c +++ b/tests/daemon_failure_test.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -19,19 +18,14 @@ enum { - Failtimeout = 8000, + Failtimeout = 4000, }; typedef struct Test Test; - struct Test { pid_t child; int errfd; - int notifyfd; - int runtimewd; - int buswd; - int endpointclean; char root[256]; char runtime[320]; char config[320]; @@ -40,14 +34,10 @@ struct Test char home[320]; char socket[384]; char badmap[320]; - char expected[768]; char err[4096]; size_t nerr; }; -static uint32_t endpointmask = IN_CREATE|IN_MOVED_TO|IN_MOVED_FROM|IN_ATTRIB| - IN_DELETE|IN_MODIFY|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF; - static int fail(char *fmt, ...) { @@ -82,9 +72,7 @@ leftms(int64_t deadline) n = deadline - n; if(n <= 0) return 0; - if(n > 0x7fffffff) - return 0x7fffffff; - return n; + return n > INT_MAX ? INT_MAX : n; } static int @@ -100,9 +88,7 @@ readerrors(Test *t) { ssize_t n; - if(t->errfd < 0) - return; - while(t->nerr + 1 < sizeof t->err){ + while(t->errfd >= 0 && t->nerr + 1 < sizeof t->err){ n = read(t->errfd, t->err + t->nerr, sizeof t->err - t->nerr - 1); if(n > 0){ @@ -116,26 +102,12 @@ readerrors(Test *t) t->err[t->nerr] = '\0'; } -static void -showerrors(Test *t) -{ - readerrors(t); - if(t->nerr != 0) - fprintf(stderr, "daemon_failure_test: daemon stderr:\n%s", t->err); -} - static int setup(Test *t) { memset(t, 0, sizeof *t); t->child = -1; t->errfd = -1; - t->notifyfd = -1; - t->runtimewd = -1; - t->buswd = -1; - t->endpointclean = 1; - if(nowms() < 0) - return fail("read monotonic clock: %s", strerror(errno)); snprintf(t->root, sizeof t->root, "/tmp/strans-failure.XXXXXX"); if(mkdtemp(t->root) == NULL){ t->root[0] = '\0'; @@ -154,24 +126,10 @@ setup(Test *t) snprintf(t->socket, sizeof t->socket, "%s/strans.sock", t->runtime) >= (int)sizeof t->socket || snprintf(t->badmap, sizeof t->badmap, "%s/missing-map", t->root) - >= (int)sizeof t->badmap || - snprintf(t->expected, sizeof t->expected, - "strans: can't open: %s/hira.map\n", t->badmap) - >= (int)sizeof t->expected) + >= (int)sizeof t->badmap) return fail("temporary path is too long"); - if(!makedir(t->runtime) || !makedir(t->config) || !makedir(t->ibus) || - !makedir(t->bus) || !makedir(t->home)) - return 0; - t->notifyfd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if(t->notifyfd < 0) - return fail("inotify_init1: %s", strerror(errno)); - t->runtimewd = inotify_add_watch(t->notifyfd, t->runtime, endpointmask); - if(t->runtimewd < 0) - return fail("watch runtime directory: %s", strerror(errno)); - t->buswd = inotify_add_watch(t->notifyfd, t->bus, endpointmask); - if(t->buswd < 0) - return fail("watch IBus directory: %s", strerror(errno)); - return 1; + return makedir(t->runtime) && makedir(t->config) && makedir(t->ibus) && + makedir(t->bus) && makedir(t->home); } static int @@ -205,11 +163,9 @@ startchild(Test *t, char *program) setenv("XDG_CONFIG_HOME", t->config, 1) < 0 || setenv("HOME", t->home, 1) < 0 || unsetenv("DISPLAY") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || - unsetenv("IBUS_ADDRESS") < 0){ - dprintf(STDERR_FILENO, "set daemon environment: %s\n", - strerror(errno)); + unsetenv("IBUS_ADDRESS") < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0) _exit(126); - } execl(program, program, t->badmap, (char*)0); dprintf(STDERR_FILENO, "exec %s: %s\n", program, strerror(errno)); _exit(127); @@ -220,330 +176,92 @@ startchild(Test *t, char *program) return 1; } -static int -drainnotify(Test *t) -{ - char buf[4096]; - char *where; - struct inotify_event *ev; - ssize_t n; - size_t off; - int ok; - - ok = 1; - for(;;){ - n = read(t->notifyfd, buf, sizeof buf); - if(n < 0 && errno == EINTR) - continue; - if(n < 0 && errno == EAGAIN) - return ok; - if(n < 0) - return fail("read endpoint watches: %s", strerror(errno)); - if(n == 0) - return ok; - for(off = 0; off + sizeof *ev <= (size_t)n; - off += sizeof *ev + ev->len){ - ev = (struct inotify_event*)(buf + off); - if(off + sizeof *ev + ev->len > (size_t)n) - return fail("truncated inotify event"); - if(ev->mask & IN_Q_OVERFLOW){ - t->endpointclean = 0; - fail("endpoint watch queue overflowed"); - ok = 0; - continue; - } - if(ev->mask & (IN_IGNORED|IN_UNMOUNT)){ - t->endpointclean = 0; - fail("endpoint watch became invalid: %#x", ev->mask); - ok = 0; - continue; - } - if(ev->wd != t->runtimewd && ev->wd != t->buswd) - continue; - if(!(ev->mask & endpointmask)) - continue; - where = ev->wd == t->runtimewd ? "runtime" : "IBus"; - t->endpointclean = 0; - fail("unexpected %s endpoint event %#x for %s", where, - ev->mask, ev->len != 0 ? ev->name : "directory"); - ok = 0; - } - if(off != (size_t)n) - return fail("truncated inotify event buffer"); - } -} - -static int -killowned(Test *t, int *status) -{ - pid_t pid; - int n, ok; - - if(t->child <= 0) - return 1; - pid = t->child; - ok = 1; - if(kill(pid, SIGKILL) < 0 && errno != ESRCH){ - fail("kill -9 %ld: %s", (long)pid, strerror(errno)); - ok = 0; - } - do - n = waitpid(pid, status, 0); - while(n < 0 && errno == EINTR); - if(n != pid){ - fail("reap %ld after SIGKILL: %s", (long)pid, - n < 0 ? strerror(errno) : "wrong child"); - ok = 0; - }else - t->child = -1; - return ok; -} - static int waitfailed(Test *t) { - struct pollfd pfd[2]; - pid_t pid; - int n, ok, status, timeout; + struct pollfd pfd; + int n, status; int64_t deadline; - pid = t->child; - ok = 1; deadline = nowms(); - if(deadline < 0){ - fail("read monotonic clock before waiting for daemon: %s", - strerror(errno)); - killowned(t, &status); - readerrors(t); - drainnotify(t); - return 0; - } + if(deadline < 0) + return fail("read monotonic clock: %s", strerror(errno)); deadline += Failtimeout; for(;;){ - do - n = waitpid(pid, &status, WNOHANG); - while(n < 0 && errno == EINTR); - if(n == pid){ + n = waitpid(t->child, &status, WNOHANG); + if(n == t->child){ t->child = -1; readerrors(t); - if(!drainnotify(t)) - ok = 0; break; } - if(n < 0){ - fail("waitpid failed daemon %ld: %s", (long)pid, - strerror(errno)); - if(errno == ECHILD){ - t->child = -1; - readerrors(t); - drainnotify(t); - }else{ - killowned(t, &status); - readerrors(t); - drainnotify(t); - } - return 0; - } - timeout = leftms(deadline); - if(timeout < 0){ - fail("read monotonic clock while waiting for daemon: %s", - strerror(errno)); - killowned(t, &status); - readerrors(t); - drainnotify(t); - return 0; - } - if(timeout == 0){ - fail("daemon %ld did not exit after map initialization failure", - (long)pid); - killowned(t, &status); - return 0; - } - pfd[0].fd = t->notifyfd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = t->errfd; - pfd[1].events = POLLIN|POLLHUP; - pfd[1].revents = 0; - n = poll(pfd, 2, timeout); if(n < 0 && errno == EINTR) continue; if(n < 0){ - fail("poll failed daemon: %s", strerror(errno)); - killowned(t, &status); - readerrors(t); - drainnotify(t); - return 0; + if(errno == ECHILD) + t->child = -1; + return fail("waitpid: %s", strerror(errno)); } - if(pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)){ - fail("endpoint watch became unusable: %#x", pfd[0].revents); - killowned(t, &status); - readerrors(t); - drainnotify(t); - return 0; - } - if(pfd[1].revents & (POLLERR|POLLNVAL)){ - fail("daemon stderr pipe became unusable: %#x", - pfd[1].revents); - killowned(t, &status); - readerrors(t); - drainnotify(t); - return 0; - } - if((pfd[0].revents & POLLIN) && !drainnotify(t)) - ok = 0; - if(pfd[1].revents & (POLLIN|POLLHUP)) + n = leftms(deadline); + if(n < 0) + return fail("read monotonic clock: %s", strerror(errno)); + if(n == 0) + return fail("daemon did not exit after map initialization failure"); + pfd.fd = t->errfd; + pfd.events = POLLIN|POLLHUP; + pfd.revents = 0; + n = poll(&pfd, 1, n); + if(n < 0 && errno == EINTR) + continue; + if(n < 0) + return fail("poll daemon: %s", strerror(errno)); + if(n > 0) readerrors(t); } - if(!WIFEXITED(status) || WEXITSTATUS(status) != 1) + if(!WIFEXITED(status) || WEXITSTATUS(status) == 0) return fail("daemon map failure had wait status %#x", status); - if(strcmp(t->err, t->expected) != 0) - return fail("daemon did not report the expected map initialization failure"); - return ok && t->endpointclean; + if(strstr(t->err, "can't open") == NULL || strstr(t->err, t->badmap) == NULL) + return fail("daemon did not report the missing map directory"); + return 1; } static int -dirempty(char *path, char *which) +emptydir(char *path) { DIR *dir; struct dirent *de; - int count, ok; + int empty; dir = opendir(path); if(dir == NULL) - return fail("open %s directory %s: %s", which, path, strerror(errno)); - count = 0; - ok = 1; + return fail("open %s: %s", path, strerror(errno)); + empty = 1; errno = 0; - while((de = readdir(dir)) != NULL){ - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - count++; - fail("unexpected %s directory entry %s", which, de->d_name); - ok = 0; - errno = 0; - } + while((de = readdir(dir)) != NULL) + if(strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0){ + fail("unexpected endpoint %s/%s", path, de->d_name); + empty = 0; + } if(errno != 0){ - fail("read %s directory %s: %s", which, path, strerror(errno)); - ok = 0; + fail("read %s: %s", path, strerror(errno)); + empty = 0; } if(closedir(dir) < 0){ - fail("close %s directory %s: %s", which, path, strerror(errno)); - ok = 0; + fail("close %s: %s", path, strerror(errno)); + empty = 0; } - if(count != 0) - ok = 0; - return ok; + return empty; } static int checkendpoints(Test *t) { struct stat st; - int ok; - ok = 1; - errno = 0; - if(lstat(t->socket, &st) == 0){ - fail("IPC endpoint exists after failed startup: %s", t->socket); - ok = 0; - }else if(errno != ENOENT){ - fail("lstat failed IPC endpoint: %s", strerror(errno)); - ok = 0; - } - if(!dirempty(t->runtime, "runtime")) - ok = 0; - if(!dirempty(t->bus, "IBus")) - ok = 0; - return ok && t->endpointclean; -} - -static int clearfd(int); - -static int -removeentry(int dirfd, char *name) -{ - struct stat st; - int fd, ok; - - if(fstatat(dirfd, name, &st, AT_SYMLINK_NOFOLLOW) < 0) - return fail("stat cleanup entry %s: %s", name, strerror(errno)); - if(!S_ISDIR(st.st_mode)){ - if(unlinkat(dirfd, name, 0) == 0 || errno == ENOENT) - return 1; - return fail("remove cleanup entry %s: %s", name, strerror(errno)); - } - fd = openat(dirfd, name, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); - if(fd < 0) - return fail("open cleanup directory %s: %s", name, strerror(errno)); - ok = clearfd(fd); - if(close(fd) < 0){ - fail("close cleanup directory %s: %s", name, strerror(errno)); - ok = 0; - } - if(unlinkat(dirfd, name, AT_REMOVEDIR) < 0 && errno != ENOENT){ - fail("remove cleanup directory %s: %s", name, strerror(errno)); - ok = 0; - } - return ok; -} - -static int -clearfd(int fd) -{ - DIR *dir; - struct dirent *de; - int copy, ok; - - copy = dup(fd); - if(copy < 0) - return fail("duplicate cleanup directory: %s", strerror(errno)); - dir = fdopendir(copy); - if(dir == NULL){ - close(copy); - return fail("open cleanup directory stream: %s", strerror(errno)); - } - ok = 1; - errno = 0; - while((de = readdir(dir)) != NULL){ - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - if(!removeentry(fd, de->d_name)) - ok = 0; - errno = 0; - } - if(errno != 0){ - fail("read cleanup directory: %s", strerror(errno)); - ok = 0; - } - if(closedir(dir) < 0){ - fail("close cleanup directory stream: %s", strerror(errno)); - ok = 0; - } - return ok; -} - -static int -removeroot(Test *t) -{ - int fd, ok; - - if(t->root[0] == '\0') - return 1; - fd = open(t->root, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); - if(fd < 0) - return errno == ENOENT || fail("open cleanup root %s: %s", t->root, - strerror(errno)); - ok = clearfd(fd); - if(close(fd) < 0){ - fail("close cleanup root %s: %s", t->root, strerror(errno)); - ok = 0; - } - if(rmdir(t->root) < 0 && errno != ENOENT){ - fail("rmdir %s: %s", t->root, strerror(errno)); - ok = 0; - } - return ok; + if(lstat(t->socket, &st) == 0) + return fail("IPC endpoint exists after failed startup: %s", t->socket); + if(errno != ENOENT) + return fail("lstat IPC endpoint: %s", strerror(errno)); + return emptydir(t->runtime) && emptydir(t->bus); } static int @@ -553,53 +271,31 @@ cleanup(Test *t) ok = 1; if(t->child > 0){ + kill(t->child, SIGKILL); do - n = waitpid(t->child, &status, WNOHANG); + n = waitpid(t->child, &status, 0); while(n < 0 && errno == EINTR); - if(n == t->child) - t->child = -1; - else if(n == 0){ - fail("failed-start daemon still running during cleanup"); - ok = 0; - if(!killowned(t, &status)) - ok = 0; - }else{ - fail("check failed-start daemon during cleanup: %s", - strerror(errno)); - ok = 0; - if(errno == ECHILD) - t->child = -1; - else if(!killowned(t, &status)) - ok = 0; - } + if(n != t->child) + ok = fail("reap child: %s", strerror(errno)); + t->child = -1; } readerrors(t); - if(t->errfd >= 0){ - if(close(t->errfd) < 0){ - fail("close daemon stderr: %s", strerror(errno)); - ok = 0; - } - t->errfd = -1; - } - if(t->notifyfd >= 0){ - if(t->runtimewd >= 0 && - inotify_rm_watch(t->notifyfd, t->runtimewd) < 0 && errno != EINVAL){ - fail("remove runtime watch: %s", strerror(errno)); - ok = 0; - } - if(t->buswd >= 0 && - inotify_rm_watch(t->notifyfd, t->buswd) < 0 && errno != EINVAL){ - fail("remove IBus watch: %s", strerror(errno)); - ok = 0; - } - if(close(t->notifyfd) < 0){ - fail("close endpoint watches: %s", strerror(errno)); - ok = 0; - } - t->notifyfd = -1; - } - if(!removeroot(t)) - ok = 0; + if(t->errfd >= 0 && close(t->errfd) < 0) + ok = fail("close stderr: %s", strerror(errno)); + if(t->socket[0] != '\0' && unlink(t->socket) < 0 && errno != ENOENT) + ok = fail("remove socket: %s", strerror(errno)); + if(t->bus[0] != '\0' && rmdir(t->bus) < 0 && errno != ENOENT) + ok = fail("remove bus directory: %s", strerror(errno)); + if(t->ibus[0] != '\0' && rmdir(t->ibus) < 0 && errno != ENOENT) + ok = fail("remove IBus directory: %s", strerror(errno)); + if(t->config[0] != '\0' && rmdir(t->config) < 0 && errno != ENOENT) + ok = fail("remove config directory: %s", strerror(errno)); + if(t->runtime[0] != '\0' && rmdir(t->runtime) < 0 && errno != ENOENT) + ok = fail("remove runtime directory: %s", strerror(errno)); + if(t->home[0] != '\0' && rmdir(t->home) < 0 && errno != ENOENT) + ok = fail("remove home directory: %s", strerror(errno)); + if(t->root[0] != '\0' && rmdir(t->root) < 0 && errno != ENOENT) + ok = fail("remove root directory: %s", strerror(errno)); return ok; } @@ -616,17 +312,15 @@ main(int argc, char **argv) ok = setup(&test); if(ok) ok = startchild(&test, argv[1]); - if(ok){ - ok = waitfailed(&test); - if(test.child <= 0 && !checkendpoints(&test)) - ok = 0; - } + if(ok) + ok = waitfailed(&test) && checkendpoints(&test); if(!cleanup(&test)) ok = 0; if(!ok){ - showerrors(&test); + if(test.nerr != 0) + fprintf(stderr, "daemon_failure_test: daemon stderr:\n%s", test.err); return 1; } - printf("failed daemon startup published no endpoints: ok\n"); + printf("failed daemon startup leaves no endpoints: ok\n"); return 0; } diff --git a/tests/daemon_restart_test.c b/tests/daemon_restart_test.c index 2f83831..e6c2cdb 100644 --- a/tests/daemon_restart_test.c +++ b/tests/daemon_restart_test.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -28,10 +27,6 @@ enum Starttimeout = 8000, Stoptimeout = 3000, Ctrlmask = 1<<2, - - Phaseignore, - Phasequiet, - Phaserecover, }; typedef struct Test Test; @@ -45,19 +40,9 @@ struct Test int firsterrfd; int seconderrfd; int helpererrfd; - int notifyfd; - int runtimewd; - int buswd; int ipcfirst; int ipcnew; int busfirstfd; - int phase; - int socketdeleted; - int socketcreated; - int bustempcreated; - int bustempmoved; - int buspublished; - uint32_t buscookie; DBusConnection *busfirst; DBusConnection *busnew; char root[256]; @@ -68,18 +53,8 @@ struct Test char home[320]; char socket[384]; char addrfile[512]; - char addrbase[256]; char firstaddress[512]; char secondaddress[512]; - char firstcontents[2048]; - size_t nfirstcontents; - char secondcontents[2048]; - size_t nsecondcontents; - char bustemp[256]; - struct stat firstsocketst; - struct stat firstaddrst; - struct stat secondsocketst; - struct stat secondaddrst; char firsterr[8192]; size_t nfirsterr; char seconderr[8192]; @@ -88,9 +63,6 @@ struct Test size_t nhelpererr; }; -static uint32_t watchmask = IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MOVED_FROM| - IN_MOVED_TO|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF; - static int fail(char *fmt, ...) { @@ -191,13 +163,9 @@ setup(Test *t) t->firsterrfd = -1; t->seconderrfd = -1; t->helpererrfd = -1; - t->notifyfd = -1; - t->runtimewd = -1; - t->buswd = -1; t->ipcfirst = -1; t->ipcnew = -1; t->busfirstfd = -1; - t->phase = Phaseignore; memset(&sa, 0, sizeof sa); sa.sa_handler = SIG_DFL; if(sigemptyset(&sa.sa_mask) < 0 || sigaction(SIGCHLD, &sa, NULL) < 0) @@ -225,15 +193,6 @@ setup(Test *t) if(!makedir(t->runtime) || !makedir(t->config) || !makedir(t->ibus) || !makedir(t->bus) || !makedir(t->home)) return 0; - t->notifyfd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if(t->notifyfd < 0) - return fail("inotify_init1: %s", strerror(errno)); - t->runtimewd = inotify_add_watch(t->notifyfd, t->runtime, watchmask); - if(t->runtimewd < 0) - return fail("watch runtime directory: %s", strerror(errno)); - t->buswd = inotify_add_watch(t->notifyfd, t->bus, watchmask); - if(t->buswd < 0) - return fail("watch IBus directory: %s", strerror(errno)); return 1; } @@ -284,7 +243,8 @@ startchild(Test *t, char *program, char *mapdir, pid_t *child, int *errfd) setenv("XDG_CONFIG_HOME", t->config, 1) < 0 || setenv("HOME", t->home, 1) < 0 || unsetenv("DISPLAY") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || - unsetenv("IBUS_ADDRESS") < 0){ + unsetenv("IBUS_ADDRESS") < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0){ dprintf(STDERR_FILENO, "set daemon environment: %s\n", strerror(errno)); _exit(126); @@ -299,150 +259,6 @@ startchild(Test *t, char *program, char *mapdir, pid_t *child, int *errfd) return 1; } -static int -runtimeevent(Test *t, struct inotify_event *ev) -{ - uint32_t mask; - - if(ev->len == 0 || strcmp(ev->name, "strans.sock") != 0) - return fail("unexpected runtime entry event %#x for %s", ev->mask, - ev->len != 0 ? ev->name : "directory"); - mask = ev->mask & watchmask; - if(mask & IN_DELETE){ - if(t->socketdeleted || t->socketcreated) - return fail("IPC socket deletion occurred out of order"); - t->socketdeleted = 1; - mask &= ~IN_DELETE; - } - if(mask & IN_CREATE){ - if(!t->socketdeleted || t->socketcreated) - return fail("IPC socket creation occurred before stale deletion"); - t->socketcreated = 1; - mask &= ~IN_CREATE; - } - if(mask & IN_ATTRIB){ - if(!t->socketcreated) - return fail("IPC socket protection occurred before recreation"); - mask &= ~IN_ATTRIB; - } - if(mask != 0) - return fail("unexpected IPC recovery event %#x", ev->mask); - return 1; -} - -static int -tempname(Test *t, char *name) -{ - size_t n, nn; - - n = strlen(t->addrbase); - nn = strlen(name); - if(nn <= n + 5) - return 0; - return strncmp(name, t->addrbase, n) == 0 && - strncmp(name + n, ".tmp.", 5) == 0; -} - -static int -busevent(Test *t, struct inotify_event *ev) -{ - uint32_t mask; - - if(ev->len == 0) - return fail("unexpected unnamed IBus directory event %#x", ev->mask); - mask = ev->mask & watchmask; - if(tempname(t, ev->name)){ - if(mask & IN_CREATE){ - if(t->bustempcreated || snprintf(t->bustemp, sizeof t->bustemp, - "%s", ev->name) >= (int)sizeof t->bustemp) - return fail("invalid replacement IBus temporary file"); - t->bustempcreated = 1; - mask &= ~IN_CREATE; - } - if(!t->bustempcreated || strcmp(ev->name, t->bustemp) != 0) - return fail("unowned IBus temporary file event for %s", ev->name); - if(mask & IN_ATTRIB) - mask &= ~IN_ATTRIB; - if(mask & IN_CLOSE_WRITE) - mask &= ~IN_CLOSE_WRITE; - if(mask & IN_MOVED_FROM){ - if(t->bustempmoved || ev->cookie == 0) - return fail("invalid IBus temporary-file rename"); - t->bustempmoved = 1; - t->buscookie = ev->cookie; - mask &= ~IN_MOVED_FROM; - } - if(mask != 0) - return fail("unexpected IBus temporary-file event %#x", ev->mask); - return 1; - } - if(strcmp(ev->name, t->addrbase) != 0) - return fail("unexpected IBus recovery entry %s", ev->name); - if(mask == IN_MOVED_TO && t->bustempmoved && ev->cookie == t->buscookie){ - t->buspublished = 1; - return 1; - } - return fail("IBus address replacement was not one atomic rename: %#x", - ev->mask); -} - -static int -drainnotify(Test *t) -{ - char buf[4096]; - struct inotify_event *ev; - ssize_t n; - size_t off; - int ok; - - ok = 1; - for(;;){ - n = read(t->notifyfd, buf, sizeof buf); - if(n < 0 && errno == EINTR) - continue; - if(n < 0 && errno == EAGAIN) - return ok; - if(n < 0) - return fail("read endpoint watches: %s", strerror(errno)); - if(n == 0) - return ok; - for(off = 0; off + sizeof *ev <= (size_t)n; - off += sizeof *ev + ev->len){ - ev = (struct inotify_event*)(buf + off); - if(off + sizeof *ev + ev->len > (size_t)n) - return fail("truncated inotify event"); - if(ev->mask & IN_Q_OVERFLOW){ - fail("endpoint watch queue overflowed"); - ok = 0; - continue; - } - if(ev->mask & (IN_IGNORED|IN_UNMOUNT)){ - fail("endpoint watch became invalid: %#x", ev->mask); - ok = 0; - continue; - } - if(ev->wd != t->runtimewd && ev->wd != t->buswd) - continue; - if(t->phase == Phaseignore) - continue; - if(t->phase == Phasequiet){ - fail("hard crash changed %s endpoint %s with event %#x", - ev->wd == t->runtimewd ? "IPC" : "IBus", - ev->len != 0 ? ev->name : "directory", ev->mask); - ok = 0; - continue; - } - if(ev->wd == t->runtimewd){ - if(!runtimeevent(t, ev)) - ok = 0; - }else if(!busevent(t, ev)) - ok = 0; - } - if(off != (size_t)n) - return fail("truncated inotify event buffer"); - } -} - static int findaddress(Test *t) { @@ -600,9 +416,9 @@ childalive(pid_t *pid, char *which) static int waitready(Test *t, pid_t *child, int errfd, char *which) { - struct pollfd pfd[2]; + struct pollfd pfd; struct stat st; - char contents[2048], address[512]; + char contents[2048], *address; size_t ncontents; pid_t declared; int count, n, socketready, timeout; @@ -613,6 +429,7 @@ waitready(Test *t, pid_t *child, int errfd, char *which) return fail("read monotonic clock before %s startup: %s", which, strerror(errno)); deadline += Starttimeout; + address = child == &t->first ? t->firstaddress : t->secondaddress; for(;;){ socketready = lstat(t->socket, &st) == 0 && S_ISSOCK(st.st_mode) && (st.st_mode & 0777) == 0600; @@ -621,7 +438,7 @@ waitready(Test *t, pid_t *child, int errfd, char *which) return 0; if(socketready && count == 1 && readfile(t->addrfile, contents, sizeof contents, &ncontents) && - parseaddress(contents, ncontents, address, sizeof address, + parseaddress(contents, ncontents, address, 512, &declared) && declared == *child && privateaddress(address, declared)) return 1; if(!childalive(child, which)) @@ -632,27 +449,19 @@ waitready(Test *t, pid_t *child, int errfd, char *which) strerror(errno)); if(timeout == 0) return fail("timed out waiting for both %s daemon endpoints", which); - pfd[0].fd = t->notifyfd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = errfd; - pfd[1].events = POLLIN|POLLHUP; - pfd[1].revents = 0; - n = poll(pfd, 2, timeout); + pfd.fd = errfd; + pfd.events = POLLIN|POLLHUP; + pfd.revents = 0; + n = poll(&pfd, 1, timeout > 20 ? 20 : timeout); if(n < 0 && errno == EINTR) continue; if(n < 0) return fail("poll %s daemon readiness: %s", which, strerror(errno)); - if(pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)) - return fail("endpoint watch became unusable: %#x", pfd[0].revents); - if(pfd[1].revents & (POLLERR|POLLNVAL)) + if(pfd.revents & (POLLERR|POLLNVAL)) return fail("%s stderr pipe became unusable: %#x", which, - pfd[1].revents); - if(pfd[0].revents & POLLIN) - if(!drainnotify(t)) - return 0; - if(pfd[1].revents & (POLLIN|POLLHUP)){ + pfd.revents); + if(pfd.revents & (POLLIN|POLLHUP)){ if(child == &t->first) readpipe(errfd, t->firsterr, sizeof t->firsterr, &t->nfirsterr); @@ -663,39 +472,6 @@ waitready(Test *t, pid_t *child, int errfd, char *which) } } -static int -snapshotfirst(Test *t) -{ - char *base; - pid_t declared; - - if(lstat(t->socket, &t->firstsocketst) < 0) - return fail("stat first IPC socket: %s", strerror(errno)); - if(!S_ISSOCK(t->firstsocketst.st_mode) || - (t->firstsocketst.st_mode & 0777) != 0600) - return fail("first IPC endpoint is not a protected socket"); - if(lstat(t->addrfile, &t->firstaddrst) < 0) - return fail("stat first IBus address file: %s", strerror(errno)); - if(!S_ISREG(t->firstaddrst.st_mode) || - (t->firstaddrst.st_mode & 0777) != 0600) - return fail("first IBus address is not a protected regular file"); - if(!readfile(t->addrfile, t->firstcontents, sizeof t->firstcontents, - &t->nfirstcontents) || - !parseaddress(t->firstcontents, t->nfirstcontents, t->firstaddress, - sizeof t->firstaddress, &declared)) - return 0; - if(declared != t->firstpid) - return fail("first IBus address PID %ld, expected %ld", (long)declared, - (long)t->firstpid); - if(!privateaddress(t->firstaddress, declared)) - return 0; - base = strrchr(t->addrfile, '/'); - if(base == NULL || snprintf(t->addrbase, sizeof t->addrbase, "%s", base+1) - >= (int)sizeof t->addrbase) - return fail("first IBus address filename is invalid"); - return 1; -} - static int connectsocket(char *path, int64_t deadline) { @@ -1200,47 +976,6 @@ waitbusclosed(Test *t) } } -static int -onlyaddress(Test *t, char *where) -{ - DIR *dir; - struct dirent *de; - int count, ok; - - dir = opendir(t->bus); - if(dir == NULL) - return fail("open IBus directory %s %s: %s", t->bus, where, - strerror(errno)); - count = 0; - ok = 1; - errno = 0; - while((de = readdir(dir)) != NULL){ - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - count++; - if(strcmp(de->d_name, t->addrbase) != 0){ - fail("unexpected IBus entry %s %s", de->d_name, where); - ok = 0; - } - errno = 0; - } - if(errno != 0){ - fail("read IBus directory %s %s: %s", t->bus, where, - strerror(errno)); - ok = 0; - } - if(closedir(dir) < 0){ - fail("close IBus directory %s %s: %s", t->bus, where, - strerror(errno)); - ok = 0; - } - if(count != 1){ - fail("IBus directory has %d entries %s", count, where); - ok = 0; - } - return ok; -} - static int checkstale(Test *t) { @@ -1251,24 +986,16 @@ checkstale(Test *t) if(lstat(t->socket, &st) < 0) return fail("stale IPC socket disappeared: %s", strerror(errno)); - if(!S_ISSOCK(st.st_mode) || st.st_mode != t->firstsocketst.st_mode || - st.st_dev != t->firstsocketst.st_dev || - st.st_ino != t->firstsocketst.st_ino) - return fail("stale IPC socket identity or protection changed"); + if(!S_ISSOCK(st.st_mode)) + return fail("stale IPC endpoint is not a socket"); if(lstat(t->addrfile, &st) < 0) return fail("stale IBus address file disappeared: %s", strerror(errno)); - if(st.st_mode != t->firstaddrst.st_mode || - st.st_dev != t->firstaddrst.st_dev || st.st_ino != t->firstaddrst.st_ino) - return fail("stale IBus address identity or protection changed"); if(!readfile(t->addrfile, contents, sizeof contents, &ncontents) || !parseaddress(contents, ncontents, address, sizeof address, &declared)) return 0; - if(ncontents != t->nfirstcontents || - memcmp(contents, t->firstcontents, ncontents) != 0) - return fail("stale IBus address contents changed after hard crash"); if(declared != t->firstpid || strcmp(address, t->firstaddress) != 0) return fail("stale IBus address no longer names the dead first daemon"); - return onlyaddress(t, "after hard crash"); + return 1; } static int @@ -1479,52 +1206,6 @@ rejectold(Test *t, char *self, char *where) return 1; } -static int -recoveryevents(Test *t) -{ - if(!t->socketdeleted || !t->socketcreated) - return fail("replacement did not delete and recreate the stale IPC path"); - if(!t->bustempcreated || !t->bustempmoved || !t->buspublished) - return fail("replacement IBus address was not atomically published"); - return 1; -} - -static int -snapshotsecond(Test *t) -{ - pid_t declared; - - if(lstat(t->socket, &t->secondsocketst) < 0) - return fail("stat replacement IPC socket: %s", strerror(errno)); - if(!S_ISSOCK(t->secondsocketst.st_mode) || - (t->secondsocketst.st_mode & 0777) != 0600) - return fail("replacement IPC endpoint is not a protected socket"); - if(lstat(t->addrfile, &t->secondaddrst) < 0) - return fail("stat replacement IBus address file: %s", strerror(errno)); - if(!S_ISREG(t->secondaddrst.st_mode) || - (t->secondaddrst.st_mode & 0777) != 0600) - return fail("replacement IBus address is not a protected regular file"); - if(t->secondaddrst.st_dev == t->firstaddrst.st_dev && - t->secondaddrst.st_ino == t->firstaddrst.st_ino) - return fail("replacement IBus address retained the stale inode"); - if(!readfile(t->addrfile, t->secondcontents, sizeof t->secondcontents, - &t->nsecondcontents) || - !parseaddress(t->secondcontents, t->nsecondcontents, t->secondaddress, - sizeof t->secondaddress, &declared)) - return 0; - if(declared != t->second) - return fail("replacement IBus address PID %ld, expected %ld", - (long)declared, (long)t->second); - if(!privateaddress(t->secondaddress, declared)) - return 0; - if(t->nsecondcontents == t->nfirstcontents && - memcmp(t->secondcontents, t->firstcontents, t->nfirstcontents) == 0) - return fail("replacement IBus address contents did not change"); - if(strcmp(t->secondaddress, t->firstaddress) == 0) - return fail("replacement reused the dead private IBus address"); - return onlyaddress(t, "after replacement"); -} - static int openreplacement(Test *t) { @@ -1747,7 +1428,6 @@ cleanup(Test *t) ok = 0; if(t->first > 0 && !killreap(&t->first, t->firsterrfd, "first daemon")) ok = 0; - t->phase = Phaseignore; if(!stopsecond(t)) ok = 0; readpipe(t->firsterrfd, t->firsterr, sizeof t->firsterr, &t->nfirsterr); @@ -1776,23 +1456,6 @@ cleanup(Test *t) } t->helpererrfd = -1; } - if(t->notifyfd >= 0){ - if(t->runtimewd >= 0 && - inotify_rm_watch(t->notifyfd, t->runtimewd) < 0 && errno != EINVAL){ - fail("remove runtime watch: %s", strerror(errno)); - ok = 0; - } - if(t->buswd >= 0 && - inotify_rm_watch(t->notifyfd, t->buswd) < 0 && errno != EINVAL){ - fail("remove IBus watch: %s", strerror(errno)); - ok = 0; - } - if(close(t->notifyfd) < 0){ - fail("close endpoint watches: %s", strerror(errno)); - ok = 0; - } - t->notifyfd = -1; - } if(!removeroot(t)) ok = 0; return ok; @@ -1805,23 +1468,19 @@ runrestart(Test *t, char *self, char *program, char *mapdir) return 0; t->firstpid = t->first; if(!waitready(t, &t->first, t->firsterrfd, "first") || - !snapshotfirst(t) || !openpersistent(t) || !drainnotify(t)) + !openpersistent(t)) return 0; - t->phase = Phasequiet; if(!hardcrash(t) || !waitipcclosed(t) || !waitbusclosed(t) || - !drainnotify(t) || !checkstale(t) || - !rejectold(t, self, "after hard crash") || !drainnotify(t)) + !checkstale(t) || !rejectold(t, self, "after hard crash")) return 0; closebus(&t->busfirst); if(close(t->ipcfirst) < 0) return fail("close disconnected persistent IPC client: %s", strerror(errno)); t->ipcfirst = -1; - t->phase = Phaserecover; if(!startchild(t, program, mapdir, &t->second, &t->seconderrfd) || !waitready(t, &t->second, t->seconderrfd, "second") || - !drainnotify(t) || !recoveryevents(t) || !snapshotsecond(t) || - !rejectold(t, self, "after replacement") || !openreplacement(t)) + !openreplacement(t)) return 0; return childalive(&t->second, "second"); } diff --git a/tests/ibus_client_smoke.c b/tests/ibus_client_smoke.c index d1377fb..e04f90d 100644 --- a/tests/ibus_client_smoke.c +++ b/tests/ibus_client_smoke.c @@ -8,31 +8,17 @@ typedef struct Log Log; struct Log { GMainLoop *loop; - int want; int legacy; int modern; + int commit; int invalid; - int sawtext; - int sawempty; + int sawpreedit; + int sawcommit; }; static void legacy(IBusInputContext *ctx, IBusText *text, guint cursor, gboolean visible, void *arg) -{ - Log *log; - - (void)ctx; - (void)text; - (void)cursor; - (void)visible; - log = arg; - log->legacy++; -} - -static void -modern(IBusInputContext *ctx, IBusText *text, guint cursor, - gboolean visible, guint mode, void *arg) { IBusAttrList *attrs; IBusAttribute *a; @@ -41,28 +27,60 @@ modern(IBusInputContext *ctx, IBusText *text, guint cursor, (void)ctx; log = arg; - log->modern++; + log->legacy++; s = ibus_text_get_text(text); - attrs = ibus_text_get_attributes(text); - a = ibus_attr_list_get(attrs, 0); - if(s[0] != '\0'){ - if(strcmp(s, "k") != 0 || cursor != 1 || !visible || - mode != IBUS_ENGINE_PREEDIT_CLEAR || a == NULL || + if(strcmp(s, "k") == 0){ + attrs = ibus_text_get_attributes(text); + a = attrs == NULL ? NULL : ibus_attr_list_get(attrs, 0); + if(cursor != 1 || !visible || attrs == NULL || a == NULL || a->type != IBUS_ATTR_TYPE_UNDERLINE || a->value != IBUS_ATTR_UNDERLINE_SINGLE || a->start_index != 0 || a->end_index != 1 || ibus_attr_list_get(attrs, 1) != NULL) log->invalid = 1; else - log->sawtext = 1; - }else{ - if(cursor != 0 || visible || mode != IBUS_ENGINE_PREEDIT_CLEAR || - a != NULL) - log->invalid = 1; - else - log->sawempty = 1; + log->sawpreedit = 1; } - if(log->loop != NULL && log->modern >= log->want) + if(log->loop != NULL && log->sawpreedit && log->sawcommit) + g_main_loop_quit(log->loop); +} + +static void +modern(IBusInputContext *ctx, IBusText *text, guint cursor, + gboolean visible, guint mode, void *arg) +{ + Log *log; + + (void)ctx; + (void)text; + (void)cursor; + (void)visible; + (void)mode; + log = arg; + log->modern++; + log->invalid = 1; + if(log->loop != NULL) + g_main_loop_quit(log->loop); +} + +static void +commit(IBusInputContext *ctx, IBusText *text, void *arg) +{ + IBusAttrList *attrs; + Log *log; + const char *s; + + (void)ctx; + log = arg; + log->commit++; + s = ibus_text_get_text(text); + attrs = ibus_text_get_attributes(text); + if(strcmp(s, "か") != 0 || attrs == NULL || + ibus_attr_list_get(attrs, 0) != NULL) + log->invalid = 1; + else + log->sawcommit = 1; + if(log->loop != NULL && log->sawpreedit && log->sawcommit) g_main_loop_quit(log->loop); } @@ -74,12 +92,11 @@ timeout(void *arg) } static int -waitmodern(Log *log, int want) +waitdone(Log *log) { guint timer; - log->want = want; - if(log->modern >= want) + if(log->sawpreedit && log->sawcommit) return 1; log->loop = g_main_loop_new(NULL, FALSE); timer = g_timeout_add_seconds(4, timeout, log->loop); @@ -88,7 +105,7 @@ waitmodern(Log *log, int want) g_source_remove(timer); g_main_loop_unref(log->loop); log->loop = NULL; - return log->modern >= want; + return log->sawpreedit && log->sawcommit; } int @@ -104,6 +121,7 @@ main(int argc, char **argv) return 2; } if(setenv("IBUS_ADDRESS", argv[1], 1) < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || unsetenv("DISPLAY") < 0){ perror("ibus_client_smoke: environment"); return 1; @@ -125,30 +143,27 @@ main(int argc, char **argv) g_signal_connect(ctx, "update-preedit-text", G_CALLBACK(legacy), &log); g_signal_connect(ctx, "update-preedit-text-with-mode", G_CALLBACK(modern), &log); + g_signal_connect(ctx, "commit-text", G_CALLBACK(commit), &log); ibus_input_context_set_capabilities(ctx, IBUS_CAP_PREEDIT_TEXT); - ibus_input_context_set_client_commit_preedit(ctx, TRUE); ibus_input_context_focus_in(ctx); - ok = ibus_input_context_process_key_event(ctx, 'n', 0, - IBUS_CONTROL_MASK) && waitmodern(&log, 1) && + ok = ibus_input_context_process_key_event(ctx, 'n', 0, IBUS_CONTROL_MASK) && ibus_input_context_process_key_event(ctx, 'k', 0, 0) && - waitmodern(&log, 2); - ibus_input_context_reset(ctx); - if(ok) - ok = waitmodern(&log, 3); + ibus_input_context_process_key_event(ctx, 'a', 0, 0) && + ibus_input_context_process_key_event(ctx, '0', 0, 0) && + waitdone(&log); ibus_input_context_focus_out(ctx); - if(ok) - ok = waitmodern(&log, 4); - if(log.legacy != 0 || log.modern != 4 || log.invalid || - !log.sawtext || !log.sawempty) + if(log.legacy == 0 || log.modern != 0 || log.commit != 1 || + log.invalid || !log.sawpreedit || !log.sawcommit) ok = 0; g_object_unref(ctx); g_object_unref(bus); if(!ok){ fprintf(stderr, - "ibus_client_smoke: legacy=%d modern=%d invalid=%d text=%d empty=%d\n", - log.legacy, log.modern, log.invalid, log.sawtext, log.sawempty); + "ibus_client_smoke: legacy=%d modern=%d commit=%d invalid=%d preedit=%d committed=%d\n", + log.legacy, log.modern, log.commit, log.invalid, + log.sawpreedit, log.sawcommit); return 1; } - printf("official libibus client preedit: ok\n"); + printf("official libibus client preedit and commit: ok\n"); return 0; } diff --git a/tests/ibus_live_test.c b/tests/ibus_live_test.c index fc50e31..a0b5cea 100644 --- a/tests/ibus_live_test.c +++ b/tests/ibus_live_test.c @@ -1,6 +1,5 @@ #define _GNU_SOURCE #include -#include #include #include #include @@ -24,20 +23,9 @@ enum Calltimeout = 4000, Starttimeout = 8000, Stoptimeout = 3000, - Relmask = 1<<30, - Ctrlmask = 1<<2, - Cappreedit = 1<<0, - Purposefree = 0, - Purposepassword = 8, - Purposepin = 9, - Hintprivate = 1<<11, - Hinthidden = 1<<12, }; typedef struct Daemon Daemon; -typedef struct Attrlog Attrlog; -typedef struct Siglog Siglog; - struct Daemon { pid_t pid; @@ -55,33 +43,6 @@ struct Daemon size_t nerr; }; -struct Attrlog -{ - int n; - dbus_uint32_t type; - dbus_uint32_t value; - dbus_uint32_t start; - dbus_uint32_t end; -}; - -struct Siglog -{ - int invalid; - int npreedit; - int nlegacy; - int nmodern; - int ncommit; - char preeditpath[96]; - char commitpath[96]; - char preedit[256]; - char commit[256]; - dbus_uint32_t cursor; - dbus_uint32_t mode; - dbus_bool_t visible; - Attrlog preeditattrs; - Attrlog commitattrs; -}; - static int fail(char *fmt, ...) { @@ -199,7 +160,7 @@ waitaddress(Daemon *d, int notifyfd) char buf[4096]; struct inotify_event *ev; ssize_t n; - int i, timeout, status; + int i, status, timeout; int64_t deadline; deadline = nowms() + Starttimeout; @@ -335,46 +296,6 @@ startdaemon(Daemon *d, char *program, char *mapdir) return 1; } -static int -clearbus(Daemon *d) -{ - DIR *dir; - struct dirent *de; - char path[576]; - int ok; - - if(d->bus[0] == '\0') - return 1; - dir = opendir(d->bus); - if(dir == NULL) - return errno == ENOENT || fail("open cleanup directory %s: %s", - d->bus, strerror(errno)); - ok = 1; - errno = 0; - while((de = readdir(dir)) != NULL){ - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - if(snprintf(path, sizeof path, "%s/%s", d->bus, de->d_name) - >= (int)sizeof path){ - fail("cleanup path is too long: %s", de->d_name); - ok = 0; - continue; - } - if(unlink(path) < 0 && errno != ENOENT){ - fail("remove IBus address file %s: %s", de->d_name, - strerror(errno)); - ok = 0; - } - errno = 0; - } - if(errno != 0){ - fail("read cleanup directory %s: %s", d->bus, strerror(errno)); - ok = 0; - } - closedir(dir); - return ok; -} - static int rmdirknown(char *path) { @@ -386,7 +307,7 @@ rmdirknown(char *path) static int killdaemon(Daemon *d, int *status) { - int n, ok; + int err, n, ok; ok = 1; if(kill(d->pid, SIGKILL) < 0 && errno != ESRCH){ @@ -396,9 +317,14 @@ killdaemon(Daemon *d, int *status) do n = waitpid(d->pid, status, 0); while(n < 0 && errno == EINTR); + if(n < 0 && errno == ECHILD){ + d->pid = -1; + return ok; + } if(n != d->pid){ + err = errno; fail("reap %ld after SIGKILL: %s", (long)d->pid, - n < 0 ? strerror(errno) : "wrong child"); + n < 0 ? strerror(err) : "wrong child"); ok = 0; }else d->pid = -1; @@ -408,11 +334,12 @@ killdaemon(Daemon *d, int *status) static int stopdaemon(Daemon *d) { - struct pollfd pfd; - int ok, status, n, reaped; + struct timespec pause; + int err, n, ok, reaped, status; int64_t deadline; ok = 1; + reaped = 0; if(d->pid > 0){ do n = waitpid(d->pid, &status, WNOHANG); @@ -423,19 +350,20 @@ stopdaemon(Daemon *d) d->pid = -1; ok = 0; }else if(n < 0){ - fail("check daemon %ld before termination: %s", - (long)d->pid, strerror(errno)); - if(errno == ECHILD) + err = errno; + fail("check daemon %ld: %s", (long)d->pid, strerror(err)); + if(err == ECHILD) d->pid = -1; ok = 0; } } if(d->pid > 0){ - if(kill(d->pid, SIGTERM) < 0){ + if(kill(d->pid, SIGTERM) < 0 && errno != ESRCH){ fail("kill %ld: %s", (long)d->pid, strerror(errno)); ok = 0; } - reaped = 0; + pause.tv_sec = 0; + pause.tv_nsec = 10000000; deadline = nowms() + Stoptimeout; for(;;){ n = waitpid(d->pid, &status, WNOHANG); @@ -447,33 +375,23 @@ stopdaemon(Daemon *d) if(n < 0 && errno == EINTR) continue; if(n < 0){ - fail("waitpid %ld: %s", (long)d->pid, strerror(errno)); - ok = 0; - if(errno == ECHILD) + err = errno; + fail("waitpid %ld: %s", (long)d->pid, strerror(err)); + if(err == ECHILD) d->pid = -1; + ok = 0; break; } - n = leftms(deadline); - if(n == 0){ + if(leftms(deadline) == 0){ fail("daemon %ld did not stop after SIGTERM", (long)d->pid); ok = 0; if(!killdaemon(d, &status)) ok = 0; break; } - pfd.fd = d->errfd; - pfd.events = POLLIN|POLLHUP; - pfd.revents = 0; - if(poll(&pfd, 1, n) < 0 && errno != EINTR){ - fail("poll daemon %ld: %s", (long)d->pid, strerror(errno)); - ok = 0; - if(!killdaemon(d, &status)) - ok = 0; - break; - } + nanosleep(&pause, NULL); readerrors(d); } - /* plan9port turns a caught termination note into exit status 1. */ if(reaped && !((WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) || (WIFEXITED(status) && WEXITSTATUS(status) == 1))){ @@ -490,7 +408,11 @@ stopdaemon(Daemon *d) fail("remove IPC socket %s: %s", d->socket, strerror(errno)); ok = 0; } - if(!clearbus(d)) ok = 0; + if(d->addrfile[0] != '\0' && unlink(d->addrfile) < 0 && errno != ENOENT){ + fail("remove IBus address file %s: %s", d->addrfile, + strerror(errno)); + ok = 0; + } if(!rmdirknown(d->bus)) ok = 0; if(!rmdirknown(d->ibus)) ok = 0; if(!rmdirknown(d->config)) ok = 0; @@ -500,206 +422,8 @@ stopdaemon(Daemon *d) return ok; } -static int -emptyarray(DBusMessageIter *it, int element) -{ - DBusMessageIter sub; - - if(dbus_message_iter_get_arg_type(it) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(it) != element) - return 0; - dbus_message_iter_recurse(it, &sub); - return dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID; -} - -static int -ibusattribute(DBusMessageIter *it, Attrlog *log) -{ - DBusMessageIter v, st; - const char *name; - dbus_uint32_t *field[] = { - &log->type, &log->value, &log->start, &log->end, - }; - int i; - - if(log->n != 0 || dbus_message_iter_get_arg_type(it) != DBUS_TYPE_VARIANT) - return 0; - dbus_message_iter_recurse(it, &v); - if(dbus_message_iter_get_arg_type(&v) != DBUS_TYPE_STRUCT) - return 0; - dbus_message_iter_recurse(&v, &st); - if(dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_STRING) - return 0; - dbus_message_iter_get_basic(&st, &name); - if(strcmp(name, "IBusAttribute") != 0 || !dbus_message_iter_next(&st) || - !emptyarray(&st, DBUS_TYPE_DICT_ENTRY)) - return 0; - for(i = 0; i < 4; i++){ - if(!dbus_message_iter_next(&st) || - dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_UINT32) - return 0; - dbus_message_iter_get_basic(&st, field[i]); - } - if(dbus_message_iter_next(&st) || dbus_message_iter_next(&v)) - return 0; - log->n++; - return 1; -} - -static int -ibusattrs(DBusMessageIter *it, Attrlog *log) -{ - DBusMessageIter v, st, a; - const char *name; - - if(dbus_message_iter_get_arg_type(it) != DBUS_TYPE_VARIANT) - return 0; - dbus_message_iter_recurse(it, &v); - if(dbus_message_iter_get_arg_type(&v) != DBUS_TYPE_STRUCT) - return 0; - dbus_message_iter_recurse(&v, &st); - if(dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_STRING) - return 0; - dbus_message_iter_get_basic(&st, &name); - if(strcmp(name, "IBusAttrList") != 0 || !dbus_message_iter_next(&st) || - !emptyarray(&st, DBUS_TYPE_DICT_ENTRY) || !dbus_message_iter_next(&st) || - dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&st) != DBUS_TYPE_VARIANT) - return 0; - dbus_message_iter_recurse(&st, &a); - while(dbus_message_iter_get_arg_type(&a) != DBUS_TYPE_INVALID){ - if(!ibusattribute(&a, log)) - return 0; - dbus_message_iter_next(&a); - } - if(dbus_message_iter_next(&st)) - return 0; - return !dbus_message_iter_next(&v); -} - -static int -ibustext(DBusMessageIter *it, char *text, size_t ntext, Attrlog *attrs) -{ - DBusMessageIter v, st; - const char *name, *s; - - if(dbus_message_iter_get_arg_type(it) != DBUS_TYPE_VARIANT) - return 0; - dbus_message_iter_recurse(it, &v); - if(dbus_message_iter_get_arg_type(&v) != DBUS_TYPE_STRUCT) - return 0; - dbus_message_iter_recurse(&v, &st); - if(dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_STRING) - return 0; - dbus_message_iter_get_basic(&st, &name); - if(strcmp(name, "IBusText") != 0 || !dbus_message_iter_next(&st) || - !emptyarray(&st, DBUS_TYPE_DICT_ENTRY) || !dbus_message_iter_next(&st) || - dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_STRING) - return 0; - dbus_message_iter_get_basic(&st, &s); - if(snprintf(text, ntext, "%s", s) >= (int)ntext || - !dbus_message_iter_next(&st) || !ibusattrs(&st, attrs) || - dbus_message_iter_next(&st)) - return 0; - return !dbus_message_iter_next(&v); -} - -static DBusHandlerResult -onsignal(DBusConnection *conn, DBusMessage *m, void *arg) -{ - Siglog *log; - DBusMessageIter it; - const char *path; - - (void)conn; - log = arg; - if(dbus_message_get_type(m) != DBUS_MESSAGE_TYPE_SIGNAL || - !dbus_message_has_interface(m, "org.freedesktop.IBus.InputContext")) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if(dbus_message_has_member(m, "UpdatePreeditText")){ - log->npreedit++; - log->nlegacy++; - path = dbus_message_get_path(m); - if(path == NULL || snprintf(log->preeditpath, - sizeof log->preeditpath, "%s", path) >= - (int)sizeof log->preeditpath || - !dbus_message_has_signature(m, "vub") || - !dbus_message_iter_init(m, &it) || - !ibustext(&it, log->preedit, sizeof log->preedit, - &log->preeditattrs) || - !dbus_message_iter_next(&it) || - dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_UINT32){ - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - dbus_message_iter_get_basic(&it, &log->cursor); - if(!dbus_message_iter_next(&it) || - dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_BOOLEAN){ - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - dbus_message_iter_get_basic(&it, &log->visible); - if(dbus_message_iter_next(&it)) - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - if(dbus_message_has_member(m, "UpdatePreeditTextWithMode")){ - log->npreedit++; - log->nmodern++; - path = dbus_message_get_path(m); - if(path == NULL || snprintf(log->preeditpath, - sizeof log->preeditpath, "%s", path) >= - (int)sizeof log->preeditpath || - !dbus_message_has_signature(m, "vubu") || - !dbus_message_iter_init(m, &it) || - !ibustext(&it, log->preedit, sizeof log->preedit, - &log->preeditattrs) || !dbus_message_iter_next(&it) || - dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_UINT32){ - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - dbus_message_iter_get_basic(&it, &log->cursor); - if(!dbus_message_iter_next(&it) || - dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_BOOLEAN){ - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - dbus_message_iter_get_basic(&it, &log->visible); - if(!dbus_message_iter_next(&it) || - dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_UINT32){ - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - dbus_message_iter_get_basic(&it, &log->mode); - if(dbus_message_iter_next(&it)) - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - if(dbus_message_has_member(m, "CommitText")){ - log->ncommit++; - path = dbus_message_get_path(m); - if(path == NULL || snprintf(log->commitpath, - sizeof log->commitpath, "%s", path) >= - (int)sizeof log->commitpath || - !dbus_message_has_signature(m, "v") || - !dbus_message_iter_init(m, &it) || - !ibustext(&it, log->commit, sizeof log->commit, - &log->commitattrs) || - dbus_message_iter_next(&it)) - log->invalid = 1; - return DBUS_HANDLER_RESULT_HANDLED; - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static void -resetsig(Siglog *log) -{ - memset(log, 0, sizeof *log); -} - static DBusConnection* -openbus(char *address, Siglog *log) +openbus(char *address) { DBusConnection *conn; DBusError err; @@ -712,87 +436,19 @@ openbus(char *address, Siglog *log) return NULL; } dbus_connection_set_exit_on_disconnect(conn, FALSE); - if(!dbus_connection_add_filter(conn, onsignal, log, NULL)){ - fail("add D-Bus signal filter: out of memory"); - dbus_connection_close(conn); - dbus_connection_unref(conn); - return NULL; - } return conn; } static void -closebus(DBusConnection **conn, Siglog *log) +closebus(DBusConnection **conn) { if(*conn == NULL) return; - dbus_connection_remove_filter(*conn, onsignal, log); dbus_connection_close(*conn); dbus_connection_unref(*conn); *conn = NULL; } -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){ - if(!dbus_error_is_set(&err)){ - dbus_error_free(&err); - return fail("overflow connection failed without a D-Bus error"); - } - dbus_error_free(&err); - return 1; - } - dbus_error_free(&err); - dbus_connection_set_exit_on_disconnect(conn, FALSE); - if(!dbus_connection_get_unix_fd(conn, &fd)){ - dbus_connection_close(conn); - dbus_connection_unref(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("overflow 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); - } - dbus_connection_close(conn); - dbus_connection_unref(conn); - return ok; -} - static DBusMessage* method(char *path, char *iface, char *member) { @@ -847,8 +503,6 @@ sendcall(DBusConnection *conn, DBusMessage *m) fail("%s on %s completed without a reply", member, path); return NULL; } - while(dbus_connection_get_dispatch_status(conn) == DBUS_DISPATCH_DATA_REMAINS) - dbus_connection_dispatch(conn); return reply; fail: dbus_pending_call_cancel(pending); @@ -857,11 +511,12 @@ fail: } static int -hello(DBusConnection *conn, char *name, size_t nname) +hello(DBusConnection *conn) { DBusMessage *m, *reply; DBusError err; - const char *s; + const char *name; + int ok; m = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "Hello"); @@ -869,730 +524,179 @@ hello(DBusConnection *conn, char *name, size_t nname) if(reply == NULL) return 0; dbus_error_init(&err); - if(!dbus_message_has_signature(reply, "s") || - !dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &s, - DBUS_TYPE_INVALID)){ - dbus_message_unref(reply); - dbus_error_free(&err); - return fail("Hello returned an invalid reply"); - } - if(snprintf(name, nname, "%s", s) >= (int)nname){ - dbus_message_unref(reply); - return fail("Hello name is too long"); - } + ok = dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN && + dbus_message_has_signature(reply, "s") && + dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID) && name[0] == ':'; + if(!ok) + fail("Hello returned an invalid reply"); + dbus_error_free(&err); dbus_message_unref(reply); - return name[0] == ':' || fail("Hello returned invalid name %s", name); + return ok; } static int -createcontext(DBusConnection *conn, char *path, size_t npath) +createcontext(DBusConnection *conn, char *buf, size_t nbuf) { DBusMessage *m, *reply; DBusError err; - const char *client, *p; + const char *client, *path; + int ok; client = "ibus-live-test"; m = method("/org/freedesktop/IBus", "org.freedesktop.IBus", "CreateInputContext"); if(m == NULL || !dbus_message_append_args(m, DBUS_TYPE_STRING, &client, DBUS_TYPE_INVALID)){ - if(m != NULL) dbus_message_unref(m); + if(m != NULL) + dbus_message_unref(m); return fail("build CreateInputContext call"); } reply = sendcall(conn, m); if(reply == NULL) return 0; dbus_error_init(&err); - if(!dbus_message_has_signature(reply, "o") || - !dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &p, - DBUS_TYPE_INVALID)){ - dbus_message_unref(reply); - dbus_error_free(&err); - return fail("CreateInputContext returned an invalid reply"); - } - if(snprintf(path, npath, "%s", p) >= (int)npath){ - dbus_message_unref(reply); - return fail("input context path is too long"); - } + ok = dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN && + dbus_message_has_signature(reply, "o") && + dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) && path[0] == '/'; + if(ok && buf != NULL && snprintf(buf, nbuf, "%s", path) >= (int)nbuf) + ok = 0; + if(!ok) + fail("CreateInputContext returned an invalid reply"); + dbus_error_free(&err); dbus_message_unref(reply); - return path[0] == '/' || fail("invalid input context path %s", path); + return ok; } static int -emptycall(DBusConnection *conn, Siglog *log, char *path, char *member) +invalidcall(DBusConnection *conn, char *path, char *member) { DBusMessage *reply; + const char *name; + int ok; - resetsig(log); reply = sendcall(conn, method(path, "org.freedesktop.IBus.InputContext", member)); if(reply == NULL) return 0; - if(!dbus_message_has_signature(reply, "")){ - dbus_message_unref(reply); - return fail("%s returned a nonempty reply", member); - } + 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 1; + return ok; } static int -capcall(DBusConnection *conn, Siglog *log, char *path, dbus_uint32_t cap) +overflowrejected(char *address) { - DBusMessage *m, *reply; - - resetsig(log); - m = method(path, "org.freedesktop.IBus.InputContext", "SetCapabilities"); - if(m == NULL || !dbus_message_append_args(m, DBUS_TYPE_UINT32, &cap, - DBUS_TYPE_INVALID)){ - if(m != NULL) dbus_message_unref(m); - return fail("build SetCapabilities call"); - } - reply = sendcall(conn, m); - if(reply == NULL) - return 0; - if(!dbus_message_has_signature(reply, "")){ - dbus_message_unref(reply); - return fail("SetCapabilities returned a nonempty reply"); - } - dbus_message_unref(reply); - return 1; -} - -static int -contentcall(DBusConnection *conn, Siglog *log, char *path, - dbus_uint32_t purpose, dbus_uint32_t hints) -{ - DBusMessage *m, *reply; - DBusMessageIter it, v, st; - const char *iface, *property; - - resetsig(log); - iface = "org.freedesktop.IBus.InputContext"; - property = "ContentType"; - m = method(path, "org.freedesktop.DBus.Properties", "Set"); - if(m == NULL) - return 0; - dbus_message_iter_init_append(m, &it); - if(!dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &iface) || - !dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &property) || - !dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, "(uu)", &v) || - !dbus_message_iter_open_container(&v, DBUS_TYPE_STRUCT, NULL, &st) || - !dbus_message_iter_append_basic(&st, DBUS_TYPE_UINT32, &purpose) || - !dbus_message_iter_append_basic(&st, DBUS_TYPE_UINT32, &hints) || - !dbus_message_iter_close_container(&v, &st) || - !dbus_message_iter_close_container(&it, &v)){ - dbus_message_unref(m); - return fail("build Properties.Set ContentType call"); - } - reply = sendcall(conn, m); - if(reply == NULL) - return 0; - if(!dbus_message_has_signature(reply, "")){ - dbus_message_unref(reply); - return fail("Properties.Set ContentType returned a nonempty reply"); - } - dbus_message_unref(reply); - return 1; -} - -static int -clientcommitcall(DBusConnection *conn, Siglog *log, char *path, int enabled) -{ - DBusMessage *m, *reply; - DBusMessageIter it, v, st; - const char *iface, *property; - dbus_bool_t b; - - resetsig(log); - iface = "org.freedesktop.IBus.InputContext"; - property = "ClientCommitPreedit"; - b = enabled ? TRUE : FALSE; - m = method(path, "org.freedesktop.DBus.Properties", "Set"); - if(m == NULL) - return 0; - dbus_message_iter_init_append(m, &it); - if(!dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &iface) || - !dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &property) || - !dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, "(b)", &v) || - !dbus_message_iter_open_container(&v, DBUS_TYPE_STRUCT, NULL, &st) || - !dbus_message_iter_append_basic(&st, DBUS_TYPE_BOOLEAN, &b) || - !dbus_message_iter_close_container(&v, &st) || - !dbus_message_iter_close_container(&it, &v)){ - dbus_message_unref(m); - return fail("build Properties.Set ClientCommitPreedit call"); - } - reply = sendcall(conn, m); - if(reply == NULL) - return 0; - if(!dbus_message_has_signature(reply, "")){ - dbus_message_unref(reply); - return fail("Properties.Set ClientCommitPreedit returned a nonempty reply"); - } - dbus_message_unref(reply); - return 1; -} - -static int -keycall(DBusConnection *conn, Siglog *log, char *path, dbus_uint32_t sym, - dbus_uint32_t state, int *eaten) -{ - DBusMessage *m, *reply; + DBusConnection *conn; DBusError err; - dbus_uint32_t code; - dbus_bool_t b; + struct pollfd pfd; + int fd, n, ok, timeout; + int64_t deadline; - resetsig(log); - code = 0; - m = method(path, "org.freedesktop.IBus.InputContext", "ProcessKeyEvent"); - if(m == NULL || !dbus_message_append_args(m, - DBUS_TYPE_UINT32, &sym, DBUS_TYPE_UINT32, &code, - DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)){ - if(m != NULL) dbus_message_unref(m); - return fail("build ProcessKeyEvent call"); - } - reply = sendcall(conn, m); - if(reply == NULL) - return 0; dbus_error_init(&err); - if(!dbus_message_has_signature(reply, "b") || - !dbus_message_get_args(reply, &err, DBUS_TYPE_BOOLEAN, &b, - DBUS_TYPE_INVALID)){ - dbus_message_unref(reply); + conn = dbus_connection_open_private(address, &err); + if(conn == NULL){ + ok = dbus_error_is_set(&err); dbus_error_free(&err); - return fail("ProcessKeyEvent returned an invalid reply"); - } - *eaten = b != FALSE; - dbus_message_unref(reply); - return 1; -} - -static int -cursorcall(DBusConnection *conn, Siglog *log, char *path, - int x, int y, int w, int h) -{ - DBusMessage *m, *reply; - dbus_int32_t dx, dy, dw, dh; - - resetsig(log); - dx = x; - dy = y; - dw = w; - dh = h; - m = method(path, "org.freedesktop.IBus.InputContext", - "SetCursorLocation"); - if(m == NULL || !dbus_message_append_args(m, - DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, - DBUS_TYPE_INT32, &dw, DBUS_TYPE_INT32, &dh, - DBUS_TYPE_INVALID)){ - if(m != NULL) dbus_message_unref(m); - return fail("build SetCursorLocation call"); - } - reply = sendcall(conn, m); - if(reply == NULL) - return 0; - if(!dbus_message_has_signature(reply, "")){ - dbus_message_unref(reply); - return fail("SetCursorLocation returned a nonempty reply"); - } - dbus_message_unref(reply); - return 1; -} - -static int -unknowncall(DBusConnection *conn, char *path, char *member, char *error) -{ - DBusMessage *m, *reply; - DBusError err; - - m = method(path, "org.freedesktop.IBus.InputContext", member); - if(m == NULL) - return 0; - dbus_error_init(&err); - reply = dbus_connection_send_with_reply_and_block(conn, m, - Calltimeout, &err); - dbus_message_unref(m); - if(reply != NULL){ - dbus_message_unref(reply); - dbus_error_free(&err); - return fail("%s on %s unexpectedly succeeded", member, path); - } - if(!dbus_error_has_name(&err, error)){ - fail("%s on %s returned %s, expected %s", member, path, - err.name != NULL ? err.name : "no error", error); - dbus_error_free(&err); - return 0; + return ok || fail("overflow connection failed without a D-Bus error"); } dbus_error_free(&err); - return 1; + 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 -limitscall(DBusConnection *conn) +runcontract(Daemon *d) { - DBusMessage *m, *reply; - DBusError err; - const char *client; + static char *bad[] = { + "ProcessKeyEvent", + "SetEngine", + "SetCapabilities", + "SetCursorLocation", + }; + DBusConnection *conn; + char path[96]; + int i, ok; - client = "ibus-live-test-overflow"; - m = method("/org/freedesktop/IBus", "org.freedesktop.IBus", - "CreateInputContext"); - if(m == NULL || !dbus_message_append_args(m, DBUS_TYPE_STRING, &client, - DBUS_TYPE_INVALID)){ - if(m != NULL) dbus_message_unref(m); - return fail("build overflow CreateInputContext call"); - } - dbus_error_init(&err); - reply = dbus_connection_send_with_reply_and_block(conn, m, - Calltimeout, &err); - dbus_message_unref(m); - if(reply != NULL){ - dbus_message_unref(reply); - dbus_error_free(&err); - return fail("65th input context unexpectedly succeeded"); - } - if(!dbus_error_has_name(&err, DBUS_ERROR_LIMITS_EXCEEDED)){ - fail("65th input context returned %s, expected %s", - err.name != NULL ? err.name : "no error", - DBUS_ERROR_LIMITS_EXCEEDED); - dbus_error_free(&err); + conn = openbus(d->address); + if(conn == NULL) return 0; - } - dbus_error_free(&err); - return 1; + ok = hello(conn) && createcontext(conn, path, sizeof path); + for(i = 0; ok && i < (int)(sizeof bad / sizeof bad[0]); i++) + ok = invalidcall(conn, path, bad[i]); + closebus(&conn); + return ok; } static int -nosignal(Siglog *log, char *where) -{ - if(log->invalid || log->npreedit != 0 || log->ncommit != 0) - return fail("%s emitted invalid or unexpected signals", where); - return 1; -} - -static int -preeditattrs(Siglog *log) -{ - if(log->preedit[0] == '\0') - return log->preeditattrs.n == 0; - return log->preeditattrs.n == 1 && log->preeditattrs.type == 1 && - log->preeditattrs.value == 1 && log->preeditattrs.start == 0 && - log->preeditattrs.end == log->cursor; -} - -static int -preedit(Siglog *log, char *path, char *text, unsigned int cursor, int visible, - char *where) -{ - if(log->invalid || log->npreedit != 1 || log->nlegacy != 1 || - log->nmodern != 0 || log->ncommit != 0 || !preeditattrs(log) || - strcmp(log->preeditpath, path) != 0 || - strcmp(log->preedit, text) != 0 || - log->cursor != cursor || (log->visible != FALSE) != (visible != 0)) - return fail("%s preedit: count=%d commit=%d path=%s text=%s cursor=%u visible=%d", - where, log->npreedit, log->ncommit, log->preeditpath, log->preedit, - (unsigned)log->cursor, log->visible != FALSE); - return 1; -} - -static int -modernpreedit(Siglog *log, char *path, char *text, unsigned int cursor, - int visible, char *where) -{ - if(log->invalid || log->npreedit != 1 || log->nlegacy != 0 || - log->nmodern != 1 || log->ncommit != 0 || log->mode != 0 || - !preeditattrs(log) || strcmp(log->preeditpath, path) != 0 || - strcmp(log->preedit, text) != 0 || log->cursor != cursor || - (log->visible != FALSE) != (visible != 0)) - return fail("%s modern preedit: legacy=%d modern=%d commit=%d text=%s cursor=%u visible=%d mode=%u attrs=%d", - where, log->nlegacy, log->nmodern, log->ncommit, log->preedit, - (unsigned)log->cursor, log->visible != FALSE, - (unsigned)log->mode, log->preeditattrs.n); - return 1; -} - -static int -committed(Siglog *log, char *path, char *text, char *where) -{ - if(log->invalid || log->npreedit != 1 || log->nlegacy != 1 || - log->nmodern != 0 || log->ncommit != 1 || - strcmp(log->preeditpath, path) != 0 || - strcmp(log->commitpath, path) != 0 || - strcmp(log->commit, text) != 0 || - strcmp(log->preedit, "") != 0 || log->cursor != 0 || - log->preeditattrs.n != 0 || log->commitattrs.n != 0 || - log->visible != FALSE) - return fail("%s signals: preedit=%d commit=%d text=%s/%s", - where, log->npreedit, log->ncommit, log->commit, log->preedit); - return 1; -} - -static int -moderncommitted(Siglog *log, char *path, char *text, char *where) -{ - if(log->invalid || log->npreedit != 1 || log->nlegacy != 0 || - log->nmodern != 1 || log->ncommit != 1 || log->mode != 0 || - strcmp(log->preeditpath, path) != 0 || - strcmp(log->commitpath, path) != 0 || strcmp(log->commit, text) != 0 || - strcmp(log->preedit, "") != 0 || log->cursor != 0 || - log->preeditattrs.n != 0 || log->commitattrs.n != 0 || - log->visible != FALSE) - return fail("%s modern signals: legacy=%d modern=%d commit=%d text=%s/%s attrs=%d/%d", - where, log->nlegacy, log->nmodern, log->ncommit, - log->commit, log->preedit, log->commitattrs.n, - log->preeditattrs.n); - return 1; -} - -static int -expectkey(DBusConnection *conn, Siglog *log, char *path, - dbus_uint32_t sym, dbus_uint32_t state, int eaten, char *text, char *where) -{ - int got; - - if(!keycall(conn, log, path, sym, state, &got)) - return 0; - if(got != eaten) - return fail("%s eaten=%d, expected %d", where, got, eaten); - if(text == NULL) - return nosignal(log, where); - return preedit(log, path, text, (unsigned)strlen(text) == 0 ? 0 : 1, - text[0] != '\0', where); -} - -static int -runconnectioncapacity(Daemon *d) +runcapacity(Daemon *d) { DBusConnection *conn[Maxconnections]; - Siglog logs[Maxconnections]; - char names[Maxconnections][32]; - char paths[Maxconnections][96]; int i, ok; for(i = 0; i < Maxconnections; i++) conn[i] = NULL; - memset(logs, 0, sizeof logs); ok = 0; for(i = 0; i < Maxconnections; i++){ - conn[i] = openbus(d->address, &logs[i]); - if(conn[i] == NULL || !hello(conn[i], names[i], sizeof names[i]) || - !createcontext(conn[i], paths[i], sizeof paths[i]) || - !emptycall(conn[i], &logs[i], paths[i], "FocusIn") || - !nosignal(&logs[i], "connection capacity FocusIn") || - !expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, NULL, - "connection capacity key")) + conn[i] = openbus(d->address); + if(conn[i] == NULL || !hello(conn[i])) goto out; } if(!overflowrejected(d->address)) goto out; - for(i = 0; i < Maxconnections; i++) - if(!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, NULL, - "accepted connection after rejection")) - goto out; - closebus(&conn[Maxconnections-1], &logs[Maxconnections-1]); - if(!expectkey(conn[0], &logs[0], paths[0], 0xffe1, 0, 0, NULL, - "connection close barrier")) + closebus(&conn[Maxconnections-1]); + if(!createcontext(conn[0], NULL, 0)) goto out; - conn[Maxconnections-1] = openbus(d->address, - &logs[Maxconnections-1]); + conn[Maxconnections-1] = openbus(d->address); if(conn[Maxconnections-1] == NULL || - !hello(conn[Maxconnections-1], names[Maxconnections-1], - sizeof names[Maxconnections-1]) || - !createcontext(conn[Maxconnections-1], paths[Maxconnections-1], - sizeof paths[Maxconnections-1]) || - !emptycall(conn[Maxconnections-1], &logs[Maxconnections-1], - paths[Maxconnections-1], "FocusIn") || - !nosignal(&logs[Maxconnections-1], "recovered connection FocusIn") || - !capcall(conn[Maxconnections-1], &logs[Maxconnections-1], - paths[Maxconnections-1], Cappreedit) || - !nosignal(&logs[Maxconnections-1], "recovered connection capability") || - !expectkey(conn[Maxconnections-1], &logs[Maxconnections-1], - paths[Maxconnections-1], 'n', Ctrlmask, 1, "", - "recovered connection select Japanese") || - !expectkey(conn[Maxconnections-1], &logs[Maxconnections-1], - paths[Maxconnections-1], 'k', 0, 1, "k", - "recovered connection key")) + !hello(conn[Maxconnections-1]) || + !createcontext(conn[Maxconnections-1], NULL, 0)) goto out; - for(i = 0; i < Maxconnections-1; i++) - if(!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, NULL, - "accepted connection after recovery")) - goto out; ok = 1; out: for(i = 0; i < Maxconnections; i++) - closebus(&conn[i], &logs[i]); - return ok; -} - -static int -runlifecycle(Daemon *d) -{ - DBusConnection *c1, *c2, *c3; - Siglog s1, s2, s3; - char n1[32], n2[32], n3[32]; - char a[96], a2[96], b[96], active[96], path[96], first[96]; - int eaten, i, ok; - - c1 = c2 = c3 = NULL; - memset(&s1, 0, sizeof s1); - memset(&s2, 0, sizeof s2); - memset(&s3, 0, sizeof s3); - ok = 0; - c1 = openbus(d->address, &s1); - c2 = openbus(d->address, &s2); - if(c1 == NULL || c2 == NULL) - goto out; - if(!hello(c1, n1, sizeof n1) || !hello(c2, n2, sizeof n2) || - strcmp(n1, n2) == 0) - goto out; - if(!createcontext(c1, a, sizeof a) || !createcontext(c2, b, sizeof b)) - goto out; - if(!unknowncall(c1, a, "ProcessKeyEvent", DBUS_ERROR_INVALID_ARGS) || - !unknowncall(c1, a, "SetEngine", DBUS_ERROR_INVALID_ARGS)) - goto out; - if(!unknowncall(c1, "/org/freedesktop/IBus/InputContext_missing", - "FocusIn", DBUS_ERROR_UNKNOWN_OBJECT)) - goto out; - if(!expectkey(c1, &s1, a, 'x', 0, 0, NULL, "unfocused key")) - goto out; - if(!cursorcall(c1, &s1, a, -10, -20, 0, 14) || - !nosignal(&s1, "pre-focus cursor") || - !emptycall(c1, &s1, a, "FocusIn") || !nosignal(&s1, "FocusIn") || - !capcall(c1, &s1, a, Cappreedit) || - !nosignal(&s1, "A capability") || - !expectkey(c1, &s1, a, 'x', Relmask, 0, NULL, "physical release")) - goto out; - if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, "", "select Japanese") || - !expectkey(c1, &s1, a, 'k', 0, 1, "k", "A key k") || - !expectkey(c1, &s1, a, 'a', 0, 1, "か", "A key a")) - goto out; - if(!emptycall(c2, &s2, b, "FocusIn") || !nosignal(&s2, "B FocusIn") || - !capcall(c2, &s2, b, Cappreedit) || - !nosignal(&s2, "inactive B capability") || - !expectkey(c2, &s2, b, 'n', 0, 1, "ん", "B takeover") || - !expectkey(c1, &s1, a, 0xffe1, 0, 0, "", "stale A state") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "active B state")) - goto out; - if(!emptycall(c1, &s1, a, "Reset") || - !preedit(&s1, a, "", 0, 0, "stale Reset reply") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "B after stale Reset") || - !emptycall(c1, &s1, a, "FocusOut") || - !preedit(&s1, a, "", 0, 0, "stale FocusOut reply") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "B after stale FocusOut")) - goto out; - if(!emptycall(c1, &s1, a, "FocusIn") || !nosignal(&s1, "stale FocusIn") || - !cursorcall(c1, &s1, a, 90, 100, 0, 20) || - !nosignal(&s1, "stale cursor") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "B after stale cursor") || - !emptycall(c1, &s1, a, "Destroy") || !nosignal(&s1, "stale Destroy") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "B after stale Destroy") || - !unknowncall(c1, a, "FocusIn", DBUS_ERROR_UNKNOWN_OBJECT)) - goto out; - if(!createcontext(c1, a2, sizeof a2) || - !emptycall(c1, &s1, a2, "FocusIn") || !nosignal(&s1, "A2 FocusIn") || - !capcall(c1, &s1, a2, Cappreedit) || - !nosignal(&s1, "A2 capability") || - !cursorcall(c1, &s1, a2, 120, 130, 0, 18) || - !nosignal(&s1, "A2 stale cursor")) - goto out; - closebus(&c1, &s1); - if(!expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "B during stale close") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "B after stale close")) - goto out; - if(!emptycall(c2, &s2, b, "Reset") || - !preedit(&s2, b, "", 0, 0, "active Reset") || - !expectkey(c2, &s2, b, 'k', 0, 1, "k", "reset owner key k") || - !expectkey(c2, &s2, b, 'a', 0, 1, "か", "reset owner key a")) - goto out; - if(!keycall(c2, &s2, b, '0', 0, &eaten) || !eaten || - !committed(&s2, b, "か", "active commit")) - goto out; - if(!expectkey(c2, &s2, b, 'k', 0, 1, "k", "pre-FocusOut key") || - !cursorcall(c2, &s2, b, 50, 60, 0, 12) || - !nosignal(&s2, "active cursor") || - !emptycall(c2, &s2, b, "FocusOut") || - !preedit(&s2, b, "", 0, 0, "active FocusOut") || - !emptycall(c2, &s2, b, "FocusOut") || - !preedit(&s2, b, "", 0, 0, "repeated FocusOut") || - !emptycall(c2, &s2, b, "FocusIn") || !nosignal(&s2, "B refocus") || - !expectkey(c2, &s2, b, 0xffe1, 0, 0, "", "B state after FocusOut") || - !expectkey(c2, &s2, b, 'k', 0, 1, "k", "B after refocus") || - !emptycall(c2, &s2, b, "Destroy") || !nosignal(&s2, "active Destroy") || - !unknowncall(c2, b, "Reset", DBUS_ERROR_UNKNOWN_OBJECT)) - goto out; - if(!createcontext(c2, active, sizeof active)) - goto out; - for(i = 1; i < 64; i++) - if(!createcontext(c2, path, sizeof path)) - goto out; - if(!emptycall(c2, &s2, active, "FocusIn") || - !nosignal(&s2, "capacity owner FocusIn") || - !capcall(c2, &s2, active, Cappreedit) || - !nosignal(&s2, "capacity owner capability") || - !expectkey(c2, &s2, active, 'k', 0, 1, "k", "capacity owner key")) - goto out; - closebus(&c2, &s2); - c3 = openbus(d->address, &s3); - if(c3 == NULL || !hello(c3, n3, sizeof n3)) - goto out; - for(i = 0; i < 64; i++){ - if(!createcontext(c3, path, sizeof path)) - goto out; - if(i == 0) - snprintf(first, sizeof first, "%s", path); - } - if(!limitscall(c3) || - !emptycall(c3, &s3, first, "FocusIn") || - !nosignal(&s3, "reused context FocusIn") || - !capcall(c3, &s3, first, Cappreedit) || - !nosignal(&s3, "reused context capability") || - !expectkey(c3, &s3, first, 'k', 0, 1, "k", "reused context key")) - goto out; - ok = 1; -out: - closebus(&c1, &s1); - closebus(&c2, &s2); - closebus(&c3, &s3); - return ok; -} - -static int -runpolicy(Daemon *d) -{ - DBusConnection *c1, *c2; - Siglog s1, s2; - char n1[32], n2[32], a[96], b[96], typed[16]; - char *query; - int eaten, i, ok; - - c1 = c2 = NULL; - memset(&s1, 0, sizeof s1); - memset(&s2, 0, sizeof s2); - ok = 0; - c1 = openbus(d->address, &s1); - c2 = openbus(d->address, &s2); - if(c1 == NULL || c2 == NULL || - !hello(c1, n1, sizeof n1) || !hello(c2, n2, sizeof n2) || - !createcontext(c1, a, sizeof a) || !createcontext(c2, b, sizeof b) || - !emptycall(c1, &s1, a, "FocusIn") || !nosignal(&s1, "policy FocusIn")) - goto out; - - /* Capability starts at zero; only advertised bit zero affects routing. */ - if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, NULL, - "zero-capability language") || - !expectkey(c1, &s1, a, 'k', 0, 1, NULL, "zero-capability key") || - !emptycall(c1, &s1, a, "Reset") || - !nosignal(&s1, "zero-capability Reset") || - !expectkey(c1, &s1, a, 'k', 0, 1, NULL, - "zero-capability before PASSWORD") || - !contentcall(c1, &s1, a, Purposepassword, 0) || - !nosignal(&s1, "zero-capability PASSWORD") || - !contentcall(c1, &s1, a, Purposefree, 0) || - !nosignal(&s1, "zero-capability leave PASSWORD") || - !expectkey(c1, &s1, a, 'k', 0, 1, NULL, - "zero-capability before FocusOut") || - !emptycall(c1, &s1, a, "FocusOut") || - !nosignal(&s1, "zero-capability FocusOut") || - !emptycall(c1, &s1, a, "FocusIn") || - !nosignal(&s1, "zero-capability refocus") || - !expectkey(c1, &s1, a, 'k', 0, 1, NULL, - "zero-capability after refocus") || - !capcall(c1, &s1, a, 1<<1) || !nosignal(&s1, "non-preedit cap") || - !capcall(c1, &s1, a, Cappreedit|(1<<1)) || - !preedit(&s1, a, "k", 1, 1, "enable inline") || - !capcall(c1, &s1, a, Cappreedit|(1<<1)) || - !nosignal(&s1, "repeat inline capability") || - !capcall(c1, &s1, a, 1<<1) || - !preedit(&s1, a, "", 0, 0, "disable inline") || - !capcall(c1, &s1, a, 1<<1) || - !nosignal(&s1, "repeat popup capability") || - !expectkey(c1, &s1, a, 'a', 0, 1, NULL, "popup-preedit key") || - !capcall(c1, &s1, a, Cappreedit|(1<<1)) || - !preedit(&s1, a, "か", 1, 1, "restore inline")) - goto out; - - /* A focused but inactive context cannot acquire ownership through Keycap. */ - if(!emptycall(c2, &s2, b, "FocusIn") || !nosignal(&s2, "inactive FocusIn") || - !capcall(c2, &s2, b, Cappreedit) || - !nosignal(&s2, "inactive capability") || - !expectkey(c1, &s1, a, 0xffe1, 0, 0, "か", "owner after inactive cap")) - goto out; - - /* Toggling inline display leaves the engine-owned candidate list intact. */ - if(!emptycall(c1, &s1, a, "Reset") || - !preedit(&s1, a, "", 0, 0, "candidate reset") || - !expectkey(c1, &s1, a, 'e', Ctrlmask, 1, "", "begin emoji search")) - goto out; - query = "smile"; - typed[0] = '\0'; - for(i = 0; query[i] != '\0'; i++){ - typed[i] = query[i]; - typed[i+1] = '\0'; - if(!keycall(c1, &s1, a, query[i], 0, &eaten) || !eaten || - !preedit(&s1, a, typed, i+1, 1, "emoji query")) - goto out; - } - if(!capcall(c1, &s1, a, 1<<1) || - !preedit(&s1, a, "", 0, 0, "candidate popup policy") || - !capcall(c1, &s1, a, Cappreedit|(1<<1)) || - !preedit(&s1, a, "smile", 5, 1, "candidate inline policy") || - !keycall(c1, &s1, a, '1', 0, &eaten) || !eaten || - !committed(&s1, a, "😀", "candidate after capability toggle")) - goto out; - - /* PRIVATE preserves composition; hidden purposes and hints reset it. */ - if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, "", "private select Japanese") || - !expectkey(c1, &s1, a, 'n', 0, 1, "ん", "private preedit") || - !contentcall(c1, &s1, a, Purposefree, Hintprivate) || - !nosignal(&s1, "PRIVATE property") || - !expectkey(c1, &s1, a, 'a', 0, 1, "な", "PRIVATE key") || - !contentcall(c1, &s1, a, Purposepassword, 0) || - !preedit(&s1, a, "", 0, 0, "PASSWORD transition") || - !expectkey(c1, &s1, a, 'x', 0, 0, NULL, "PASSWORD key") || - !contentcall(c1, &s1, a, Purposefree, 0) || - !nosignal(&s1, "leave PASSWORD") || - !expectkey(c1, &s1, a, 'k', 0, 1, "k", "after PASSWORD") || - !contentcall(c1, &s1, a, Purposepin, 0) || - !preedit(&s1, a, "", 0, 0, "PIN transition") || - !expectkey(c1, &s1, a, 'x', 0, 0, NULL, "PIN key") || - !contentcall(c1, &s1, a, Purposefree, Hintprivate) || - !nosignal(&s1, "leave PIN with PRIVATE") || - !expectkey(c1, &s1, a, 'n', 0, 1, "ん", "PRIVATE resumes") || - !contentcall(c1, &s1, a, Purposefree, Hinthidden) || - !preedit(&s1, a, "", 0, 0, "HIDDEN_TEXT transition") || - !expectkey(c1, &s1, a, 'x', 0, 0, NULL, "HIDDEN_TEXT key") || - !contentcall(c1, &s1, a, Purposefree, 0) || - !nosignal(&s1, "free-form property") || - !expectkey(c1, &s1, a, 'n', 0, 1, "ん", "free-form resumes")) - goto out; - - /* Modern clients receive only WithMode and code-point attribute ranges. */ - if(!emptycall(c1, &s1, a, "Reset") || - !preedit(&s1, a, "", 0, 0, "modern setup reset") || - !clientcommitcall(c1, &s1, a, 1) || - !nosignal(&s1, "enable client preedit commit") || - !keycall(c1, &s1, a, 'k', 0, &eaten) || !eaten || - !modernpreedit(&s1, a, "k", 1, 1, "modern key k") || - !keycall(c1, &s1, a, 'a', 0, &eaten) || !eaten || - !modernpreedit(&s1, a, "か", 1, 1, "modern key a") || - !keycall(c1, &s1, a, 'n', 0, &eaten) || !eaten || - !modernpreedit(&s1, a, "かん", 2, 1, "modern multibyte") || - !emptycall(c1, &s1, a, "Reset") || - !modernpreedit(&s1, a, "", 0, 0, "modern empty clear") || - !keycall(c1, &s1, a, 'k', 0, &eaten) || !eaten || - !modernpreedit(&s1, a, "k", 1, 1, "modern commit key k") || - !keycall(c1, &s1, a, 'a', 0, &eaten) || !eaten || - !modernpreedit(&s1, a, "か", 1, 1, "modern commit key a") || - !keycall(c1, &s1, a, '0', 0, &eaten) || !eaten || - !moderncommitted(&s1, a, "か", "modern commit") || - !clientcommitcall(c1, &s1, a, 0) || - !nosignal(&s1, "disable client preedit commit") || - !expectkey(c1, &s1, a, 'k', 0, 1, "k", "legacy restored")) - goto out; - ok = 1; -out: - closebus(&c1, &s1); - closebus(&c2, &s2); + closebus(&conn[i]); return ok; } @@ -1610,7 +714,8 @@ runclient(Daemon *d, char *program) if(pid == 0){ if(setenv("XDG_RUNTIME_DIR", d->runtime, 1) < 0 || setenv("XDG_CONFIG_HOME", d->config, 1) < 0 || - setenv("HOME", d->home, 1) < 0) + setenv("HOME", d->home, 1) < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0) _exit(126); execl(program, program, d->address, (char*)0); dprintf(STDERR_FILENO, "exec %s: %s\n", program, strerror(errno)); @@ -1647,27 +752,27 @@ int main(int argc, char **argv) { Daemon daemon; - int ok; + int capacity, ok; + 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; } - ok = startdaemon(&daemon, argv[1], argv[2]); + ok = startdaemon(&daemon, argv[capacity ? 2 : 1], + argv[capacity ? 3 : 2]); if(ok) - ok = runconnectioncapacity(&daemon); - if(ok) - ok = runlifecycle(&daemon); - if(ok) - ok = runpolicy(&daemon); - if(ok) - ok = runclient(&daemon, argv[3]); + ok = capacity ? runcapacity(&daemon) : runclient(&daemon, argv[3]); + if(ok && !capacity) + ok = runcontract(&daemon); if(!ok) showerrors(&daemon); if(!stopdaemon(&daemon)) ok = 0; if(!ok) return 1; - printf("ibus live lifecycle, policy, and connection capacity: ok\n"); + printf(capacity ? "ibus connection capacity: ok\n" : + "ibus official client and malformed call: ok\n"); return 0; } diff --git a/tests/ipc_live_test.c b/tests/ipc_live_test.c index 4b4325e..6766801 100644 --- a/tests/ipc_live_test.c +++ b/tests/ipc_live_test.c @@ -27,6 +27,7 @@ enum Calltimeout = 4000, Starttimeout = 8000, Stoptimeout = 3000, + Cappreedit = 1, }; typedef struct Daemon Daemon; @@ -141,11 +142,19 @@ waitsocket(Daemon *d, int notifyfd) if(lstat(d->socket, &st) == 0 && S_ISSOCK(st.st_mode) && (st.st_mode & 0777) == 0600) return 1; - if(waitpid(d->pid, &status, WNOHANG) == d->pid){ + do + n = waitpid(d->pid, &status, WNOHANG); + while(n < 0 && errno == EINTR); + if(n == d->pid){ d->pid = -1; readerrors(d); return fail("daemon exited before creating IPC socket"); } + if(n < 0){ + if(errno == ECHILD) + d->pid = -1; + return fail("wait for daemon startup: %s", strerror(errno)); + } timeout = leftms(deadline); if(timeout == 0) return fail("timed out waiting for protected IPC socket %s", @@ -182,8 +191,10 @@ startdaemon(Daemon *d, char *program, char *mapdir) d->pid = -1; d->errfd = -1; snprintf(d->root, sizeof d->root, "/tmp/strans-ipc.XXXXXX"); - if(mkdtemp(d->root) == NULL) + if(mkdtemp(d->root) == NULL){ + d->root[0] = '\0'; return fail("mkdtemp: %s", strerror(errno)); + } if(snprintf(d->runtime, sizeof d->runtime, "%s/runtime", d->root) >= (int)sizeof d->runtime || snprintf(d->config, sizeof d->config, "%s/config", d->root) @@ -236,10 +247,10 @@ startdaemon(Daemon *d, char *program, char *mapdir) } if(setenv("XDG_RUNTIME_DIR", d->runtime, 1) < 0 || setenv("XDG_CONFIG_HOME", d->config, 1) < 0 || - setenv("HOME", d->home, 1) < 0 || - setenv("DISPLAY", "invalid", 1) < 0 || + setenv("HOME", d->home, 1) < 0 || unsetenv("DISPLAY") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || - unsetenv("IBUS_ADDRESS") < 0){ + unsetenv("IBUS_ADDRESS") < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0){ dprintf(STDERR_FILENO, "set daemon environment: %s\n", strerror(errno)); _exit(126); @@ -323,6 +334,8 @@ killdaemon(Daemon *d, int *status) if(n != d->pid){ fail("reap %ld after SIGKILL: %s", (long)d->pid, n < 0 ? strerror(errno) : "wrong child"); + if(n < 0 && errno == ECHILD) + d->pid = -1; ok = 0; }else d->pid = -1; @@ -614,6 +627,15 @@ sendreset(int fd, int want) return ipcsend(fd, req, sizeof req) == 0; } +static int +sendcap(int fd, int cap) +{ + unsigned char req[Ipcreqsz]; + + ipcpackcap(req, cap); + return ipcsend(fd, req, sizeof req) == 0; +} + static int expectresponse(int fd, int want, int eaten, char *commit, char *preedit, char *where) @@ -740,7 +762,25 @@ overflowrejected(char *path) } static int -runlistener(Daemon *d) +runsmoke(Daemon *d) +{ + int client, ok; + + client = connectuntil(d->socket, nowms() + Calltimeout); + if(client < 0) + return fail("connect client: %s", strerror(errno)); + ok = sendcap(client, Cappreedit) && + expectresponse(client, 1, 1, "", "", "negotiate preedit") && + requestkey(client, 1, Mctrl, 'n', 1, "", "", + "select Japanese") && + requestkey(client, 1, 0, 'k', 1, "", "k", "preedit") && + requestreset(client, 1, 1, "", "reset"); + close(client); + return ok; +} + +static int +runcapacity(Daemon *d) { int client[Maxclients]; int i, ok; @@ -753,17 +793,8 @@ runlistener(Daemon *d) fail("connect first client: %s", strerror(errno)); goto out; } - if(!requestkey(client[0], 1, Mctrl, 'n', 1, "", "", "select Japanese") || - !requestkey(client[0], 1, 0, 'k', 1, "", "k", "GTK preedit") || - !requestreset(client[0], 1, 1, "", "active reset")) - goto out; - if(!sendkey(client[0], 0, 0, 'k') || - !sendkey(client[0], 1, 0, Kmodfirst)){ - fail("send pipelined XIM/GTK requests: %s", strerror(errno)); - goto out; - } - if(!expectresponse(client[0], 0, 1, "", "", "XIM framing") || - !expectresponse(client[0], 1, 0, "", "k", "GTK after XIM framing")) + if(!requestkey(client[0], 1, Mctrl, 'n', 1, "", "", + "select Japanese")) goto out; for(i = 1; i < Maxclients; i++){ client[i] = connectuntil(d->socket, nowms() + Calltimeout); @@ -799,21 +830,26 @@ int main(int argc, char **argv) { Daemon daemon; - int ok; + char *program, *mapdir; + int capacity, ok; - if(argc != 3){ - fprintf(stderr, "usage: ipc_live_test strans mapdir\n"); + capacity = argc == 4 && strcmp(argv[1], "--capacity") == 0; + if((!capacity && argc != 3) || (capacity && argc != 4)){ + fprintf(stderr, + "usage: ipc_live_test [--capacity] strans mapdir\n"); return 2; } - ok = startdaemon(&daemon, argv[1], argv[2]); + program = argv[1+capacity]; + mapdir = argv[2+capacity]; + ok = startdaemon(&daemon, program, mapdir); if(ok) - ok = runlistener(&daemon); + ok = capacity ? runcapacity(&daemon) : runsmoke(&daemon); if(!ok) showerrors(&daemon); if(!stopdaemon(&daemon)) ok = 0; if(!ok) return 1; - printf("ipc live listener: ok\n"); + printf("ipc %s: ok\n", capacity ? "connection capacity" : "live smoke"); return 0; } diff --git a/tests/unit_test.c b/tests/unit_test.c index 562d393..cea5747 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -61,6 +61,7 @@ testmapinit(void) testvi.map = trieopen("../map/telex.map"); } +#ifndef STRESS static const struct ct_test tests[] = { { "str/init-utf8", str_init_utf8 }, { "str/edit-and-alias", str_edit_and_alias }, @@ -119,7 +120,6 @@ static const struct ct_test tests[] = { { "engine/hanja-korean-keys", engine_hanja_korean_keys }, { "engine/hanja-backspace", engine_hanja_backspace }, { "engine/hanja-input-languages", engine_hanja_input_languages }, - { "engine/randomized-stress", engine_randomized_stress }, { "engine/full-boundary-passthrough", engine_full_boundary_passthrough }, { "dict/candidates", dictionary_candidates }, { "dict/misses-clear-result", dictionary_misses_clear_result }, @@ -155,6 +155,11 @@ static const struct ct_test tests[] = { { "xim/callback-owner-loss", xim_adapter_callback_owner_loss }, { "xim/commit-encoding", xim_adapter_commit_encoding }, }; +#else +static const struct ct_test tests[] = { + { "engine/randomized-stress", engine_randomized_stress }, +}; +#endif void threadmain(int argc, char **argv)