932 lines
22 KiB
C
932 lines
22 KiB
C
#define STRANS_TEST_ENGINE
|
||
#include "../strans.c"
|
||
#include "test.h"
|
||
|
||
static int typekeys(struct ct*, char*, Str*);
|
||
|
||
void
|
||
vietnamese_state_lifetime(struct ct *t)
|
||
{
|
||
Str com;
|
||
|
||
init();
|
||
im.l = &testvi;
|
||
im.pre = mkstr("as");
|
||
im.raw = mkstr("as");
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "committed Telex", "á", &com);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.raw.n);
|
||
|
||
init();
|
||
im.l = &testvi;
|
||
im.pre = mkstr("as");
|
||
im.raw = mkstr("as");
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||
CT_EQ_INT(t, 0, com.n);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.raw.n);
|
||
|
||
init();
|
||
im.l = &testvi;
|
||
im.pre = mkstr("as");
|
||
im.raw = mkstr("as");
|
||
sclear(&com);
|
||
CT_CHECK(t, dotrans('q', &com));
|
||
checkstr(t, "completed Telex", "á", &com);
|
||
checkstr(t, "next preedit", "q", &im.pre);
|
||
checkstr(t, "next raw input", "q", &im.raw);
|
||
}
|
||
|
||
void
|
||
engine_clears_candidates(struct ct *t)
|
||
{
|
||
Str com;
|
||
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
im.pre = mkstr("ㄱ");
|
||
im.nkouho = 2;
|
||
im.sel = 1;
|
||
sclear(&com);
|
||
dotrans('k', &com);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_EQ_INT(t, -1, im.sel);
|
||
}
|
||
|
||
void
|
||
engine_backspace_clears_candidates(struct ct *t)
|
||
{
|
||
Lang *lang;
|
||
Str com, shown;
|
||
|
||
lang = getlang(LangJP);
|
||
init();
|
||
im.l = lang;
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('n', 0, &com));
|
||
CT_CHECK(t, keystroke('a', 0, &com));
|
||
shown = shownpre(&im);
|
||
checkstr(t, "before backspace", "な", &shown);
|
||
im.kouho[0] = mkstr("stale-candidate");
|
||
im.nkouho = 1;
|
||
im.sel = 0;
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
checkstr(t, "backspace commit", "", &com);
|
||
checkstr(t, "pending romaji after backspace", "n", &im.raw);
|
||
shown = shownpre(&im);
|
||
checkstr(t, "shown preedit after backspace", "ん", &shown);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_EQ_INT(t, -1, im.sel);
|
||
}
|
||
|
||
void
|
||
engine_selects_visible_candidate(struct ct *t)
|
||
{
|
||
static const struct { int n, sel; Rune key; char *want; } cases[] = {
|
||
{ Maxdisp, -1, '9', "c9" },
|
||
{ Maxdisp+3, Maxdisp+1, '1', "c3" },
|
||
};
|
||
char name[8];
|
||
Str com;
|
||
int i, j;
|
||
|
||
for(i = 0; i < nelem(cases); i++){
|
||
init();
|
||
for(j = 0; j < cases[i].n; j++){
|
||
snprint(name, sizeof name, "c%d", j+1);
|
||
im.kouho[j] = mkstr(name);
|
||
}
|
||
im.nkouho = cases[i].n;
|
||
im.sel = cases[i].sel;
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(cases[i].key, 0, &com));
|
||
checkstr(t, cases[i].want, cases[i].want, &com);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
}
|
||
}
|
||
|
||
void
|
||
engine_commit_contract(struct ct *t)
|
||
{
|
||
Str com;
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
im.nkouho = 1;
|
||
im.sel = -1;
|
||
sclear(&com);
|
||
if(!typekeys(t, "ka", &com))
|
||
return;
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "return commit", "か", &com);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.raw.n);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_EQ_INT(t, -1, im.sel);
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(!typekeys(t, "ka", &com))
|
||
return;
|
||
im.kouho[0] = mkstr("c1");
|
||
im.kouho[1] = mkstr("c2");
|
||
im.nkouho = 2;
|
||
im.sel = 1;
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "selected candidate", "c2", &com);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_EQ_INT(t, -1, im.sel);
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(!typekeys(t, "ka", &com))
|
||
return;
|
||
im.nkouho = 1;
|
||
im.sel = -1;
|
||
CT_CHECK(t, !keystroke('q', 0, &com));
|
||
checkstr(t, "uneaten-key commit", "か", &com);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.raw.n);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_EQ_INT(t, -1, im.sel);
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(!typekeys(t, "ka", &com))
|
||
return;
|
||
CT_CHECK(t, keystroke(0xf008, 0, &com));
|
||
CT_EQ_INT(t, 2, com.n);
|
||
CT_EQ_UINT(t, 0x304b, com.r[0]);
|
||
CT_EQ_UINT(t, 0xf008, com.r[1]);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
}
|
||
|
||
void
|
||
engine_telex_history_bound(struct ct *t)
|
||
{
|
||
Str com;
|
||
Rune tone;
|
||
int i;
|
||
|
||
init();
|
||
im.l = &testvi;
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('a', 0, &com));
|
||
for(i = 0; i < Maxrunes; i++){
|
||
tone = i % 2 == 0 ? 's' : 'f';
|
||
if(!CT_CHECK(t, keystroke(tone, 0, &com)))
|
||
break;
|
||
}
|
||
checkstr(t, "bounded Telex commit", "à", &com);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.raw.n);
|
||
}
|
||
|
||
void
|
||
engine_korean_modifiers_and_backspace(struct ct *t)
|
||
{
|
||
static const struct {
|
||
Rune key;
|
||
char *plain;
|
||
char *shifted;
|
||
} cases[] = {
|
||
{ 'r', "ㄱ", "ㄲ" }, { 'e', "ㄷ", "ㄸ" },
|
||
{ 'q', "ㅂ", "ㅃ" }, { 't', "ㅅ", "ㅆ" },
|
||
{ 'w', "ㅈ", "ㅉ" }, { 'o', "ㅐ", "ㅒ" },
|
||
{ 'p', "ㅔ", "ㅖ" },
|
||
};
|
||
Str com;
|
||
int i;
|
||
|
||
for(i = 0; i < nelem(cases); i++){
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(cases[i].key, 0, &com));
|
||
checkstr(t, "plain Korean key", cases[i].plain, &im.pre);
|
||
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(cases[i].key, Mshift, &com));
|
||
checkstr(t, "shifted Korean key", cases[i].shifted, &im.pre);
|
||
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(cases[i].key - 'a' + 'A', 0, &com));
|
||
checkstr(t, "Caps Lock Korean key", cases[i].plain, &im.pre);
|
||
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke(cases[i].key, Mshift, &com));
|
||
checkstr(t, "Shift+Caps Korean key", cases[i].shifted, &im.pre);
|
||
}
|
||
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
sclear(&com);
|
||
if(typekeys(t, "rkr", &com)){
|
||
checkstr(t, "Korean engine preedit", "각", &im.pre);
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
checkstr(t, "Korean final backspace", "가", &im.pre);
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
checkstr(t, "Korean vowel backspace", "ㄱ", &im.pre);
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_CHECK(t, !keystroke(Kback, 0, &com));
|
||
}
|
||
}
|
||
|
||
void
|
||
engine_language_switch_state(struct ct *t)
|
||
{
|
||
static const struct {
|
||
char *name;
|
||
Rune key;
|
||
int mod;
|
||
int lang;
|
||
char *pre;
|
||
char *raw;
|
||
char *commit;
|
||
int stale;
|
||
} steps[] = {
|
||
{ "select Vietnamese", 'v', Mctrl, LangVI, "", "", "", 0 },
|
||
{ "Vietnamese a", 'a', 0, LangVI, "a", "a", "", 0 },
|
||
{ "Vietnamese tone", 's', 0, LangVI, "as", "as", "", 0 },
|
||
{ "switch to Korean", 's', Mctrl, LangKO, "", "", "", 1 },
|
||
{ "Korean initial", 'r', 0, LangKO, "ㄱ", "", "", 0 },
|
||
{ "Korean syllable", 'k', 0, LangKO, "가", "", "", 0 },
|
||
{ "switch to Vietnamese", 'v', Mctrl, LangVI, "", "", "", 0 },
|
||
{ "Vietnamese a again", 'a', 0, LangVI, "a", "a", "", 0 },
|
||
{ "Vietnamese tone again", 's', 0, LangVI, "as", "as", "", 0 },
|
||
{ "commit Vietnamese", Kret, 0, LangVI, "", "", "á", 0 },
|
||
};
|
||
char where[80];
|
||
Lang *vi;
|
||
Trie *saved;
|
||
Str com;
|
||
int eaten, i;
|
||
|
||
vi = getlang(LangVI);
|
||
if(!CT_CHECK(t, vi != nil))
|
||
return;
|
||
saved = vi->map;
|
||
vi->map = testvi.map;
|
||
init();
|
||
for(i = 0; i < nelem(steps); i++){
|
||
if(steps[i].stale){
|
||
im.kouho[0] = mkstr("stale");
|
||
im.nkouho = 1;
|
||
im.sel = 0;
|
||
}
|
||
sclear(&com);
|
||
eaten = keystroke(steps[i].key, steps[i].mod, &com);
|
||
if(eaten != 1){
|
||
CT_ERRORF(t, "%s: want eaten 1, got %d",
|
||
steps[i].name, eaten);
|
||
break;
|
||
}
|
||
if(im.l == nil || im.l->lang != steps[i].lang){
|
||
CT_ERRORF(t, "%s: want language %d, got %d",
|
||
steps[i].name, steps[i].lang,
|
||
im.l == nil ? -1 : im.l->lang);
|
||
break;
|
||
}
|
||
snprint(where, sizeof where, "%s preedit", steps[i].name);
|
||
if(!checkstr(t, where, steps[i].pre, &im.pre))
|
||
break;
|
||
snprint(where, sizeof where, "%s raw", steps[i].name);
|
||
if(!checkstr(t, where, steps[i].raw, &im.raw))
|
||
break;
|
||
snprint(where, sizeof where, "%s commit", steps[i].name);
|
||
if(!checkstr(t, where, steps[i].commit, &com))
|
||
break;
|
||
if(im.nkouho != 0 || im.sel != -1){
|
||
CT_ERRORF(t, "%s: want candidates 0 and selection -1, got %d and %d",
|
||
steps[i].name, im.nkouho, im.sel);
|
||
break;
|
||
}
|
||
}
|
||
vi->map = saved;
|
||
}
|
||
|
||
typedef struct Emojifix Emojifix;
|
||
struct Emojifix
|
||
{
|
||
Im im;
|
||
Search search;
|
||
Hmap *dict;
|
||
int popup;
|
||
int visible;
|
||
};
|
||
|
||
static int
|
||
draindraw(Drawcmd *last)
|
||
{
|
||
Drawcmd dc = {0};
|
||
int n;
|
||
|
||
n = 0;
|
||
while(channbrecv(drawc, &dc) > 0){
|
||
n++;
|
||
if(last != nil)
|
||
*last = dc;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
static void
|
||
setemoji(Hmap **dict, char *key, char *val)
|
||
{
|
||
Str s;
|
||
|
||
s = mkstr(key);
|
||
hmapset(dict, &s, val, strlen(val));
|
||
}
|
||
|
||
static void
|
||
emojibegin(Emojifix *f, int showpre)
|
||
{
|
||
Lang *l;
|
||
|
||
l = getlang(LangEMOJI);
|
||
f->im = im;
|
||
f->search = search;
|
||
f->dict = l->dict;
|
||
f->popup = popup;
|
||
f->visible = visible;
|
||
draindraw(nil);
|
||
l->dict = hmapalloc(32);
|
||
setemoji(&l->dict, "a", "A B");
|
||
setemoji(&l->dict, "ㅁ", "B C");
|
||
setemoji(&l->dict, "alpha", "α");
|
||
setemoji(&l->dict, "smil", "😀 😄");
|
||
setemoji(&l->dict, "smile", "😀 😄");
|
||
setemoji(&l->dict, "웃음", "😀 😄 😂");
|
||
setemoji(&l->dict, "^", "¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁽");
|
||
setemoji(&l->dict, "^0", "⁰");
|
||
setemoji(&l->dict, "_", "₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₍");
|
||
setemoji(&l->dict, "<", "← ≤ ♥ ≠");
|
||
setemoji(&l->dict, "many",
|
||
"c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12");
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
popup = showpre;
|
||
}
|
||
|
||
static void
|
||
emojiend(Emojifix *f)
|
||
{
|
||
Lang *l;
|
||
Hmap *dict;
|
||
|
||
l = getlang(LangEMOJI);
|
||
dict = l->dict;
|
||
l->dict = f->dict;
|
||
hmapfree(dict);
|
||
draindraw(nil);
|
||
im = f->im;
|
||
search = f->search;
|
||
popup = f->popup;
|
||
visible = f->visible;
|
||
}
|
||
|
||
static int
|
||
typekeys(struct ct *t, char *keys, Str *com)
|
||
{
|
||
for(; *keys != '\0'; keys++)
|
||
if(!CT_CHECK(t, keystroke((uchar)*keys, 0, com)))
|
||
return 0;
|
||
return 1;
|
||
}
|
||
|
||
void
|
||
engine_japanese_readings(struct ct *t)
|
||
{
|
||
static const struct {
|
||
char *keys;
|
||
char *want;
|
||
} cases[] = {
|
||
{ "nya", "にゃ" },
|
||
{ "n'ya", "んや" },
|
||
{ "nnya", "んにゃ" },
|
||
{ "kin'youbi", "きんようび" },
|
||
};
|
||
Str com, shown;
|
||
int i;
|
||
|
||
for(i = 0; i < nelem(cases); i++){
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(!typekeys(t, cases[i].keys, &com))
|
||
continue;
|
||
checkstr(t, cases[i].keys, "", &com);
|
||
shown = shownpre(&im);
|
||
checkstr(t, cases[i].keys, cases[i].want, &shown);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "committed reading", cases[i].want, &com);
|
||
}
|
||
}
|
||
|
||
static int
|
||
newestrequest(Dictreq *req)
|
||
{
|
||
int n;
|
||
|
||
n = 0;
|
||
while(channbrecv(dictreqc, req) > 0)
|
||
n++;
|
||
return n;
|
||
}
|
||
|
||
void
|
||
engine_japanese_candidates(struct ct *t)
|
||
{
|
||
Dictreq req;
|
||
Dictres res;
|
||
Hmap *saved;
|
||
Lang *jp;
|
||
Str com, key, shown;
|
||
|
||
jp = getlang(LangJP);
|
||
saved = jp->dict;
|
||
jp->dict = hmapalloc(8);
|
||
key = mkstr("かんじ");
|
||
hmapset(&jp->dict, &key, "漢字 幹事", strlen("漢字 幹事"));
|
||
init();
|
||
im.l = jp;
|
||
sclear(&com);
|
||
if(!typekeys(t, "kanji", &com))
|
||
goto cleanup;
|
||
shown = shownpre(&im);
|
||
checkstr(t, "complete Japanese reading", "かんじ", &shown);
|
||
if(!CT_CHECK(t, newestrequest(&req) > 0))
|
||
goto cleanup;
|
||
checkstr(t, "dictionary lookup key", "かんじ", &req.key);
|
||
checkstr(t, "dictionary request identity", "かんじ", &req.pre);
|
||
dictlookup(&req, &res);
|
||
dictresult(&res);
|
||
if(!CT_EQ_INT(t, 2, im.nkouho))
|
||
goto cleanup;
|
||
checkstr(t, "candidate one", "漢字", &im.kouho[0]);
|
||
checkstr(t, "candidate two", "幹事", &im.kouho[1]);
|
||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||
CT_EQ_INT(t, 0, im.sel);
|
||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||
CT_EQ_INT(t, 1, im.sel);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "selected Kanji", "幹事", &com);
|
||
cleanup:
|
||
newestrequest(&req);
|
||
hmapfree(jp->dict);
|
||
jp->dict = saved;
|
||
}
|
||
|
||
void
|
||
engine_japanese_backspace_and_boundaries(struct ct *t)
|
||
{
|
||
Str com, shown;
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(typekeys(t, "kanji", &com)){
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
shown = shownpre(&im);
|
||
checkstr(t, "backspace pending i", "かんj", &shown);
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
shown = shownpre(&im);
|
||
checkstr(t, "backspace pending j", "かん", &shown);
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
shown = shownpre(&im);
|
||
checkstr(t, "backspace accumulated kana", "か", &shown);
|
||
}
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(typekeys(t, "kanji", &com)){
|
||
CT_CHECK(t, keystroke('k', Mctrl, &com));
|
||
checkstr(t, "Hiragana mode boundary", "かんじ", &com);
|
||
CT_EQ_INT(t, LangJPK, im.l->lang);
|
||
CT_EQ_INT(t, 0, im.pre.n);
|
||
CT_EQ_INT(t, 0, im.raw.n);
|
||
sclear(&com);
|
||
if(typekeys(t, "ka", &com)){
|
||
CT_CHECK(t, keystroke('n', Mctrl, &com));
|
||
checkstr(t, "Katakana mode boundary", "カ", &com);
|
||
CT_EQ_INT(t, LangJP, im.l->lang);
|
||
}
|
||
}
|
||
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(typekeys(t, "kanji", &com)){
|
||
CT_CHECK(t, keystroke(' ', 0, &com));
|
||
checkstr(t, "real input boundary", "かんじ ", &com);
|
||
}
|
||
}
|
||
|
||
void
|
||
engine_katakana_sequences(struct ct *t)
|
||
{
|
||
static const struct {
|
||
char *keys;
|
||
char *want;
|
||
} cases[] = {
|
||
{ "kitte", "キッテ" },
|
||
{ "katta", "カッタ" },
|
||
{ "gakkou", "ガッコウ" },
|
||
{ "matcha", "マッチャ" },
|
||
{ "xu", "ゥ" },
|
||
{ "vu", "ヴ" },
|
||
};
|
||
Dictreq req;
|
||
Str com, shown;
|
||
int i;
|
||
|
||
newestrequest(&req);
|
||
for(i = 0; i < nelem(cases); i++){
|
||
init();
|
||
im.l = getlang(LangJPK);
|
||
sclear(&com);
|
||
if(!typekeys(t, cases[i].keys, &com))
|
||
continue;
|
||
checkstr(t, cases[i].keys, "", &com);
|
||
shown = shownpre(&im);
|
||
checkstr(t, cases[i].keys, cases[i].want, &shown);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "committed Katakana", cases[i].want, &com);
|
||
}
|
||
CT_EQ_INT(t, 0, newestrequest(&req));
|
||
CT_EQ_PTR(t, nil, getlang(LangJPK)->dictname);
|
||
}
|
||
|
||
void
|
||
engine_emoji_single_candidate(struct ct *t)
|
||
{
|
||
Drawcmd dc = {0};
|
||
Emojifix f;
|
||
Str com;
|
||
|
||
emojibegin(&f, 1);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('p', Mctrl, &com));
|
||
CT_EQ_INT(t, 0, popup);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
CT_CHECK(t, search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
if(typekeys(t, "alpha", &com)){
|
||
checkstr(t, "no automatic commit", "", &com);
|
||
checkstr(t, "raw English query", "alpha", &search.text);
|
||
CT_CHECK(t, search.on);
|
||
CT_EQ_INT(t, 1, im.nkouho);
|
||
checkstr(t, "single candidate", "α", &im.kouho[0]);
|
||
CT_CHECK(t, draindraw(&dc) > 0);
|
||
CT_EQ_INT(t, 0, dc.pre.n);
|
||
CT_EQ_INT(t, 1, dc.nkouho);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "explicit candidate commit", "α", &com);
|
||
CT_CHECK(t, !search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
show();
|
||
CT_CHECK(t, draindraw(&dc) > 0);
|
||
CT_EQ_INT(t, 0, dc.pre.n);
|
||
CT_EQ_INT(t, 0, dc.nkouho);
|
||
}
|
||
emojiend(&f);
|
||
}
|
||
|
||
void
|
||
engine_emoji_queries(struct ct *t)
|
||
{
|
||
Drawcmd dc = {0};
|
||
Emojifix f;
|
||
Str com;
|
||
|
||
emojibegin(&f, 1);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
CT_CHECK(t, keystroke('a', 0, &com));
|
||
checkstr(t, "raw query wins", "a", &search.text);
|
||
CT_EQ_INT(t, 3, im.nkouho);
|
||
checkstr(t, "raw candidate", "A", &im.kouho[0]);
|
||
checkstr(t, "deduplicated candidate", "B", &im.kouho[1]);
|
||
checkstr(t, "local candidate", "C", &im.kouho[2]);
|
||
CT_CHECK(t, draindraw(&dc) > 0);
|
||
checkstr(t, "prefix popup query", "a", &dc.pre);
|
||
CT_EQ_INT(t, 3, dc.nkouho);
|
||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "smile", &com)){
|
||
checkstr(t, "English alias", "smile", &search.text);
|
||
CT_EQ_INT(t, 2, im.nkouho);
|
||
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||
checkstr(t, "backspaced query", "smil", &search.text);
|
||
CT_EQ_INT(t, 2, im.nkouho);
|
||
CT_CHECK(t, keystroke('e', 0, &com));
|
||
CT_CHECK(t, keystroke('2', 0, &com));
|
||
checkstr(t, "selected English alias", "😄", &com);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "SMILE", &com)){
|
||
checkstr(t, "folded English alias", "smile", &search.text);
|
||
CT_EQ_INT(t, 2, im.nkouho);
|
||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "dntdma", &com)){
|
||
checkstr(t, "localized Korean query", "웃음", &search.text);
|
||
CT_EQ_INT(t, 3, im.nkouho);
|
||
CT_CHECK(t, draindraw(&dc) > 0);
|
||
checkstr(t, "popup query", "웃음", &dc.pre);
|
||
CT_EQ_INT(t, 3, dc.nkouho);
|
||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||
checkstr(t, "cancel commit", "", &com);
|
||
CT_CHECK(t, !search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
}
|
||
emojiend(&f);
|
||
}
|
||
|
||
void
|
||
engine_emoji_digit_aliases(struct ct *t)
|
||
{
|
||
static const struct {
|
||
char *name;
|
||
Rune prefix;
|
||
Rune digit;
|
||
char *want;
|
||
} cases[] = {
|
||
{ "^1", '^', '1', "¹" },
|
||
{ "_2", '_', '2', "₂" },
|
||
{ "<3", '<', '3', "♥" },
|
||
};
|
||
Emojifix f;
|
||
Str com;
|
||
int i;
|
||
|
||
emojibegin(&f, 0);
|
||
for(i = 0; i < nelem(cases); i++){
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
CT_CHECK(t, keystroke(cases[i].prefix, Mshift, &com));
|
||
CT_CHECK(t, im.nkouho >= cases[i].digit - '0');
|
||
CT_CHECK(t, keystroke(cases[i].digit, 0, &com));
|
||
checkstr(t, cases[i].name, cases[i].want, &com);
|
||
CT_CHECK(t, !search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
CT_CHECK(t, keystroke('^', Mshift, &com));
|
||
CT_CHECK(t, keystroke('0', 0, &com));
|
||
checkstr(t, "zero query", "^0", &search.text);
|
||
checkstr(t, "zero does not commit", "", &com);
|
||
CT_EQ_INT(t, 1, im.nkouho);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "zero alias", "⁰", &com);
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "smile", &com)){
|
||
CT_CHECK(t, keystroke('9', 0, &com));
|
||
checkstr(t, "missing number query", "smile9", &search.text);
|
||
checkstr(t, "missing number commit", "", &com);
|
||
CT_CHECK(t, search.on);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "missing number text", "smile9", &com);
|
||
}
|
||
emojiend(&f);
|
||
}
|
||
|
||
void
|
||
engine_emoji_navigation(struct ct *t)
|
||
{
|
||
Drawcmd dc = {0};
|
||
Emojifix f;
|
||
Str com;
|
||
int i;
|
||
|
||
emojibegin(&f, 0);
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "many", &com)){
|
||
CT_EQ_INT(t, 12, im.nkouho);
|
||
for(i = 0; i < Maxdisp+1; i++)
|
||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||
CT_EQ_INT(t, Maxdisp, im.sel);
|
||
CT_CHECK(t, draindraw(&dc) > 0);
|
||
checkstr(t, "scrolled first row", "c2", &dc.kouho[0]);
|
||
checkstr(t, "scrolled ninth row", "c10", &dc.kouho[8]);
|
||
CT_EQ_INT(t, 8, dc.sel);
|
||
CT_CHECK(t, keystroke('1', 0, &com));
|
||
checkstr(t, "scrolled row one", "c2", &com);
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "many", &com)){
|
||
for(i = 0; i < 12; i++)
|
||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||
CT_EQ_INT(t, 11, im.sel);
|
||
CT_CHECK(t, keystroke('9', 0, &com));
|
||
checkstr(t, "scrolled row nine", "c12", &com);
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "smile", &com)){
|
||
CT_CHECK(t, keystroke(Ktab, 0, &com));
|
||
CT_EQ_INT(t, 0, im.sel);
|
||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||
CT_EQ_INT(t, 1, im.sel);
|
||
CT_CHECK(t, keystroke(Kup, 0, &com));
|
||
CT_EQ_INT(t, 0, im.sel);
|
||
CT_CHECK(t, keystroke(Ktab, Mshift, &com));
|
||
CT_EQ_INT(t, 1, im.sel);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "navigated candidate", "😄", &com);
|
||
}
|
||
emojiend(&f);
|
||
}
|
||
|
||
void
|
||
engine_emoji_preedit_languages(struct ct *t)
|
||
{
|
||
Emojifix f;
|
||
Str com, shown;
|
||
int p;
|
||
|
||
emojibegin(&f, 0);
|
||
init();
|
||
im.l = getlang(LangJP);
|
||
sclear(&com);
|
||
if(typekeys(t, "ka", &com)){
|
||
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
|
||
shown = shownpre(&im);
|
||
checkstr(t, "modifier preserves Japanese", "か", &shown);
|
||
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
|
||
checkstr(t, "committed Japanese preedit", "か", &com);
|
||
CT_CHECK(t, search.on);
|
||
CT_EQ_INT(t, LangJP, im.l->lang);
|
||
CT_CHECK(t, keystroke('a', 0, &com));
|
||
p = popup;
|
||
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
|
||
CT_CHECK(t, search.on);
|
||
checkstr(t, "modifier preserves search", "a", &search.text);
|
||
CT_CHECK(t, keystroke('P', Mctrl|Mshift, &com));
|
||
CT_EQ_INT(t, !p, popup);
|
||
CT_CHECK(t, search.on);
|
||
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
|
||
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
|
||
CT_CHECK(t, !search.on);
|
||
sclear(&com);
|
||
if(typekeys(t, "na", &com)){
|
||
shown = shownpre(&im);
|
||
checkstr(t, "Japanese resumed", "な", &shown);
|
||
}
|
||
}
|
||
|
||
init();
|
||
im.l = getlang(LangKO);
|
||
sclear(&com);
|
||
if(typekeys(t, "rk", &com)){
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
checkstr(t, "committed Korean preedit", "가", &com);
|
||
if(typekeys(t, "smile", &com)){
|
||
CT_CHECK(t, keystroke('1', 0, &com));
|
||
checkstr(t, "Korean selection", "가😀", &com);
|
||
sclear(&com);
|
||
if(typekeys(t, "rk", &com))
|
||
checkstr(t, "Korean resumed", "가", &im.pre);
|
||
}
|
||
}
|
||
|
||
init();
|
||
im.l = &testvi;
|
||
sclear(&com);
|
||
if(typekeys(t, "as", &com)){
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
checkstr(t, "committed Telex preedit", "á", &com);
|
||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||
CT_CHECK(t, !search.on);
|
||
CT_EQ_PTR(t, &testvi, im.l);
|
||
sclear(&com);
|
||
if(typekeys(t, "as", &com)){
|
||
shown = shownpre(&im);
|
||
checkstr(t, "Telex resumed", "á", &shown);
|
||
checkstr(t, "Telex raw resumed", "as", &im.raw);
|
||
}
|
||
}
|
||
emojiend(&f);
|
||
}
|
||
|
||
void
|
||
engine_emoji_start_and_unknown(struct ct *t)
|
||
{
|
||
Emojifix f;
|
||
Str com;
|
||
char want[Maxrunes+1];
|
||
int i;
|
||
|
||
emojibegin(&f, 0);
|
||
sclear(&com);
|
||
if(typekeys(t, "dkssud", &com)){
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
checkstr(t, "committed Korean preedit", "안녕", &com);
|
||
CT_CHECK(t, search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
if(typekeys(t, "xyz", &com)){
|
||
checkstr(t, "unknown displayed query", "xyz", &search.text);
|
||
CT_EQ_INT(t, 0, im.nkouho);
|
||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||
checkstr(t, "unknown query commit", "안녕xyz", &com);
|
||
CT_CHECK(t, !search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
}
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
if(typekeys(t, "xyz", &com)){
|
||
CT_CHECK(t, !keystroke(Kspec|0x51, 0, &com));
|
||
checkstr(t, "special commits shown query", "xyz", &com);
|
||
CT_CHECK(t, !search.on);
|
||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||
}
|
||
|
||
sclear(&com);
|
||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||
for(i = 0; i < Maxrunes; i++)
|
||
CT_CHECK(t, keystroke('q', 0, &com));
|
||
CT_EQ_INT(t, Maxrunes, search.raw.n);
|
||
CT_CHECK(t, search.on);
|
||
CT_CHECK(t, !keystroke('z', 0, &com));
|
||
memset(want, 'q', Maxrunes);
|
||
want[Maxrunes] = '\0';
|
||
checkstr(t, "bounded query commit", want, &com);
|
||
CT_CHECK(t, !search.on);
|
||
emojiend(&f);
|
||
}
|
||
|
||
void
|
||
engine_emoji_dictionary_identity(struct ct *t)
|
||
{
|
||
Dictres res;
|
||
Emojifix f;
|
||
|
||
emojibegin(&f, 0);
|
||
im.pre = mkstr("same");
|
||
im.kouho[0] = mkstr("old");
|
||
im.nkouho = 1;
|
||
im.sel = 0;
|
||
memset(&res, 0, sizeof res);
|
||
res.key = im.pre;
|
||
res.kouho[0] = mkstr("wrong");
|
||
res.nkouho = 1;
|
||
res.lang = LangJP;
|
||
dictresult(&res);
|
||
CT_EQ_INT(t, 1, im.nkouho);
|
||
checkstr(t, "wrong language ignored", "old", &im.kouho[0]);
|
||
|
||
res.lang = LangKO;
|
||
res.key = mkstr("other");
|
||
dictresult(&res);
|
||
CT_EQ_INT(t, 1, im.nkouho);
|
||
checkstr(t, "wrong preedit ignored", "old", &im.kouho[0]);
|
||
|
||
res.key = im.pre;
|
||
search.on = 1;
|
||
dictresult(&res);
|
||
CT_EQ_INT(t, 1, im.nkouho);
|
||
checkstr(t, "search response ignored", "old", &im.kouho[0]);
|
||
|
||
search.on = 0;
|
||
res.kouho[0] = mkstr("right");
|
||
dictresult(&res);
|
||
CT_EQ_INT(t, 1, im.nkouho);
|
||
CT_EQ_INT(t, -1, im.sel);
|
||
checkstr(t, "matching response accepted", "right", &im.kouho[0]);
|
||
emojiend(&f);
|
||
}
|