test: separate quick, live, and stress checks
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user