#include #include #include #include #include "srv.c" #include "test.h" #undef recv typedef struct Serverfix Serverfix; typedef struct Testclient Testclient; /* A held pump plus a private client-slot channel of the wanted depth. */ struct Serverfix { Pump pump; Channel *oldclientc; }; struct Testclient { int fd; int peer; dev_t dev; ino_t ino; Channel *done; }; static void serverbegin(Serverfix *f, int nclients) { Drawcmd dc; uchar token; f->oldclientc = clientc; while(channbrecv(drawc, &dc) > 0) ; clientc = chancreate(sizeof token, nclients); testengineinit(LangJP); pumpstart(&f->pump, 0); pumphold(&f->pump, Pumpall); } static void serverend(Serverfix *f) { Drawcmd dc; pumpstop(&f->pump); chanfree(clientc); clientc = f->oldclientc; while(channbrecv(drawc, &dc) > 0) ; } 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 nextrequestcap(struct ct *t, Pump *p, int op, int cap) { Keyreq req; memset(&req, 0, sizeof req); chanrecv(p->trace, &req); CT_EQ_INT(t, op, req.op); CT_EQ_INT(t, cap, req.cap); return req; } static Keyreq nextrequest(struct ct *t, Pump *p, int op) { return nextrequestcap(t, p, op, Cclientpreedit); } static void allowrequest(Pump *p) { uchar token; token = 0; chansend(p->go, &token); } static int readreply(struct ct *t, Testclient *client, int want, char *wantcommit, char *preedit) { char commit[Ipcfieldmax+1]; Ipcresp resp; if(ipcreadresp(client->peer, want, commit, preedit, &resp) < 0) return CT_ERRORF(t, "response read failed: %s", strerror(errno)); CT_EQ_STR(t, wantcommit, commit); return resp.eaten; } static int sendframe(struct ct *t, Testclient *client, uchar *frame, int n, int fragment) { int i, m; for(i = 0; i < n; i += m){ m = fragment && n-i > 1 ? 1 : n-i; if(ipcsend(client->peer, frame+i, m) < 0) return CT_ERRORF(t, "frame send failed: %s", strerror(errno)); } return 1; } static void checknoreply(struct ct *t, Testclient *client) { uchar byte; ssize_t n; errno = 0; n = recv(client->peer, &byte, 1, MSG_PEEK|MSG_DONTWAIT); CT_EQ_INT(t, -1, n); CT_CHECK(t, errno == EAGAIN || errno == EWOULDBLOCK); } 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, Pump *p, Testclient *client, void *owner) { Keyreq req; uchar token; close(client->peer); client->peer = -1; req = nextrequest(t, p, Keyrelease); if(owner != nil) CT_EQ_PTR(t, owner, req.owner); CT_CHECK(t, channbrecv(client->done, &token) <= 0); allowrequest(p); waitclient(t, client); } void server_connection_ownership(struct ct *t) { Serverfix f; Testclient a, b, c; Keyreq req; Str shown; char preedit[Ipcfieldmax+1]; void *aowner, *bowner, *cowner; uchar byte, token; ssize_t n; 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; serverbegin(&f, 3); if(!startclient(t, &a) || !startclient(t, &b)) goto cleanup; if(!sendkey(t, &a, 1, 0, 'k')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); aowner = req.owner; allowrequest(&f.pump); CT_CHECK(t, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "k", preedit); if(!sendkey(t, &a, 1, 0, 'a')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); CT_EQ_PTR(t, aowner, req.owner); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "か", preedit); CT_EQ_PTR(t, aowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'n')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); bowner = req.owner; CT_CHECK(t, bowner != aowner); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 1, "", preedit)); CT_EQ_STR(t, "ん", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendreset(t, &a, 1)) goto cleanup; req = nextrequest(t, &f.pump, Keyreset); CT_EQ_PTR(t, aowner, req.owner); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 0)) goto cleanup; req = nextrequest(t, &f.pump, Keypress); allowrequest(&f.pump); CT_CHECK(t, !readreply(t, &b, 1, "", preedit)); CT_EQ_STR(t, "ん", preedit); disconnectclient(t, &f.pump, &a, aowner); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'y')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 1, "", preedit)); if(!sendkey(t, &b, 1, 0, 'a')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 1, "", preedit)); CT_EQ_STR(t, "にゃ", preedit); if(!sendreset(t, &b, 1)) goto cleanup; req = nextrequest(t, &f.pump, Keyreset); CT_EQ_PTR(t, bowner, req.owner); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 1, "にゃ", preedit)); CT_EQ_STR(t, "", preedit); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 0, 0, 'k')) goto cleanup; req = nextrequestcap(t, &f.pump, Keypress, 0); CT_EQ_PTR(t, bowner, req.owner); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 0, "", preedit)); 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, &f.pump, Keypress); allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 1, "", preedit)); CT_EQ_STR(t, "か", preedit); disconnectclient(t, &f.pump, &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, &f.pump, Keypress); cowner = req.owner; close(c.peer); c.peer = -1; allowrequest(&f.pump); req = nextrequest(t, &f.pump, Keyrelease); CT_EQ_PTR(t, cowner, req.owner); CT_EQ_PTR(t, cowner, testengineowner()); CT_CHECK(t, channbrecv(c.done, &token) <= 0); allowrequest(&f.pump); waitclient(t, &c); CT_EQ_PTR(t, nil, testengineowner()); CT_CHECK(t, channbrecv(clientc, &token) <= 0); cleanup: if(a.peer >= 0) disconnectclient(t, &f.pump, &a, aowner); if(b.peer >= 0) disconnectclient(t, &f.pump, &b, bowner); if(c.peer >= 0) disconnectclient(t, &f.pump, &c, cowner); serverend(&f); } void server_extension_stream(struct ct *t) { Serverfix f; Testclient a, b; Keyreq req; uchar frame[Ipccaretsz]; char preedit[Ipcfieldmax+1]; void *aowner, *bowner; memset(&a, 0, sizeof a); memset(&b, 0, sizeof b); a.fd = a.peer = b.fd = b.peer = -1; aowner = bowner = nil; serverbegin(&f, 2); if(!startclient(t, &a)) goto cleanup; /* Capability and caret frames may be split at any byte boundary. */ ipcpackcap(frame, Cclientpreedit); if(!sendframe(t, &a, frame, Ipcreqsz, 1)) goto cleanup; req = nextrequest(t, &f.pump, Keycap); aowner = req.owner; CT_EQ_PTR(t, nil, testengineowner()); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "", preedit); ipcpackcaret(frame, 1, -101, -7, 23); if(!sendframe(t, &a, frame, Ipccaretsz, 1)) goto cleanup; req = nextrequest(t, &f.pump, Keycaret); CT_EQ_PTR(t, aowner, req.owner); CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, -101, req.caret.x); CT_EQ_INT(t, -7, req.caret.y); CT_EQ_INT(t, 23, req.caret.h); CT_EQ_PTR(t, nil, testengineowner()); allowrequest(&f.pump); checknoreply(t, &a); ipcpackreq(frame, 1, 0, 'k'); if(!sendframe(t, &a, frame, Ipcreqsz, 1)) goto cleanup; req = nextrequest(t, &f.pump, Keypress); CT_EQ_PTR(t, aowner, req.owner); CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, -101, req.caret.x); CT_EQ_INT(t, -7, req.caret.y); CT_EQ_INT(t, 23, req.caret.h); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "k", preedit); CT_EQ_PTR(t, aowner, testengineowner()); ipcpackcap(frame, 0); if(!sendframe(t, &a, frame, Ipcreqsz, 0)) goto cleanup; req = nextrequestcap(t, &f.pump, Keycap, 0); CT_EQ_PTR(t, aowner, req.owner); CT_EQ_INT(t, 1, req.caret.valid); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 0, "", preedit)); /* Reset is still the old six-byte frame and does not discard caret. */ ipcpackreset(frame, 0); if(!sendframe(t, &a, frame, Ipcreqsz, 1)) goto cleanup; req = nextrequestcap(t, &f.pump, Keyreset, 0); CT_EQ_PTR(t, aowner, req.owner); CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, -101, req.caret.x); CT_EQ_INT(t, -7, req.caret.y); CT_EQ_INT(t, 23, req.caret.h); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 0, "k", preedit)); ipcpackcap(frame, Cclientpreedit); if(!sendframe(t, &a, frame, Ipcreqsz, 0)) goto cleanup; req = nextrequest(t, &f.pump, Keycap); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "", preedit); ipcpackreq(frame, 1, 0, 'n'); if(!sendframe(t, &a, frame, Ipcreqsz, 0)) goto cleanup; req = nextrequest(t, &f.pump, Keypress); CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, -101, req.caret.x); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 1, "", preedit)); CT_EQ_STR(t, "ん", preedit); disconnectclient(t, &f.pump, &a, aowner); /* Disconnect drops state; a legacy client gets the historical defaults. */ if(!startclient(t, &b) || !sendkey(t, &b, 1, 0, 'k')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); bowner = req.owner; CT_EQ_INT(t, 0, req.caret.valid); allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &b, 1, "", preedit)); CT_EQ_STR(t, "k", preedit); disconnectclient(t, &f.pump, &b, bowner); cleanup: if(a.peer >= 0) disconnectclient(t, &f.pump, &a, aowner); if(b.peer >= 0) disconnectclient(t, &f.pump, &b, bowner); serverend(&f); } void server_rejects_unknown_extension(struct ct *t) { Serverfix f; Testclient badver, badop, good; Keyreq req; uchar frame[Ipccaretsz]; char preedit[Ipcfieldmax+1]; void *owner; memset(&badver, 0, sizeof badver); memset(&badop, 0, sizeof badop); memset(&good, 0, sizeof good); badver.fd = badver.peer = badop.fd = badop.peer = -1; good.fd = good.peer = -1; owner = nil; serverbegin(&f, 3); if(!startclient(t, &badver)) goto cleanup; ipcpackcaret(frame, 1, 3, 4, 5); frame[1]++; if(!sendframe(t, &badver, frame, sizeof frame, 0)) goto cleanup; req = nextrequest(t, &f.pump, Keyrelease); allowrequest(&f.pump); waitclient(t, &badver); close(badver.peer); badver.peer = -1; if(!startclient(t, &badop)) goto cleanup; ipcpackcaret(frame, 1, 3, 4, 5); frame[2]++; if(!sendframe(t, &badop, frame, sizeof frame, 1)) goto cleanup; req = nextrequest(t, &f.pump, Keyrelease); allowrequest(&f.pump); waitclient(t, &badop); close(badop.peer); badop.peer = -1; /* A malformed peer must not disturb another client connection. */ if(!startclient(t, &good) || !sendkey(t, &good, 1, 0, 'k')) goto cleanup; req = nextrequest(t, &f.pump, Keypress); owner = req.owner; allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &good, 1, "", preedit)); CT_EQ_STR(t, "k", preedit); disconnectclient(t, &f.pump, &good, owner); cleanup: if(badver.peer >= 0) disconnectclient(t, &f.pump, &badver, nil); if(badop.peer >= 0) disconnectclient(t, &f.pump, &badop, nil); if(good.peer >= 0) disconnectclient(t, &f.pump, &good, owner); serverend(&f); }