engine: reject stale dictionary results
This commit is contained in:
2
dat.h
2
dat.h
@@ -172,6 +172,7 @@ struct Dictreq
|
|||||||
Str key;
|
Str key;
|
||||||
Str pre;
|
Str pre;
|
||||||
int lang;
|
int lang;
|
||||||
|
u32int seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Dictres Dictres;
|
typedef struct Dictres Dictres;
|
||||||
@@ -181,6 +182,7 @@ struct Dictres
|
|||||||
Str kouho[Maxkouho];
|
Str kouho[Maxkouho];
|
||||||
int nkouho;
|
int nkouho;
|
||||||
int lang;
|
int lang;
|
||||||
|
u32int seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Lang langs[];
|
extern Lang langs[];
|
||||||
|
|||||||
1
dict.c
1
dict.c
@@ -13,6 +13,7 @@ dictlookup(Dictreq *req, Dictres *res)
|
|||||||
res->key = req->pre;
|
res->key = req->pre;
|
||||||
res->nkouho = 0;
|
res->nkouho = 0;
|
||||||
res->lang = req->lang;
|
res->lang = req->lang;
|
||||||
|
res->seq = req->seq;
|
||||||
if(req->key.n == 0)
|
if(req->key.n == 0)
|
||||||
return;
|
return;
|
||||||
l = getlang(req->lang);
|
l = getlang(req->lang);
|
||||||
|
|||||||
7
strans.c
7
strans.c
@@ -6,6 +6,7 @@ static int visible = 0;
|
|||||||
static void *activeowner;
|
static void *activeowner;
|
||||||
static int activecap;
|
static int activecap;
|
||||||
static Caret caret;
|
static Caret caret;
|
||||||
|
static u32int dictseq;
|
||||||
static void dictqmap(Im*);
|
static void dictqmap(Im*);
|
||||||
static void dictqjp(Im*);
|
static void dictqjp(Im*);
|
||||||
static void backjp(Im*);
|
static void backjp(Im*);
|
||||||
@@ -34,6 +35,7 @@ int nlang = nelem(langs);
|
|||||||
static void
|
static void
|
||||||
clearkouho(void)
|
clearkouho(void)
|
||||||
{
|
{
|
||||||
|
dictseq++;
|
||||||
im.nkouho = 0;
|
im.nkouho = 0;
|
||||||
im.sel = -1;
|
im.sel = -1;
|
||||||
}
|
}
|
||||||
@@ -147,6 +149,7 @@ dictsend(Im *im, Str *key)
|
|||||||
|
|
||||||
req.key = *key;
|
req.key = *key;
|
||||||
req.lang = im->l->lang;
|
req.lang = im->l->lang;
|
||||||
|
req.seq = dictseq;
|
||||||
if(isjp(im))
|
if(isjp(im))
|
||||||
jpreading(im, &req.pre);
|
jpreading(im, &req.pre);
|
||||||
else
|
else
|
||||||
@@ -453,6 +456,7 @@ searchlookup(int lang, Str *key, Dictres *res)
|
|||||||
req.key = *key;
|
req.key = *key;
|
||||||
req.pre = req.key;
|
req.pre = req.key;
|
||||||
req.lang = lang;
|
req.lang = lang;
|
||||||
|
req.seq = 0;
|
||||||
dictlookup(&req, res);
|
dictlookup(&req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,6 +773,7 @@ init(void)
|
|||||||
memset(&caret, 0, sizeof caret);
|
memset(&caret, 0, sizeof caret);
|
||||||
activeowner = nil;
|
activeowner = nil;
|
||||||
activecap = 0;
|
activecap = 0;
|
||||||
|
dictseq = 0;
|
||||||
visible = 0;
|
visible = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,7 +838,7 @@ dictresult(Dictres *res)
|
|||||||
Str pre;
|
Str pre;
|
||||||
|
|
||||||
impre(&pre);
|
impre(&pre);
|
||||||
if(search.lang || res->lang != im.l->lang ||
|
if(search.lang || res->seq != dictseq || res->lang != im.l->lang ||
|
||||||
scmp(&res->key, &pre) != 0)
|
scmp(&res->key, &pre) != 0)
|
||||||
return;
|
return;
|
||||||
setkouho(res);
|
setkouho(res);
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ dictionary_candidates(struct ct *t)
|
|||||||
req.key = key;
|
req.key = key;
|
||||||
req.pre = mkstr("preedit-one");
|
req.pre = mkstr("preedit-one");
|
||||||
req.lang = LangJP;
|
req.lang = LangJP;
|
||||||
|
req.seq = 17;
|
||||||
dictlookup(&req, &res);
|
dictlookup(&req, &res);
|
||||||
if(!CT_EQ_INT(t, 2, res.nkouho))
|
if(!CT_EQ_INT(t, 2, res.nkouho))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
CT_EQ_INT(t, LangJP, res.lang);
|
CT_EQ_INT(t, LangJP, res.lang);
|
||||||
|
CT_EQ_UINT(t, 17, res.seq);
|
||||||
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
|
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
|
||||||
checkstr(t, "candidate 1", "候補1", &res.kouho[0]);
|
checkstr(t, "candidate 1", "候補1", &res.kouho[0]);
|
||||||
checkstr(t, "candidate 2", "候補2", &res.kouho[1]);
|
checkstr(t, "candidate 2", "候補2", &res.kouho[1]);
|
||||||
@@ -42,10 +44,12 @@ dictionary_candidates(struct ct *t)
|
|||||||
hmapset(&lang->dict, &key, many, strlen(many));
|
hmapset(&lang->dict, &key, many, strlen(many));
|
||||||
req.key = key;
|
req.key = key;
|
||||||
req.pre = mkstr("preedit-two");
|
req.pre = mkstr("preedit-two");
|
||||||
|
req.seq = 29;
|
||||||
dictlookup(&req, &res);
|
dictlookup(&req, &res);
|
||||||
if(!CT_EQ_INT(t, Maxkouho, res.nkouho))
|
if(!CT_EQ_INT(t, Maxkouho, res.nkouho))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
CT_EQ_INT(t, LangJP, res.lang);
|
CT_EQ_INT(t, LangJP, res.lang);
|
||||||
|
CT_EQ_UINT(t, 29, res.seq);
|
||||||
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
|
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
|
||||||
checkstr(t, "first capped candidate", "c00", &res.kouho[0]);
|
checkstr(t, "first capped candidate", "c00", &res.kouho[0]);
|
||||||
checkstr(t, "last capped candidate", "c31", &res.kouho[31]);
|
checkstr(t, "last capped candidate", "c31", &res.kouho[31]);
|
||||||
@@ -79,11 +83,13 @@ dictionary_misses_clear_result(struct ct *t)
|
|||||||
req.key = mkstr(cases[i].key);
|
req.key = mkstr(cases[i].key);
|
||||||
req.pre = mkstr(cases[i].pre);
|
req.pre = mkstr(cases[i].pre);
|
||||||
req.lang = LangJP;
|
req.lang = LangJP;
|
||||||
|
req.seq = 0xf00d0000U + i;
|
||||||
dictlookup(&req, &res);
|
dictlookup(&req, &res);
|
||||||
if(res.nkouho != 0)
|
if(res.nkouho != 0)
|
||||||
CT_ERRORF(t, "%s: want 0 candidates, got %d",
|
CT_ERRORF(t, "%s: want 0 candidates, got %d",
|
||||||
cases[i].name, res.nkouho);
|
cases[i].name, res.nkouho);
|
||||||
CT_EQ_INT(t, LangJP, res.lang);
|
CT_EQ_INT(t, LangJP, res.lang);
|
||||||
|
CT_EQ_UINT(t, 0xf00d0000U + i, res.seq);
|
||||||
checkstr(t, cases[i].name, cases[i].pre, &res.key);
|
checkstr(t, cases[i].name, cases[i].pre, &res.key);
|
||||||
}
|
}
|
||||||
hmapfree(lang->dict);
|
hmapfree(lang->dict);
|
||||||
@@ -108,6 +114,7 @@ dictionary_emoji_identity(struct ct *t)
|
|||||||
req.key = key;
|
req.key = key;
|
||||||
req.pre = key;
|
req.pre = key;
|
||||||
req.lang = LangEMOJI;
|
req.lang = LangEMOJI;
|
||||||
|
req.seq = 0;
|
||||||
dictlookup(&req, &res);
|
dictlookup(&req, &res);
|
||||||
if(CT_EQ_INT(t, 1, res.nkouho))
|
if(CT_EQ_INT(t, 1, res.nkouho))
|
||||||
checkstr(t, "emoji identity candidate", "é", &res.kouho[0]);
|
checkstr(t, "emoji identity candidate", "é", &res.kouho[0]);
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ engine_dictionary_queue_latest_wins(struct ct *t)
|
|||||||
old.key = mkstr("old-key");
|
old.key = mkstr("old-key");
|
||||||
old.pre = mkstr("old-preedit");
|
old.pre = mkstr("old-preedit");
|
||||||
old.lang = LangJP;
|
old.lang = LangJP;
|
||||||
|
old.seq = 0;
|
||||||
CT_CHECK(t, channbsend(dictreqc, &old) > 0);
|
CT_CHECK(t, channbsend(dictreqc, &old) > 0);
|
||||||
init();
|
init();
|
||||||
im.l = getlang(LangJP);
|
im.l = getlang(LangJP);
|
||||||
@@ -185,6 +186,7 @@ engine_dictionary_queue_latest_wins(struct ct *t)
|
|||||||
checkstr(t, "newest dictionary key", "new-key", &got.key);
|
checkstr(t, "newest dictionary key", "new-key", &got.key);
|
||||||
checkstr(t, "newest dictionary preedit", "new-preedit", &got.pre);
|
checkstr(t, "newest dictionary preedit", "new-preedit", &got.pre);
|
||||||
CT_EQ_INT(t, LangJP, got.lang);
|
CT_EQ_INT(t, LangJP, got.lang);
|
||||||
|
CT_EQ_UINT(t, dictseq, got.seq);
|
||||||
}
|
}
|
||||||
CT_CHECK(t, channbrecv(dictreqc, &got) <= 0);
|
CT_CHECK(t, channbrecv(dictreqc, &got) <= 0);
|
||||||
chanfree(dictreqc);
|
chanfree(dictreqc);
|
||||||
@@ -301,6 +303,66 @@ engine_active_owner_reset(struct ct *t)
|
|||||||
checkstr(t, "same owner after reset", "ん", &res.preedit);
|
checkstr(t, "same owner after reset", "ん", &res.preedit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_rejects_stale_dictionary_results(struct ct *t)
|
||||||
|
{
|
||||||
|
Dictreq a, b;
|
||||||
|
Dictres res;
|
||||||
|
Hmap *saved;
|
||||||
|
Lang *jp;
|
||||||
|
char one, two;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
jp = getlang(LangJP);
|
||||||
|
saved = jp->dict;
|
||||||
|
jp->dict = hmapalloc(1);
|
||||||
|
init();
|
||||||
|
im.l = jp;
|
||||||
|
while(channbrecv(dictreqc, &a) > 0)
|
||||||
|
;
|
||||||
|
ownerrequest(&one, Keypress, 'k', 0);
|
||||||
|
ownerrequest(&one, Keypress, 'a', 0);
|
||||||
|
n = 0;
|
||||||
|
while(channbrecv(dictreqc, &a) > 0)
|
||||||
|
n++;
|
||||||
|
if(!CT_CHECK(t, n > 0))
|
||||||
|
goto cleanup;
|
||||||
|
ownerrequest(&one, Keyreset, 0, 0);
|
||||||
|
ownerrequest(&two, Keypress, 'k', 0);
|
||||||
|
ownerrequest(&two, Keypress, 'a', 0);
|
||||||
|
n = 0;
|
||||||
|
while(channbrecv(dictreqc, &b) > 0)
|
||||||
|
n++;
|
||||||
|
if(!CT_CHECK(t, n > 0))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 0, scmp(&a.key, &b.key));
|
||||||
|
CT_EQ_INT(t, 0, scmp(&a.pre, &b.pre));
|
||||||
|
CT_EQ_INT(t, a.lang, b.lang);
|
||||||
|
CT_CHECK(t, a.seq != b.seq);
|
||||||
|
|
||||||
|
memset(&res, 0, sizeof res);
|
||||||
|
res.key = a.pre;
|
||||||
|
res.lang = a.lang;
|
||||||
|
res.seq = a.seq;
|
||||||
|
res.kouho[0] = mkstr("stale");
|
||||||
|
res.nkouho = 1;
|
||||||
|
dictresult(&res);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
|
||||||
|
res.key = b.pre;
|
||||||
|
res.lang = b.lang;
|
||||||
|
res.seq = b.seq;
|
||||||
|
res.kouho[0] = mkstr("current");
|
||||||
|
dictresult(&res);
|
||||||
|
if(CT_EQ_INT(t, 1, im.nkouho))
|
||||||
|
checkstr(t, "current dictionary result", "current", &im.kouho[0]);
|
||||||
|
cleanup:
|
||||||
|
while(channbrecv(dictreqc, &a) > 0)
|
||||||
|
;
|
||||||
|
hmapfree(jp->dict);
|
||||||
|
jp->dict = saved;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
engine_active_owner_caret(struct ct *t)
|
engine_active_owner_caret(struct ct *t)
|
||||||
{
|
{
|
||||||
@@ -676,6 +738,7 @@ engine_popup_preedit_capability(struct ct *t)
|
|||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
res.lang = LangJP;
|
res.lang = LangJP;
|
||||||
res.key = mkstr("か");
|
res.key = mkstr("か");
|
||||||
|
res.seq = dictseq;
|
||||||
res.kouho[0] = mkstr("家");
|
res.kouho[0] = mkstr("家");
|
||||||
res.nkouho = 1;
|
res.nkouho = 1;
|
||||||
dictresult(&res);
|
dictresult(&res);
|
||||||
@@ -693,6 +756,7 @@ engine_popup_preedit_capability(struct ct *t)
|
|||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
res.lang = LangJP;
|
res.lang = LangJP;
|
||||||
res.key = mkstr("ん");
|
res.key = mkstr("ん");
|
||||||
|
res.seq = dictseq;
|
||||||
res.kouho[0] = mkstr("ン");
|
res.kouho[0] = mkstr("ン");
|
||||||
res.nkouho = 1;
|
res.nkouho = 1;
|
||||||
dictresult(&res);
|
dictresult(&res);
|
||||||
@@ -1267,6 +1331,7 @@ engine_emoji_dictionary_identity(struct ct *t)
|
|||||||
im.sel = 0;
|
im.sel = 0;
|
||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
res.key = im.pre;
|
res.key = im.pre;
|
||||||
|
res.seq = dictseq;
|
||||||
res.kouho[0] = mkstr("wrong");
|
res.kouho[0] = mkstr("wrong");
|
||||||
res.nkouho = 1;
|
res.nkouho = 1;
|
||||||
res.lang = LangJP;
|
res.lang = LangJP;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ void engine_candidate_shortcut_modifiers(struct ct*);
|
|||||||
void engine_dictionary_queue_latest_wins(struct ct*);
|
void engine_dictionary_queue_latest_wins(struct ct*);
|
||||||
void engine_active_owner_lifecycle(struct ct*);
|
void engine_active_owner_lifecycle(struct ct*);
|
||||||
void engine_active_owner_reset(struct ct*);
|
void engine_active_owner_reset(struct ct*);
|
||||||
|
void engine_rejects_stale_dictionary_results(struct ct*);
|
||||||
void engine_active_owner_caret(struct ct*);
|
void engine_active_owner_caret(struct ct*);
|
||||||
void engine_popup_preedit_capability(struct ct*);
|
void engine_popup_preedit_capability(struct ct*);
|
||||||
void engine_commit_contract(struct ct*);
|
void engine_commit_contract(struct ct*);
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ static const struct ct_test tests[] = {
|
|||||||
{ "engine/dictionary-queue-latest", engine_dictionary_queue_latest_wins },
|
{ "engine/dictionary-queue-latest", engine_dictionary_queue_latest_wins },
|
||||||
{ "engine/active-owner-lifecycle", engine_active_owner_lifecycle },
|
{ "engine/active-owner-lifecycle", engine_active_owner_lifecycle },
|
||||||
{ "engine/active-owner-reset", engine_active_owner_reset },
|
{ "engine/active-owner-reset", engine_active_owner_reset },
|
||||||
|
{ "engine/rejects-stale-dictionary", engine_rejects_stale_dictionary_results },
|
||||||
{ "engine/active-owner-caret", engine_active_owner_caret },
|
{ "engine/active-owner-caret", engine_active_owner_caret },
|
||||||
{ "engine/popup-preedit-capability", engine_popup_preedit_capability },
|
{ "engine/popup-preedit-capability", engine_popup_preedit_capability },
|
||||||
{ "engine/commit-contract", engine_commit_contract },
|
{ "engine/commit-contract", engine_commit_contract },
|
||||||
|
|||||||
Reference in New Issue
Block a user