#include #include #include #include #include "../srv.c" #include "../cutest/cutest.h" void testengineinit(int); void testenginehandle(Keyreq*); void* testengineowner(void); void testenginepreedit(Str*); #undef recv typedef struct Enginegate Enginegate; typedef struct Testclient Testclient; struct Enginegate { Channel *seen; Channel *go; Channel *stop; Channel *done; }; struct Testclient { int fd; int peer; dev_t dev; ino_t ino; Channel *done; }; static void enginegate(void *arg) { Enginegate *g; Keyreq req; uchar token; Alt alts[] = { {keyc, &req, CHANRCV, nil}, {nil, &token, CHANRCV, nil}, {nil, nil, CHANEND, nil}, }; g = arg; alts[1].c = g->stop; for(;;) switch(alt(alts)){ case 0: chansend(g->seen, &req); chanrecv(g->go, &token); testenginehandle(&req); break; case 1: chansend(g->done, &token); return; } } static void clientproc(void *arg) { Testclient *client; uchar token; client = arg; clientthread((void*)(uintptr)client->fd); token = 0; chansend(client->done, &token); } static int startclient(struct ct *t, Testclient *client) { struct stat st; int fd[2]; uchar token; memset(client, 0, sizeof *client); client->fd = -1; client->peer = -1; if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) return CT_ERRORF(t, "socketpair failed: %s", strerror(errno)); client->fd = fd[0]; client->peer = fd[1]; if(fstat(client->fd, &st) < 0){ close(client->fd); close(client->peer); client->fd = -1; client->peer = -1; return CT_ERRORF(t, "socket stat failed: %s", strerror(errno)); } client->dev = st.st_dev; client->ino = st.st_ino; client->done = chancreate(sizeof token, 0); token = 0; if(channbsend(clientc, &token) <= 0){ close(client->fd); close(client->peer); chanfree(client->done); client->fd = -1; client->peer = -1; client->done = nil; return CT_ERRORF(t, "client slot unavailable"); } if(proccreate(clientproc, client, 8192) < 0){ chanrecv(clientc, &token); close(client->fd); close(client->peer); chanfree(client->done); client->fd = -1; client->peer = -1; client->done = nil; return CT_ERRORF(t, "client worker creation failed"); } return 1; } static int sendkey(struct ct *t, Testclient *client, int want, u32int mod, u32int key) { uchar req[Ipcreqsz]; ipcpackreq(req, want, mod, key); if(ipcsend(client->peer, req, sizeof req) == 0) return 1; return CT_ERRORF(t, "request send failed: %s", strerror(errno)); } static int sendreset(struct ct *t, Testclient *client, int want) { uchar req[Ipcreqsz]; ipcpackreset(req, want); if(ipcsend(client->peer, req, sizeof req) == 0) return 1; return CT_ERRORF(t, "reset send failed: %s", strerror(errno)); } static Keyreq nextrequest(struct ct *t, Enginegate *g, int op) { Keyreq req; memset(&req, 0, sizeof req); chanrecv(g->seen, &req); CT_EQ_INT(t, op, req.op); return req; } static void allowrequest(Enginegate *g) { uchar token; token = 0; chansend(g->go, &token); } static int readreply(struct ct *t, Testclient *client, int want, char *preedit, int npreedit) { char commit[Maxutf]; Ipcresp resp; if(ipcreadresp(client->peer, want, commit, sizeof commit, preedit, npreedit, &resp) < 0) return CT_ERRORF(t, "response read failed: %s", strerror(errno)); CT_EQ_STR(t, "", commit); return resp.eaten; } static void waitclient(struct ct *t, Testclient *client) { struct stat st; uchar token; chanrecv(client->done, &token); errno = 0; if(fstat(client->fd, &st) == 0) CT_CHECK(t, st.st_dev != client->dev || st.st_ino != client->ino); else CT_EQ_INT(t, EBADF, errno); chanfree(client->done); client->done = nil; } static void disconnectclient(struct ct *t, Enginegate *g, Testclient *client, void *owner) { Keyreq req; uchar token; close(client->peer); client->peer = -1; req = nextrequest(t, g, Keyrelease); if(owner != nil) CT_EQ_PTR(t, owner, req.owner); CT_CHECK(t, channbrecv(client->done, &token) <= 0); allowrequest(g); waitclient(t, client); } void server_connection_ownership(struct ct *t) { Channel *oldclientc; Enginegate gate; Testclient a, b, c; Keyreq req; Drawcmd dc; Str shown; char preedit[Maxutf]; void *aowner, *bowner, *cowner; uchar byte, token; ssize_t n; int gateactive; memset(&gate, 0, sizeof gate); memset(&a, 0, sizeof a); memset(&b, 0, sizeof b); memset(&c, 0, sizeof c); a.fd = a.peer = b.fd = b.peer = c.fd = c.peer = -1; aowner = bowner = cowner = nil; oldclientc = clientc; while(channbrecv(drawc, &dc) > 0) ; clientc = chancreate(sizeof token, 3); gate.seen = chancreate(sizeof(Keyreq), 0); gate.go = chancreate(sizeof token, 0); gate.stop = chancreate(sizeof token, 0); gate.done = chancreate(sizeof token, 0); testengineinit(LangJP); gateactive = proccreate(enginegate, &gate, 8192) >= 0; if(!CT_CHECK(t, gateactive)) goto cleanup; if(!startclient(t, &a) || !startclient(t, &b)) goto cleanup; if(!sendkey(t, &a, 1, 0, 'k')) goto cleanup; req = nextrequest(t, &gate, Keypress); aowner = req.owner; allowrequest(&gate); CT_CHECK(t, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "k", preedit); if(!sendkey(t, &a, 1, 0, 'a')) goto cleanup; req = nextrequest(t, &gate, Keypress); CT_EQ_PTR(t, aowner, req.owner); allowrequest(&gate); CT_CHECK(t, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "か", preedit); CT_EQ_PTR(t, aowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'n')) goto cleanup; req = nextrequest(t, &gate, Keypress); bowner = req.owner; CT_CHECK(t, bowner != aowner); allowrequest(&gate); CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "ん", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendreset(t, &a, 1)) goto cleanup; req = nextrequest(t, &gate, Keyreset); CT_EQ_PTR(t, aowner, req.owner); allowrequest(&gate); CT_CHECK(t, readreply(t, &a, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, Kmodfirst)) goto cleanup; req = nextrequest(t, &gate, Keypress); allowrequest(&gate); CT_CHECK(t, !readreply(t, &b, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "ん", preedit); disconnectclient(t, &gate, &a, aowner); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'y')) goto cleanup; req = nextrequest(t, &gate, Keypress); allowrequest(&gate); CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); if(!sendkey(t, &b, 1, 0, 'a')) goto cleanup; req = nextrequest(t, &gate, Keypress); allowrequest(&gate); CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "にゃ", preedit); if(!sendreset(t, &b, 1)) goto cleanup; req = nextrequest(t, &gate, Keyreset); CT_EQ_PTR(t, bowner, req.owner); allowrequest(&gate); CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 0, 0, 'k')) goto cleanup; req = nextrequest(t, &gate, Keypress); CT_EQ_PTR(t, bowner, req.owner); allowrequest(&gate); CT_CHECK(t, readreply(t, &b, 0, nil, 0)); errno = 0; n = recv(b.peer, &byte, 1, MSG_PEEK|MSG_DONTWAIT); CT_EQ_INT(t, -1, n); CT_CHECK(t, errno == EAGAIN || errno == EWOULDBLOCK); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'a')) goto cleanup; req = nextrequest(t, &gate, Keypress); allowrequest(&gate); CT_CHECK(t, readreply(t, &b, 1, preedit, sizeof preedit)); CT_EQ_STR(t, "か", preedit); disconnectclient(t, &gate, &b, bowner); CT_EQ_PTR(t, nil, testengineowner()); testenginepreedit(&shown); CT_EQ_INT(t, 0, shown.n); if(!startclient(t, &c)) goto cleanup; if(!sendkey(t, &c, 1, 0, 'k')) goto cleanup; req = nextrequest(t, &gate, Keypress); cowner = req.owner; close(c.peer); c.peer = -1; allowrequest(&gate); req = nextrequest(t, &gate, Keyrelease); CT_EQ_PTR(t, cowner, req.owner); CT_EQ_PTR(t, cowner, testengineowner()); CT_CHECK(t, channbrecv(c.done, &token) <= 0); allowrequest(&gate); waitclient(t, &c); CT_EQ_PTR(t, nil, testengineowner()); CT_CHECK(t, channbrecv(clientc, &token) <= 0); cleanup: if(a.peer >= 0) disconnectclient(t, &gate, &a, aowner); if(b.peer >= 0) disconnectclient(t, &gate, &b, bowner); if(c.peer >= 0) disconnectclient(t, &gate, &c, cowner); if(gateactive){ token = 0; chansend(gate.stop, &token); chanrecv(gate.done, &token); } chanfree(gate.seen); chanfree(gate.go); chanfree(gate.stop); chanfree(gate.done); chanfree(clientc); clientc = oldclientc; while(channbrecv(drawc, &dc) > 0) ; }