Files
strans/tests/popup_test.c
Hojun-Cho 795f11be22 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.
2026-08-16 16:30:32 +09:00

347 lines
9.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "dat.h"
#include "fn.h"
#include "test.h"
enum {
Rgbmask = 0xffffff,
};
static u32int
pixel(u32int *img, Popup *p, int x, int y)
{
return img[y*p->w + x] & Rgbmask;
}
static int
rectcolor(u32int *img, Popup *p, int x0, int y0, int x1, int y1,
u32int color)
{
int x, y;
color &= Rgbmask;
for(y = y0; y < y1; y++)
for(x = x0; x < x1; x++)
if(pixel(img, p, x, y) != color)
return 0;
return 1;
}
static void
checkpadding(struct ct *t, u32int *img, Popup *p)
{
CT_CHECK(t, rectcolor(img, p, 0, 0, p->w, PopupPad, Colbg));
CT_CHECK(t, rectcolor(img, p, 0, p->h - PopupPad,
p->w, p->h, Colbg));
CT_CHECK(t, rectcolor(img, p, 0, 0, PopupPad, p->h, Colbg));
CT_CHECK(t, rectcolor(img, p, p->w - PopupPad, 0,
p->w, p->h, Colbg));
}
void
popup_layout(struct ct *t)
{
static int tinyheight[] = {
1, PopupPad, 2*PopupPad + Fontsz - 1,
};
static const struct {
int first;
int shown;
int total;
char *want;
} markers[] = {
{ 0, 0, 0, "" },
{ 0, 9, 9, "" },
{ 0, 3, 9, "1-3/9" },
{ 8, 1, 9, "9-9/9" },
{ 0, 9, 10, "1-9/10" },
{ 9, 1, 10, "10-10/10" },
{ 0, 9, 18, "1-9/18" },
{ 9, 9, 18, "10-18/18" },
{ 9, 9, 32, "10-18/32" },
{ 18, 9, 32, "19-27/32" },
{ 27, 5, 32, "28-32/32" },
};
char buf[32];
Area area, mon[2], out, work;
Caret caret;
Drawcmd dc;
Popup p;
Str longrow;
u32int c, *img;
int i, ink, markink, n, numink, selbg, textink, x, y;
for(i = 0; i < nelem(markers); i++){
n = pagemarker(buf, sizeof buf, markers[i].first,
markers[i].shown, markers[i].total);
CT_EQ_INT(t, strlen(markers[i].want), n);
CT_CHECK(t, strcmp(markers[i].want, buf) == 0);
}
/* Monitor selection retains origins and intersects the EWMH work area. */
mon[0] = (Area){0, 0, 1920, 1080};
mon[1] = (Area){1920, 200, 1280, 1024};
work = (Area){0, 30, 3200, 1070};
popuparea(mon, 2, &work, 2500, 500, &out);
CT_EQ_INT(t, 1920, out.x);
CT_EQ_INT(t, 200, out.y);
CT_EQ_INT(t, 1280, out.w);
CT_EQ_INT(t, 900, out.h);
work = (Area){60, 0, 3140, 1224};
popuparea(mon, 2, &work, 100, 100, &out);
CT_EQ_INT(t, 60, out.x);
CT_EQ_INT(t, 0, out.y);
CT_EQ_INT(t, 1860, out.w);
CT_EQ_INT(t, 1080, out.h);
work = (Area){5000, 0, 100, 100};
popuparea(mon, 2, &work, 2500, 500, &out);
CT_EQ_INT(t, mon[1].x, out.x);
CT_EQ_INT(t, mon[1].y, out.y);
CT_EQ_INT(t, mon[1].w, out.w);
CT_EQ_INT(t, mon[1].h, out.h);
mon[0] = (Area){0, 0, 100, 100};
mon[1] = (Area){200, 0, 100, 100};
popuparea(mon, 2, nil, 150, 50, &out);
CT_EQ_INT(t, 0, out.x);
memset(&caret, 0, sizeof caret);
area = (Area){1920, 30, 1280, 900};
popupposition(&caret, 2000, 50, &area, 100, 60, &x, &y);
CT_EQ_INT(t, 2010, x);
CT_EQ_INT(t, 60, y);
caret.valid = 1;
caret.x = 2100;
caret.y = 80;
caret.h = 20;
popupposition(&caret, 0, 0, &area, 100, 60, &x, &y);
CT_EQ_INT(t, 2100, x);
CT_EQ_INT(t, 100, y);
caret.x = 3150;
caret.y = 850;
popupposition(&caret, 0, 0, &area, 200, 100, &x, &y);
CT_EQ_INT(t, 3000, x);
CT_EQ_INT(t, 750, y);
/* A popup no larger than a small work area stays inside its origin. */
area = (Area){100, 200, 80, 80};
caret.x = 120;
caret.y = 230;
caret.h = 20;
popupposition(&caret, 0, 0, &area, 80, 80, &x, &y);
CT_EQ_INT(t, 100, x);
CT_EQ_INT(t, 200, y);
if(!CT_CHECK(t, textinit()))
return;
/* Empty sections reserve nothing. */
memset(&dc, 0, sizeof dc);
dc.sel = -1;
popuplayout(&dc, PopupBasew, Imgh, &p);
CT_EQ_INT(t, 0, p.w);
CT_EQ_INT(t, 0, p.h);
CT_EQ_INT(t, -1, p.prey);
CT_EQ_INT(t, -1, p.rowsy);
CT_EQ_INT(t, -1, p.marky);
/* Preedit only. */
dc.pre = mkstr("preedit");
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, PopupBasew, p.w);
CT_EQ_INT(t, PopupPad, p.prey);
CT_EQ_INT(t, -1, p.sepy);
CT_EQ_INT(t, -1, p.rowsy);
CT_EQ_INT(t, -1, p.marky);
CT_EQ_INT(t, 2*PopupPad + Fontsz, p.h);
/* Candidates only. */
memset(&dc, 0, sizeof dc);
dc.sel = -1;
dc.nkouho = 2;
dc.kouho[0] = mkstr("short");
dc.kouho[1] = mkstr("candidate");
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, PopupBasew, p.w);
CT_EQ_INT(t, -1, p.prey);
CT_EQ_INT(t, -1, p.sepy);
CT_EQ_INT(t, PopupPad, p.rowsy);
CT_EQ_INT(t, -1, p.marky);
CT_EQ_INT(t, 2*PopupPad + 2*Fontsz, p.h);
CT_EQ_INT(t, PopupPad, p.numx);
CT_EQ_INT(t, p.numx + PopupNumw, p.textx);
CT_EQ_INT(t, PopupTextw, p.textw);
CT_EQ_INT(t, p.w - PopupPad, p.textx + p.textw);
/* Preedit and candidates meet at one separator, with no empty rows. */
dc.pre = mkstr("preedit");
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, PopupBasew, p.w);
CT_EQ_INT(t, PopupPad, p.prey);
CT_EQ_INT(t, p.prey + Fontsz, p.sepy);
CT_EQ_INT(t, p.sepy + PopupSep, p.rowsy);
CT_EQ_INT(t, -1, p.marky);
CT_EQ_INT(t, p.rowsy + 2*Fontsz + PopupPad, p.h);
CT_EQ_INT(t, 2*PopupPad + 3*Fontsz + PopupSep, p.h);
/* Long preedit expands to the monitor cap just like a candidate. */
sclear(&longrow);
for(i = 0; i < Maxrunes; i++)
sputr(&longrow, 'W');
memset(&dc, 0, sizeof dc);
dc.pre = longrow;
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, 2*PopupBasew, p.w);
/* The largest panel includes all sections and fits its backing image. */
memset(&dc, 0, sizeof dc);
dc.pre = mkstr("preedit");
dc.nkouho = Maxdisp;
dc.sel = 4;
dc.first = 0;
dc.total = Maxdisp + 1;
for(i = 0; i < Maxdisp; i++)
dc.kouho[i] = mkstr(i == dc.sel ? "M" : "candidate");
dc.kouho[0] = longrow;
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, Maxdisp, p.n);
CT_EQ_INT(t, PopupPad, p.prey);
CT_EQ_INT(t, p.prey + Fontsz, p.sepy);
CT_EQ_INT(t, p.sepy + PopupSep, p.rowsy);
CT_EQ_INT(t, p.rowsy + Maxdisp*Fontsz, p.marky);
CT_EQ_INT(t, p.marky + Fontsz + PopupPad, p.h);
CT_EQ_INT(t, Imgh, p.h);
CT_EQ_INT(t, 2*PopupBasew, p.w);
CT_CHECK(t, p.h <= Imgh);
CT_EQ_INT(t, p.w - PopupPad, p.markx + p.markw);
CT_EQ_INT(t, PopupPad, p.selx);
CT_EQ_INT(t, p.w - 2*PopupPad, p.selw);
CT_EQ_INT(t, p.rowsy + dc.sel*Fontsz, p.sely);
CT_EQ_INT(t, PopupPad, p.numx);
CT_EQ_INT(t, p.numx + PopupNumw, p.textx);
CT_EQ_INT(t, p.w - PopupPad, p.textx + p.textw);
img = emalloc(2*PopupBasew*Imgh*sizeof img[0]);
popupdraw(img, &dc, &p);
checkpadding(t, img, &p);
for(x = 0; x < p.w; x++)
CT_EQ_UINT(t,
x >= PopupPad && x < p.w - PopupPad ? Colsep : Colbg,
pixel(img, &p, x, p.sepy));
/* Selection paint and both foreground columns stay inside the row. */
selbg = numink = textink = 0;
for(y = p.sely; y < p.sely + Fontsz; y++){
for(x = 0; x < p.w; x++){
c = pixel(img, &p, x, y);
if(x < p.selx || x >= p.selx + p.selw){
CT_EQ_UINT(t, Colbg, c);
continue;
}
if(c == (Colsel & Rgbmask)){
selbg++;
continue;
}
CT_CHECK(t,
(x >= p.numx && x < p.numx + PopupNumw) ||
(x >= p.textx && x < p.textx + p.textw));
if(x < p.numx + PopupNumw)
numink++;
else
textink++;
}
}
CT_CHECK(t, Colsel != Colselfg);
CT_CHECK(t, selbg > p.selw*Fontsz/2);
CT_CHECK(t, numink > 0);
CT_CHECK(t, textink > 0);
/* The marker occupies only its compact, right-aligned footer box. */
markink = 0;
for(y = p.marky; y < p.marky + Fontsz; y++){
for(x = 0; x < p.w; x++){
if(pixel(img, &p, x, y) == (Colbg & Rgbmask))
continue;
markink++;
CT_CHECK(t, x >= p.markx && x < p.markx + p.markw);
}
}
CT_CHECK(t, markink > 0);
/* A long candidate grows from the stable base to the monitor cap. */
memset(&dc, 0, sizeof dc);
dc.sel = -1;
dc.nkouho = 1;
dc.kouho[0] = longrow;
n = textwidth(&longrow);
popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, 2*PopupBasew, p.w);
CT_CHECK(t, p.textw > PopupTextw);
CT_CHECK(t, n > p.textw);
CT_EQ_INT(t, PopupPad, p.rowsy);
CT_EQ_INT(t, p.w - PopupPad, p.textx + p.textw);
popupdraw(img, &dc, &p);
ink = 0;
for(y = p.rowsy; y < p.rowsy + Fontsz; y++){
for(x = 0; x < p.w; x++){
c = pixel(img, &p, x, y);
if(x < PopupPad || x >= p.w - PopupPad)
CT_EQ_UINT(t, Colbg, c);
if(x >= p.textx && x < p.textx + p.textw &&
c != (Colbg & Rgbmask))
ink++;
}
}
CT_CHECK(t, ink > 0);
CT_EQ_INT(t, n, textwidth(&longrow));
popuplayout(&dc, PopupBasew - 1, Imgh, &p);
CT_EQ_INT(t, PopupBasew - 1, p.w);
/* Height truncation scrolls the row window to the selected candidate. */
memset(&dc, 0, sizeof dc);
dc.nkouho = Maxdisp;
dc.sel = Maxdisp - 1;
dc.total = Maxdisp;
for(i = 0; i < Maxdisp; i++)
dc.kouho[i] = mkstr("candidate");
popuplayout(&dc, PopupBasew, 2*PopupPad + 2*Fontsz, &p);
CT_EQ_INT(t, 1, p.n);
CT_EQ_INT(t, Maxdisp - 1, p.row0);
CT_EQ_INT(t, PopupPad, p.sely);
CT_CHECK(t, p.marky >= 0);
CT_CHECK(t, p.h <= 2*PopupPad + 2*Fontsz);
popupdraw(img, &dc, &p);
numink = 0;
for(y = p.rowsy; y < p.rowsy + Fontsz; y++)
for(x = p.numx; x < p.numx + PopupNumw; x++)
if(pixel(img, &p, x, y) != (Colsel & Rgbmask))
numink++;
markink = 0;
for(y = p.marky; y < p.marky + Fontsz; y++)
for(x = p.markx; x < p.markx + p.markw; x++)
if(pixel(img, &p, x, y) != (Colbg & Rgbmask))
markink++;
CT_CHECK(t, numink > 0); /* full-width */
CT_CHECK(t, markink > 0); /* 9-9/9 */
/* The selected row wins over preedit and footer in one-row height. */
dc.pre = mkstr("preedit");
popuplayout(&dc, PopupBasew, 2*PopupPad + Fontsz, &p);
CT_EQ_INT(t, -1, p.prey);
CT_EQ_INT(t, 1, p.n);
CT_EQ_INT(t, Maxdisp - 1, p.row0);
CT_EQ_INT(t, -1, p.marky);
/* With less than one padded row, selected content starts at the top. */
for(i = 0; i < nelem(tinyheight); i++){
popuplayout(&dc, PopupBasew, tinyheight[i], &p);
CT_EQ_INT(t, min(tinyheight[i], Fontsz), p.h);
CT_EQ_INT(t, 0, p.rowsy);
CT_EQ_INT(t, 0, p.sely);
CT_EQ_INT(t, 1, p.n);
CT_EQ_INT(t, Maxdisp - 1, p.row0);
popupdraw(img, &dc, &p);
CT_EQ_UINT(t, Colsel, pixel(img, &p, p.w - PopupPad - 1, 0));
}
free(img);
}