From 795f11be22b88358b7597989cb826962bb233f64 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 16 Aug 2026 16:30:32 +0900 Subject: [PATCH] test: one engine pump and one preedit check for the white-box suites server_test, ibus_test and xim_adapter_test each ran their own copy of the same alt loop that stands in for imthread, and two of them had private checkstr/checkenginepreedit variants. test_util.c now provides Pump (trace, optional hold on a chosen op, stop) in its own proc so a test may block in socket I/O while the engine runs, and test.h declares the engine hooks once; every .c includes dat.h and fn.h itself as the rest of the tree does. server_test's three copies of fixture setup and teardown became serverbegin/serverend. --- tests/dict_test.c | 2 + tests/engine_test.c | 7 +- tests/font_test.c | 2 + tests/ibus_test.c | 110 +++----------- tests/ipc_test.c | 2 + tests/keymap_test.c | 2 + tests/ko_test.c | 2 + tests/popup_test.c | 2 + tests/server_test.c | 299 ++++++++++++++------------------------- tests/str_test.c | 2 + tests/test.h | 36 ++++- tests/test_util.c | 82 +++++++++++ tests/trie_test.c | 2 + tests/unit_test.c | 2 + tests/vi_test.c | 2 + tests/xim_adapter_test.c | 120 ++++------------ 16 files changed, 288 insertions(+), 386 deletions(-) diff --git a/tests/dict_test.c b/tests/dict_test.c index 44cb19f..acd4142 100644 --- a/tests/dict_test.c +++ b/tests/dict_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" void diff --git a/tests/engine_test.c b/tests/engine_test.c index f25c1cc..5e36e9e 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -1,10 +1,5 @@ #include "strans.c" -#include "cutest/cutest.h" - -extern Lang testvi; - -Str mkstr(char*); -int checkstr(struct ct*, char*, char*, Str*); +#include "test.h" static int typekeys(struct ct*, char*, Str*); static int draindraw(Drawcmd*); diff --git a/tests/font_test.c b/tests/font_test.c index a7cfe3a..73d982d 100644 --- a/tests/font_test.c +++ b/tests/font_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" enum diff --git a/tests/ibus_test.c b/tests/ibus_test.c index 83814eb..b5165f1 100644 --- a/tests/ibus_test.c +++ b/tests/ibus_test.c @@ -1,13 +1,8 @@ #include "ibus.c" -#include "cutest/cutest.h" +#include "test.h" #include -void testengineinit(int); -void testenginehandle(Keyreq*); -void* testengineowner(void); -void testenginepreedit(Str*); -void testenginecaret(Caret*); enum { @@ -18,12 +13,9 @@ typedef struct Ibusfix Ibusfix; struct Ibusfix { Channel *oldreply; - Channel *trace; - Channel *stop; - Channel *done; + Pump pump; DBusConnection *c1; DBusConnection *c2; - int pumpactive; }; void @@ -164,32 +156,6 @@ cleanup: } } -static void -enginepump(void *arg) -{ - Ibusfix *f; - Keyreq req; - uchar token; - Alt alts[] = { - {keyc, &req, CHANRCV, nil}, - {nil, &token, CHANRCV, nil}, - {nil, nil, CHANEND, nil}, - }; - - f = arg; - alts[1].c = f->stop; - for(;;) - switch(alt(alts)){ - case 0: - chansend(f->trace, &req); - testenginehandle(&req); - break; - case 1: - chansend(f->done, &token); - return; - } -} - static int ibusbegin(struct ct *t, Ibusfix *f) { @@ -205,33 +171,22 @@ ibusbegin(struct ct *t, Ibusfix *f) testengineinit(LangEN); f->oldreply = replyc; replyc = chancreate(sizeof(Keyres), 0); - f->trace = chancreate(sizeof(Keyreq), Maxcontexts); - f->stop = chancreate(sizeof(uchar), 0); - f->done = chancreate(sizeof(uchar), 0); - f->pumpactive = threadcreate(enginepump, f, 8192) >= 0; + pumpstart(&f->pump, Maxcontexts); f->c1 = malloc(1); f->c2 = malloc(1); - return CT_CHECK(t, f->pumpactive && f->c1 != nil && f->c2 != nil); + return CT_CHECK(t, f->c1 != nil && f->c2 != nil); } static void ibusend(Ibusfix *f) { Drawcmd dc; - Keyreq req; - uchar token; int i; - if(f->pumpactive){ - for(i = 0; i < nelem(contexts); i++) - if(contexts[i].conn != nil) - dropcontext(&contexts[i]); - while(channbrecv(f->trace, &req) > 0) - ; - token = 0; - chansend(f->stop, &token); - chanrecv(f->done, &token); - } + for(i = 0; i < nelem(contexts); i++) + if(contexts[i].conn != nil) + dropcontext(&contexts[i]); + pumpstop(&f->pump); memset(contexts, 0, sizeof contexts); memset(conns, 0, sizeof conns); nconns = 0; @@ -243,9 +198,6 @@ ibusend(Ibusfix *f) free(f->c2); chanfree(replyc); replyc = f->oldreply; - chanfree(f->trace); - chanfree(f->stop); - chanfree(f->done); } static Keyreq @@ -254,7 +206,7 @@ nexttrace(struct ct *t, Ibusfix *f, int op, Ictx *ctx) Keyreq req; memset(&req, 0, sizeof req); - if(!CT_CHECK(t, channbrecv(f->trace, &req) > 0)) + if(!CT_CHECK(t, channbrecv(f->pump.trace, &req) > 0)) return req; CT_EQ_INT(t, op, req.op); CT_EQ_PTR(t, ctx, req.owner); @@ -269,27 +221,7 @@ notrace(struct ct *t, Ibusfix *f) { Keyreq req; - CT_CHECK(t, channbrecv(f->trace, &req) <= 0); -} - -static void -checkpreedit(struct ct *t, char *want, Keyres *res) -{ - char got[Maxutf]; - - stoutf(&res->preedit, got, sizeof got); - CT_EQ_STR(t, want, got); -} - -static void -checkenginepreedit(struct ct *t, char *want) -{ - char got[Maxutf]; - Str preedit; - - testenginepreedit(&preedit); - stoutf(&preedit, got, sizeof got); - CT_EQ_STR(t, want, got); + CT_CHECK(t, channbrecv(f->pump.trace, &req) <= 0); } static Keyres @@ -321,7 +253,7 @@ ibus_capability_policy(struct ct *t) res = contextkey(t, &f, a, 'n', Testctrlmask); CT_CHECK(t, res.eaten); res = contextkey(t, &f, a, 'k', 0); - checkpreedit(t, "k", &res); + checkstr(t, "preedit", "k", &res.preedit); CT_EQ_PTR(t, a, testengineowner()); a->cap = Cclientpreedit; @@ -329,7 +261,7 @@ ibus_capability_policy(struct ct *t) sendrequest(a, Keycap, 0, 0, &res); nexttrace(t, &f, Keycap, a); CT_CHECK(t, res.eaten); - checkpreedit(t, "k", &res); + checkstr(t, "preedit", "k", &res.preedit); b->focused = 1; b->cap = Cclientpreedit; @@ -345,7 +277,7 @@ ibus_capability_policy(struct ct *t) sendrequest(a, Keycap, 0, 0, &res); nexttrace(t, &f, Keycap, a); CT_CHECK(t, res.eaten); - checkpreedit(t, "k", &res); + checkstr(t, "preedit", "k", &res.preedit); cleanup: ibusend(&f); } @@ -382,7 +314,7 @@ ibus_private_input_policy(struct ct *t) ctx->hints = 0; contextkey(t, &f, ctx, 'n', Testctrlmask); res = contextkey(t, &f, ctx, 'k', 0); - checkpreedit(t, "k", &res); + checkstr(t, "preedit", "k", &res.preedit); ctx->purpose = Ibuspurposepassword; memset(&res, 0, sizeof res); CT_CHECK(t, !processkey(ctx, 'x', 0, &res)); @@ -393,7 +325,7 @@ ibus_private_input_policy(struct ct *t) ctx->purpose = 0; ctx->hints = 1<<11; res = contextkey(t, &f, ctx, 'a', 0); - checkpreedit(t, "か", &res); + checkstr(t, "preedit", "か", &res.preedit); cleanup: ibusend(&f); } @@ -450,11 +382,11 @@ ibus_context_lifecycle(struct ct *t) CT_CHECK(t, res.eaten); contextkey(t, &f, a, 'k', 0); res = contextkey(t, &f, a, 'a', 0); - checkpreedit(t, "か", &res); + checkstr(t, "preedit", "か", &res.preedit); b->focused = 1; res = contextkey(t, &f, b, 'n', 0); - checkpreedit(t, "ん", &res); + checkstr(t, "preedit", "ん", &res.preedit); CT_EQ_PTR(t, b, testengineowner()); setcursor(b, 50, 60, 12); @@ -468,7 +400,7 @@ ibus_context_lifecycle(struct ct *t) memset(&res, 0, sizeof res); sendrequest(a, Keyreset, 0, 0, &res); nexttrace(t, &f, Keyreset, a); - checkpreedit(t, "", &res); + checkstr(t, "preedit", "", &res.preedit); checkenginepreedit(t, "ん"); CT_EQ_PTR(t, b, testengineowner()); @@ -536,17 +468,17 @@ ibus_active_release_lifecycle(struct ct *t) ctx->focused = 1; contextkey(t, &f, ctx, 'n', Testctrlmask); res = contextkey(t, &f, ctx, 'n', 0); - checkpreedit(t, "ん", &res); + checkstr(t, "preedit", "ん", &res.preedit); memset(&res, 0, sizeof res); sendrequest(ctx, Keyreset, 0, 0, &res); nexttrace(t, &f, Keyreset, ctx); - checkpreedit(t, "", &res); + checkstr(t, "preedit", "", &res.preedit); checkenginepreedit(t, ""); CT_CHECK(t, ctx->focused); CT_EQ_PTR(t, ctx, testengineowner()); res = contextkey(t, &f, ctx, 'k', 0); - checkpreedit(t, "k", &res); + checkstr(t, "preedit", "k", &res.preedit); CT_EQ_PTR(t, ctx, testengineowner()); releasecontext(ctx); diff --git a/tests/ipc_test.c b/tests/ipc_test.c index e648fb2..a10673f 100644 --- a/tests/ipc_test.c +++ b/tests/ipc_test.c @@ -7,6 +7,8 @@ #include #include #include +#include "dat.h" +#include "fn.h" #include "test.h" #undef accept diff --git a/tests/keymap_test.c b/tests/keymap_test.c index f05ab5d..23a0fe4 100644 --- a/tests/keymap_test.c +++ b/tests/keymap_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" #include #include diff --git a/tests/ko_test.c b/tests/ko_test.c index d952a78..01e9153 100644 --- a/tests/ko_test.c +++ b/tests/ko_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" static Str diff --git a/tests/popup_test.c b/tests/popup_test.c index 5a7c662..0043869 100644 --- a/tests/popup_test.c +++ b/tests/popup_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" enum { diff --git a/tests/server_test.c b/tests/server_test.c index 13e3677..d42f474 100644 --- a/tests/server_test.c +++ b/tests/server_test.c @@ -4,24 +4,18 @@ #include #include "srv.c" -#include "cutest/cutest.h" - -void testengineinit(int); -void testenginehandle(Keyreq*); -void* testengineowner(void); -void testenginepreedit(Str*); +#include "test.h" #undef recv -typedef struct Enginegate Enginegate; +typedef struct Serverfix Serverfix; typedef struct Testclient Testclient; -struct Enginegate +/* A held pump plus a private client-slot channel of the wanted depth. */ +struct Serverfix { - Channel *seen; - Channel *go; - Channel *stop; - Channel *done; + Pump pump; + Channel *oldclientc; }; struct Testclient @@ -34,30 +28,30 @@ struct Testclient }; static void -enginegate(void *arg) +serverbegin(Serverfix *f, int nclients) { - Enginegate *g; - Keyreq req; + Drawcmd dc; 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; - } + 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 @@ -142,30 +136,30 @@ sendreset(struct ct *t, Testclient *client, int want) } static Keyreq -nextrequestcap(struct ct *t, Enginegate *g, int op, int cap) +nextrequestcap(struct ct *t, Pump *p, int op, int cap) { Keyreq req; memset(&req, 0, sizeof req); - chanrecv(g->seen, &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, Enginegate *g, int op) +nextrequest(struct ct *t, Pump *p, int op) { - return nextrequestcap(t, g, op, Cclientpreedit); + return nextrequestcap(t, p, op, Cclientpreedit); } static void -allowrequest(Enginegate *g) +allowrequest(Pump *p) { uchar token; token = 0; - chansend(g->go, &token); + chansend(p->go, &token); } static int @@ -222,126 +216,111 @@ waitclient(struct ct *t, Testclient *client) } static void -disconnectclient(struct ct *t, Enginegate *g, Testclient *client, void *owner) +disconnectclient(struct ct *t, Pump *p, Testclient *client, void *owner) { Keyreq req; uchar token; close(client->peer); client->peer = -1; - req = nextrequest(t, g, Keyrelease); + req = nextrequest(t, p, Keyrelease); if(owner != nil) CT_EQ_PTR(t, owner, req.owner); CT_CHECK(t, channbrecv(client->done, &token) <= 0); - allowrequest(g); + allowrequest(p); waitclient(t, client); } void server_connection_ownership(struct ct *t) { - Channel *oldclientc; - Enginegate gate; + Serverfix f; Testclient a, b, c; Keyreq req; - Drawcmd dc; Str shown; char preedit[Ipcfieldmax+1]; 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; + serverbegin(&f, 3); if(!startclient(t, &a) || !startclient(t, &b)) goto cleanup; if(!sendkey(t, &a, 1, 0, 'k')) goto cleanup; - req = nextrequest(t, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); aowner = req.owner; - allowrequest(&gate); + 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, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); CT_EQ_PTR(t, aowner, req.owner); - allowrequest(&gate); + 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, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); bowner = req.owner; CT_CHECK(t, bowner != aowner); - allowrequest(&gate); + 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, &gate, Keyreset); + req = nextrequest(t, &f.pump, Keyreset); CT_EQ_PTR(t, aowner, req.owner); - allowrequest(&gate); + 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, Kmodfirst)) goto cleanup; - req = nextrequest(t, &gate, Keypress); - allowrequest(&gate); + req = nextrequest(t, &f.pump, Keypress); + allowrequest(&f.pump); CT_CHECK(t, !readreply(t, &b, 1, preedit)); CT_EQ_STR(t, "ん", preedit); - disconnectclient(t, &gate, &a, aowner); + disconnectclient(t, &f.pump, &a, aowner); CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'y')) goto cleanup; - req = nextrequest(t, &gate, Keypress); - allowrequest(&gate); + 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, &gate, Keypress); - allowrequest(&gate); + 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, &gate, Keyreset); + req = nextrequest(t, &f.pump, Keyreset); CT_EQ_PTR(t, bowner, req.owner); - allowrequest(&gate); + 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, &gate, Keypress, 0); + req = nextrequestcap(t, &f.pump, Keypress, 0); CT_EQ_PTR(t, bowner, req.owner); - allowrequest(&gate); + allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 0, preedit)); errno = 0; n = recv(b.peer, &byte, 1, MSG_PEEK|MSG_DONTWAIT); @@ -350,12 +329,12 @@ server_connection_ownership(struct ct *t) CT_EQ_PTR(t, bowner, testengineowner()); if(!sendkey(t, &b, 1, 0, 'a')) goto cleanup; - req = nextrequest(t, &gate, Keypress); - allowrequest(&gate); + req = nextrequest(t, &f.pump, Keypress); + allowrequest(&f.pump); CT_CHECK(t, readreply(t, &b, 1, preedit)); CT_EQ_STR(t, "か", preedit); - disconnectclient(t, &gate, &b, bowner); + disconnectclient(t, &f.pump, &b, bowner); CT_EQ_PTR(t, nil, testengineowner()); testenginepreedit(&shown); CT_EQ_INT(t, 0, shown.n); @@ -364,72 +343,45 @@ server_connection_ownership(struct ct *t) goto cleanup; if(!sendkey(t, &c, 1, 0, 'k')) goto cleanup; - req = nextrequest(t, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); cowner = req.owner; close(c.peer); c.peer = -1; - allowrequest(&gate); - req = nextrequest(t, &gate, Keyrelease); + 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(&gate); + 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, &gate, &a, aowner); + disconnectclient(t, &f.pump, &a, aowner); if(b.peer >= 0) - disconnectclient(t, &gate, &b, bowner); + disconnectclient(t, &f.pump, &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) - ; + disconnectclient(t, &f.pump, &c, cowner); + serverend(&f); } void server_extension_stream(struct ct *t) { - Channel *oldclientc; - Enginegate gate; + Serverfix f; Testclient a, b; Keyreq req; - Drawcmd dc; - uchar frame[Ipccaretsz], token; + uchar frame[Ipccaretsz]; char preedit[Ipcfieldmax+1]; void *aowner, *bowner; - int gateactive; - memset(&gate, 0, sizeof gate); memset(&a, 0, sizeof a); memset(&b, 0, sizeof b); a.fd = a.peer = b.fd = b.peer = -1; aowner = bowner = nil; - oldclientc = clientc; - while(channbrecv(drawc, &dc) > 0) - ; - clientc = chancreate(sizeof token, 2); - 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; + serverbegin(&f, 2); if(!startclient(t, &a)) goto cleanup; @@ -437,36 +389,36 @@ server_extension_stream(struct ct *t) ipcpackcap(frame, Cclientpreedit); if(!sendframe(t, &a, frame, Ipcreqsz, 1)) goto cleanup; - req = nextrequest(t, &gate, Keycap); + req = nextrequest(t, &f.pump, Keycap); aowner = req.owner; CT_EQ_PTR(t, nil, testengineowner()); - allowrequest(&gate); + 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, &gate, Keycaret); + 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(&gate); + allowrequest(&f.pump); checknoreply(t, &a); ipcpackreq(frame, 1, 0, 'k'); if(!sendframe(t, &a, frame, Ipcreqsz, 1)) goto cleanup; - req = nextrequest(t, &gate, Keypress); + 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(&gate); + 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()); @@ -474,108 +426,81 @@ server_extension_stream(struct ct *t) ipcpackcap(frame, 0); if(!sendframe(t, &a, frame, Ipcreqsz, 0)) goto cleanup; - req = nextrequestcap(t, &gate, Keycap, 0); + req = nextrequestcap(t, &f.pump, Keycap, 0); CT_EQ_PTR(t, aowner, req.owner); CT_EQ_INT(t, 1, req.caret.valid); - allowrequest(&gate); + 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, &gate, Keyreset, 0); + 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(&gate); + allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 0, preedit)); ipcpackcap(frame, Cclientpreedit); if(!sendframe(t, &a, frame, Ipcreqsz, 0)) goto cleanup; - req = nextrequest(t, &gate, Keycap); - allowrequest(&gate); + 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, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); CT_EQ_INT(t, 1, req.caret.valid); CT_EQ_INT(t, -101, req.caret.x); - allowrequest(&gate); + allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &a, 1, preedit)); CT_EQ_STR(t, "ん", preedit); - disconnectclient(t, &gate, &a, aowner); + 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, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); bowner = req.owner; CT_EQ_INT(t, 0, req.caret.valid); - allowrequest(&gate); + allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &b, 1, preedit)); CT_EQ_STR(t, "k", preedit); - disconnectclient(t, &gate, &b, bowner); + disconnectclient(t, &f.pump, &b, bowner); cleanup: if(a.peer >= 0) - disconnectclient(t, &gate, &a, aowner); + disconnectclient(t, &f.pump, &a, aowner); if(b.peer >= 0) - disconnectclient(t, &gate, &b, bowner); - 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) - ; + disconnectclient(t, &f.pump, &b, bowner); + serverend(&f); } void server_rejects_unknown_extension(struct ct *t) { - Channel *oldclientc; - Enginegate gate; + Serverfix f; Testclient badver, badop, good; Keyreq req; - Drawcmd dc; - uchar frame[Ipccaretsz], token; + uchar frame[Ipccaretsz]; char preedit[Ipcfieldmax+1]; void *owner; - int gateactive; - memset(&gate, 0, sizeof gate); 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; - 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; + serverbegin(&f, 3); if(!startclient(t, &badver)) goto cleanup; @@ -583,8 +508,8 @@ server_rejects_unknown_extension(struct ct *t) frame[1]++; if(!sendframe(t, &badver, frame, sizeof frame, 0)) goto cleanup; - req = nextrequest(t, &gate, Keyrelease); - allowrequest(&gate); + req = nextrequest(t, &f.pump, Keyrelease); + allowrequest(&f.pump); waitclient(t, &badver); close(badver.peer); badver.peer = -1; @@ -595,8 +520,8 @@ server_rejects_unknown_extension(struct ct *t) frame[2]++; if(!sendframe(t, &badop, frame, sizeof frame, 1)) goto cleanup; - req = nextrequest(t, &gate, Keyrelease); - allowrequest(&gate); + req = nextrequest(t, &f.pump, Keyrelease); + allowrequest(&f.pump); waitclient(t, &badop); close(badop.peer); badop.peer = -1; @@ -604,31 +529,19 @@ server_rejects_unknown_extension(struct ct *t) /* A malformed peer must not disturb another client connection. */ if(!startclient(t, &good) || !sendkey(t, &good, 1, 0, 'k')) goto cleanup; - req = nextrequest(t, &gate, Keypress); + req = nextrequest(t, &f.pump, Keypress); owner = req.owner; - allowrequest(&gate); + allowrequest(&f.pump); CT_EQ_INT(t, 1, readreply(t, &good, 1, preedit)); CT_EQ_STR(t, "k", preedit); - disconnectclient(t, &gate, &good, owner); + disconnectclient(t, &f.pump, &good, owner); cleanup: if(badver.peer >= 0) - disconnectclient(t, &gate, &badver, nil); + disconnectclient(t, &f.pump, &badver, nil); if(badop.peer >= 0) - disconnectclient(t, &gate, &badop, nil); + disconnectclient(t, &f.pump, &badop, nil); if(good.peer >= 0) - disconnectclient(t, &gate, &good, owner); - 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) - ; + disconnectclient(t, &f.pump, &good, owner); + serverend(&f); } diff --git a/tests/str_test.c b/tests/str_test.c index 4cc89d8..cadf94b 100644 --- a/tests/str_test.c +++ b/tests/str_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" void diff --git a/tests/test.h b/tests/test.h index e5a4130..d9fe90a 100644 --- a/tests/test.h +++ b/tests/test.h @@ -1,11 +1,43 @@ +/* Included after dat.h and fn.h (or after the source under test). */ #include "cutest/cutest.h" -#include "dat.h" -#include "fn.h" extern Lang testvi; +/* + * A test's stand-in for imthread: handles engine requests from keyc, + * tracing each one first. Requests whose op is holdop (every request + * when holdop is Pumpall) wait on go before the engine sees them. + */ +typedef struct Pump Pump; +struct Pump +{ + Channel *trace; + Channel *go; + Channel *stop; + Channel *done; + int holdop; + int active; +}; + +enum +{ + Pumpnone = -1, + Pumpall = -2, +}; + Str mkstr(char*); int checkstr(struct ct*, char*, char*, Str*); +void checkenginepreedit(struct ct*, char*); +void pumpstart(Pump*, int); +void pumphold(Pump*, int); +void pumpstop(Pump*); + +/* Engine internals reached through engine_test.c. */ +void testengineinit(int); +void testenginehandle(Keyreq*); +void* testengineowner(void); +void testenginepreedit(Str*); +void testenginecaret(Caret*); void str_init_utf8(struct ct*); void str_edit_and_alias(struct ct*); diff --git a/tests/test_util.c b/tests/test_util.c index 0b11bd6..c16385f 100644 --- a/tests/test_util.c +++ b/tests/test_util.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" Str @@ -20,3 +22,83 @@ checkstr(struct ct *t, char *where, char *want, Str *got) return CT_ERRORF(t, "%s: want \"%s\", got \"%s\"", where, want, buf); } + +void +checkenginepreedit(struct ct *t, char *want) +{ + Str preedit; + + testenginepreedit(&preedit); + checkstr(t, "engine preedit", want, &preedit); +} + +static void +pumpthread(void *arg) +{ + Pump *p; + Keyreq req; + uchar token; + Alt alts[] = { + {nil, &req, CHANRCV, nil}, + {nil, &token, CHANRCV, nil}, + {nil, nil, CHANEND, nil}, + }; + + p = arg; + alts[0].c = keyc; + alts[1].c = p->stop; + for(;;) + switch(alt(alts)){ + case 0: + chansend(p->trace, &req); + if(p->holdop == Pumpall || p->holdop == req.op) + chanrecv(p->go, &token); + testenginehandle(&req); + break; + case 1: + chansend(p->done, &token); + return; + } +} + +/* ntrace is the trace channel's depth: 0 makes every trace a rendezvous. */ +void +pumpstart(Pump *p, int ntrace) +{ + memset(p, 0, sizeof *p); + p->trace = chancreate(sizeof(Keyreq), ntrace); + p->go = chancreate(sizeof(uchar), 0); + p->stop = chancreate(sizeof(uchar), 0); + p->done = chancreate(sizeof(uchar), 0); + p->holdop = Pumpnone; + /* Its own proc: tests block in socket I/O while the engine runs. */ + proccreate(pumpthread, p, 8192); + p->active = 1; +} + +void +pumphold(Pump *p, int op) +{ + p->holdop = op; +} + +/* Every held request must have been let go before stopping. */ +void +pumpstop(Pump *p) +{ + Keyreq req; + uchar token; + + if(!p->active) + return; + while(channbrecv(p->trace, &req) > 0) + ; + token = 0; + chansend(p->stop, &token); + chanrecv(p->done, &token); + chanfree(p->trace); + chanfree(p->go); + chanfree(p->stop); + chanfree(p->done); + p->active = 0; +} diff --git a/tests/trie_test.c b/tests/trie_test.c index c3fb877..8d893e3 100644 --- a/tests/trie_test.c +++ b/tests/trie_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" void diff --git a/tests/unit_test.c b/tests/unit_test.c index abb80dc..e094606 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -1,4 +1,6 @@ #define CT_IMPLEMENTATION +#include "dat.h" +#include "fn.h" #include "test.h" #include diff --git a/tests/vi_test.c b/tests/vi_test.c index 93d24c4..ef16314 100644 --- a/tests/vi_test.c +++ b/tests/vi_test.c @@ -1,3 +1,5 @@ +#include "dat.h" +#include "fn.h" #include "test.h" static void diff --git a/tests/xim_adapter_test.c b/tests/xim_adapter_test.c index 65a8db0..b011170 100644 --- a/tests/xim_adapter_test.c +++ b/tests/xim_adapter_test.c @@ -85,7 +85,7 @@ static int wireflush(xcb_connection_t*); #undef xcb_translate_coordinates #undef xcb_translate_coordinates_reply #undef xcb_flush -#include "cutest/cutest.h" +#include "test.h" enum { @@ -384,22 +384,11 @@ testkeystate(char *layout) return state; } -void testengineinit(int); -void testenginehandle(Keyreq*); -void* testengineowner(void); -void testenginepreedit(Str*); - typedef struct Ximfix Ximfix; struct Ximfix { Channel *oldreply; - Channel *trace; - Channel *stop; - Channel *done; - Channel *waiting; - Channel *ack; - int holdrelease; - int pumpactive; + Pump pump; }; typedef struct Freejob Freejob; @@ -409,37 +398,6 @@ struct Freejob Channel *done; }; -static void -enginepump(void *arg) -{ - Ximfix *f; - Keyreq req; - uchar token; - Alt alts[] = { - {keyc, &req, CHANRCV, nil}, - {nil, &token, CHANRCV, nil}, - {nil, nil, CHANEND, nil}, - }; - - f = arg; - alts[1].c = f->stop; - for(;;) - switch(alt(alts)){ - case 0: - chansend(f->trace, &req); - if(f->holdrelease && req.op == Keyrelease){ - token = 0; - chansend(f->waiting, &token); - chanrecv(f->ack, &token); - } - testenginehandle(&req); - break; - case 1: - chansend(f->done, &token); - return; - } -} - static int ximbegin(struct ct *t, Ximfix *f, int lang) { @@ -451,38 +409,22 @@ ximbegin(struct ct *t, Ximfix *f, int lang) testengineinit(lang); f->oldreply = replyc; replyc = chancreate(sizeof(Keyres), 0); - f->trace = chancreate(sizeof(Keyreq), 16); - f->stop = chancreate(sizeof(uchar), 0); - f->done = chancreate(sizeof(uchar), 0); - f->waiting = chancreate(sizeof(uchar), 0); - f->ack = chancreate(sizeof(uchar), 0); - f->pumpactive = threadcreate(enginepump, f, 8192) >= 0; - return CT_CHECK(t, replyc != nil && f->trace != nil && - f->stop != nil && f->done != nil && f->waiting != nil && - f->ack != nil && f->pumpactive); + pumpstart(&f->pump, 16); + USED(t); + return 1; } static void ximend(Ximfix *f) { Drawcmd dc; - uchar token; - if(f->pumpactive){ - token = 0; - chansend(f->stop, &token); - chanrecv(f->done, &token); - } + pumpstop(&f->pump); testengineinit(LangEN); while(channbrecv(drawc, &dc) > 0) ; chanfree(replyc); replyc = f->oldreply; - chanfree(f->trace); - chanfree(f->stop); - chanfree(f->done); - chanfree(f->waiting); - chanfree(f->ack); } static Keyreq @@ -491,7 +433,7 @@ nexttracecap(struct ct *t, Ximfix *f, int op, Ic *owner, int cap) Keyreq req; memset(&req, 0, sizeof req); - if(!CT_CHECK(t, channbrecv(f->trace, &req) > 0)) + if(!CT_CHECK(t, channbrecv(f->pump.trace, &req) > 0)) return req; CT_EQ_INT(t, op, req.op); CT_EQ_PTR(t, owner, req.owner); @@ -511,25 +453,7 @@ notrace(struct ct *t, Ximfix *f) { Keyreq req; - CT_CHECK(t, channbrecv(f->trace, &req) <= 0); -} - -static void -checkstr(struct ct *t, char *want, Str *got) -{ - char buf[Maxutf]; - - stoutf(got, buf, sizeof buf); - CT_EQ_STR(t, want, buf); -} - -static void -checkenginepreedit(struct ct *t, char *want) -{ - Str preedit; - - testenginepreedit(&preedit); - checkstr(t, want, &preedit); + CT_CHECK(t, channbrecv(f->pump.trace, &req) <= 0); } void @@ -556,13 +480,13 @@ xim_adapter_key_contract(struct ct *t) keypress(&state, 'a', 0, &res); nexttrace(t, &f, Keypress, &state); CT_CHECK(t, res.eaten); - checkstr(t, "か", &res.preedit); + checkstr(t, "preedit", "か", &res.preedit); keypress(&state, Kspec|0x57, 0, &res); nexttrace(t, &f, Keypress, &state); CT_CHECK(t, !res.eaten); - checkstr(t, "か", &res.commit); - checkstr(t, "", &res.preedit); + checkstr(t, "preedit", "か", &res.commit); + checkstr(t, "preedit", "", &res.preedit); cleanup: ximend(&f); } @@ -586,7 +510,7 @@ xim_adapter_release_lifecycle(struct ct *t) nexttrace(t, &f, Keypress, &a); keypress(&b, 'n', 0, &res); nexttrace(t, &f, Keypress, &b); - checkstr(t, "ん", &res.preedit); + checkstr(t, "preedit", "ん", &res.preedit); CT_EQ_PTR(t, &b, testengineowner()); /* ResetIC and focus loss use the same idempotent release path. */ @@ -648,6 +572,7 @@ xim_adapter_free_waits_for_release(struct ct *t) Freejob job; Ic *state; Channel *freed; + Keyreq req; Keyres res; uchar token; int workeractive; @@ -665,18 +590,21 @@ xim_adapter_free_waits_for_release(struct ct *t) ics = state; keypress(state, 'n', 0, &res); nexttrace(t, &f, Keypress, state); - f.holdrelease = 1; + /* Hold the release: the free must not complete before the engine + * acknowledges it, since the engine still points at this Ic. */ + pumphold(&f.pump, Keyrelease); job.state = state; job.done = freed; workeractive = threadcreate(freeworker, &job, 8192) >= 0; if(!CT_CHECK(t, workeractive)) goto cleanup; - chanrecv(f.waiting, &token); - nexttrace(t, &f, Keyrelease, state); + chanrecv(f.pump.trace, &req); + CT_EQ_INT(t, Keyrelease, req.op); + CT_EQ_PTR(t, state, req.owner); CT_CHECK(t, channbrecv(freed, &token) <= 0); CT_EQ_PTR(t, state, ics); CT_EQ_PTR(t, state, testengineowner()); - chansend(f.ack, &token); + chansend(f.pump.go, &token); chanrecv(freed, &token); workeractive = 0; state = nil; @@ -684,7 +612,7 @@ xim_adapter_free_waits_for_release(struct ct *t) CT_EQ_PTR(t, nil, testengineowner()); cleanup: if(workeractive){ - chansend(f.ack, &token); + chansend(f.pump.go, &token); chanrecv(freed, &token); state = nil; } @@ -985,7 +913,7 @@ xim_adapter_callback_cleanup(struct ct *t) CT_EQ_INT(t, Wdone, wirecalls[3].op); keypress(&state, 'n', 0, &res); nexttrace(t, &f, Keypress, &state); - checkstr(t, "ん", &res.preedit); + checkstr(t, "preedit", "ん", &res.preedit); release(&state); nexttrace(t, &f, Keyrelease, &state); preowner = nil; @@ -1111,7 +1039,7 @@ xim_adapter_callback_owner_loss(struct ct *t) chansend(keyc, &req); chanrecv(replyc, &res); memset(&trace, 0, sizeof trace); - CT_CHECK(t, channbrecv(f.trace, &trace) > 0); + CT_CHECK(t, channbrecv(f.pump.trace, &trace) > 0); CT_EQ_INT(t, Keypress, trace.op); CT_EQ_PTR(t, &foreign, trace.owner); CT_EQ_PTR(t, &foreign, testengineowner()); @@ -1148,7 +1076,7 @@ xim_adapter_callback_owner_loss(struct ct *t) chansend(keyc, &req); chanrecv(replyc, &res); memset(&trace, 0, sizeof trace); - CT_CHECK(t, channbrecv(f.trace, &trace) > 0); + CT_CHECK(t, channbrecv(f.pump.trace, &trace) > 0); CT_EQ_INT(t, Keyrelease, trace.op); CT_EQ_PTR(t, &foreign, trace.owner); cleanup: