diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | surfaces/hangul.c | 148 | ||||
| -rw-r--r-- | surfaces/input.c | 1 | ||||
| -rw-r--r-- | surfaces/main.c | 65 | ||||
| -rw-r--r-- | surfaces/vega.h | 19 | ||||
| -rw-r--r-- | tests/check.py | 52 |
6 files changed, 267 insertions, 20 deletions
@@ -12,7 +12,7 @@ LIBPURE = $(PURE)/build/libpure.a # host: TUI, CLI, NUI sentences, web), symbols/ (glyphs and icons as one # format, drawn by CPU or GPU). SURFACES = surfaces/screen.c surfaces/draw.c surfaces/width.c surfaces/web.c surfaces/pixels.c surfaces/display.c \ - surfaces/input.c surfaces/main.c + surfaces/input.c surfaces/hangul.c surfaces/main.c .PHONY: all check FORCE all: $(BUILD)/vega $(BUILD)/vega-symbols $(BUILD)/vega-surface.so diff --git a/surfaces/hangul.c b/surfaces/hangul.c new file mode 100644 index 0000000..7a6ad42 --- /dev/null +++ b/surfaces/hangul.c @@ -0,0 +1,148 @@ +#include "vega.h" +#include <string.h> + +/* Hangul typed on a US keyboard as the standard two-set layout (두벌식): a + * letter key gives a jamo, and jamo compose a syllable as they come. A + * syllable is an initial consonant (19), a vowel (21) and an optional final + * (27), which Unicode composes as U+AC00 + (initial * 21 + vowel) * 28 + + * final. Two vowels or two finals may join into one (ㅗ+ㅏ ㅘ, ㄹ+ㄱ ㄺ); a + * vowel after a final takes the final, or a joined final's second half, as + * its own initial (도깨비불). A jamo alone shows as its compatibility letter. */ + +/* Each letter key's jamo: an initial's index + 1, or a vowel's index + 1 as + * a negative number; lower case first, then with Shift (the doubled + * consonants ㄲ ㄸ ㅃ ㅆ ㅉ and the vowels ㅒ ㅖ; other letters as lower case). */ +static const signed char plain[26] = { + /* a ㅁ */ 7, /* b ㅠ */ -18, /* c ㅊ */ 15, /* d ㅇ */ 12, /* e ㄷ */ 4, /* f ㄹ */ 6, /* g ㅎ */ 19, + /* h ㅗ */ -9, /* i ㅑ */ -3, /* j ㅓ */ -5, /* k ㅏ */ -1, /* l ㅣ */ -21, /* m ㅡ */ -19, /* n ㅜ */ -14, + /* o ㅐ */ -2, /* p ㅔ */ -6, /* q ㅂ */ 8, /* r ㄱ */ 1, /* s ㄴ */ 3, /* t ㅅ */ 10, /* u ㅕ */ -7, + /* v ㅍ */ 18, /* w ㅈ */ 13, /* x ㅌ */ 17, /* y ㅛ */ -13, /* z ㅋ */ 16, +}; +static signed char shifted(char upper) { + switch (upper) { + case 'Q': return 9; /* ㅃ */ + case 'W': return 14; /* ㅉ */ + case 'E': return 5; /* ㄸ */ + case 'R': return 2; /* ㄲ */ + case 'T': return 11; /* ㅆ */ + case 'O': return -4; /* ㅒ */ + case 'P': return -8; /* ㅖ */ + default: return plain[upper - 'A']; + } +} + +/* An initial as a final (0: it cannot end a syllable: ㄸ ㅃ ㅉ), and a + * single final as an initial. */ +static const unsigned char final_of[19] = {1, 2, 4, 7, 0, 8, 16, 17, 0, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27}; +static signed char initial_of(unsigned final) { + for (int i = 0; i < 19; ++i) + if (final_of[i] == final && final) return (signed char)i; + return -1; +} + +/* Joined finals: the first final, the initial added, the joined final. */ +static const unsigned char joined_finals[][3] = { + {1, 9, 3}, {4, 12, 5}, {4, 18, 6}, {8, 0, 9}, {8, 6, 10}, {8, 7, 11}, + {8, 9, 12}, {8, 16, 13}, {8, 17, 14}, {8, 18, 15}, {17, 9, 18}, +}; +/* Joined vowels: the first vowel, the vowel added, the joined vowel. */ +static const unsigned char joined_vowels[][3] = {{8, 0, 9}, {8, 1, 10}, {8, 20, 11}, {13, 4, 14}, {13, 5, 15}, + {13, 20, 16}, {18, 20, 19}}; + +/* The compatibility letters (U+3131..) of the initials; the vowels' are + * U+314F + index. */ +static const uint16_t initial_letters[19] = {0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, + 0x3142, 0x3143, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149, + 0x314A, 0x314B, 0x314C, 0x314D, 0x314E}; + +void vega_hangul_reset(VegaHangul *h) { *h = (VegaHangul){-1, -1, 0, 0, {{0}}}; } + +/* The syllable (or lone jamo) as UTF-8 into out: its bytes (0 when none). */ +static size_t spell(signed char initial, signed char vowel, unsigned char final, char out[4]) { + uint32_t c = initial >= 0 && vowel >= 0 ? 0xAC00u + ((unsigned)initial * 21 + (unsigned)vowel) * 28 + final + : initial >= 0 ? initial_letters[initial] + : vowel >= 0 ? 0x314Fu + (unsigned)vowel + : 0; + if (!c) return 0; + out[0] = (char)(0xE0 | c >> 12); + out[1] = (char)(0x80 | (c >> 6 & 0x3F)); + out[2] = (char)(0x80 | (c & 0x3F)); + return 3; +} + +size_t vega_hangul_composing(const VegaHangul *h, char out[4]) { return spell(h->initial, h->vowel, h->final, out); } + +size_t vega_hangul_finish(VegaHangul *h, char out[4]) { + size_t length = vega_hangul_composing(h, out); + vega_hangul_reset(h); + return length; +} + +/* What it composed so far is kept for Backspace, then it changes. */ +static void step(VegaHangul *h, signed char initial, signed char vowel, unsigned char final) { + if (h->steps < sizeof(h->before) / sizeof(*h->before)) + h->before[h->steps++] = (struct VegaJamo){h->initial, h->vowel, h->final}; + h->initial = initial, h->vowel = vowel, h->final = final; +} + +/* A new syllable from here: what was composed is finished into done. */ +static size_t begin(VegaHangul *h, signed char initial, signed char vowel, char done[4]) { + size_t length = vega_hangul_finish(h, done); + step(h, initial, vowel, 0); + return length; +} + +bool vega_hangul_type(VegaHangul *h, char key, char done[4], size_t *done_length) { + *done_length = 0; + signed char jamo = key >= 'a' && key <= 'z' ? plain[key - 'a'] : key >= 'A' && key <= 'Z' ? shifted(key) : 0; + if (!jamo) return false; + if (jamo > 0) { /* a consonant */ + signed char consonant = (signed char)(jamo - 1); + if (h->vowel < 0 || h->initial < 0) { /* nothing, a lone initial or a lone vowel: this one starts anew */ + if (h->initial < 0 && h->vowel < 0) step(h, consonant, -1, 0); + else *done_length = begin(h, consonant, -1, done); + } else if (!h->final && final_of[consonant]) step(h, h->initial, h->vowel, final_of[consonant]); + else { + unsigned char joined = 0; + for (size_t i = 0; i < sizeof(joined_finals) / sizeof(*joined_finals) && !joined; ++i) + if (joined_finals[i][0] == h->final && joined_finals[i][1] == (unsigned char)consonant) joined = joined_finals[i][2]; + if (joined) step(h, h->initial, h->vowel, joined); + else *done_length = begin(h, consonant, -1, done); + } + return true; + } + signed char vowel = (signed char)(-jamo - 1); + if (h->final) { /* 도깨비불: the final (or its second half) moves on to start the next syllable */ + signed char moved = -1; + unsigned char kept = 0; + for (size_t i = 0; i < sizeof(joined_finals) / sizeof(*joined_finals); ++i) + if (joined_finals[i][2] == h->final) kept = joined_finals[i][0], moved = (signed char)joined_finals[i][1]; + if (moved < 0) moved = initial_of(h->final); + char out[4]; + *done_length = spell(h->initial, h->vowel, kept, out); + memcpy(done, out, *done_length); + vega_hangul_reset(h); + step(h, moved, -1, 0); /* so Backspace leaves the initial first */ + step(h, moved, vowel, 0); + return true; + } + if (h->vowel >= 0) { + unsigned char joined = 0; + for (size_t i = 0; i < sizeof(joined_vowels) / sizeof(*joined_vowels) && !joined; ++i) + if (joined_vowels[i][0] == (unsigned char)h->vowel && joined_vowels[i][1] == (unsigned char)vowel) + joined = joined_vowels[i][2]; + if (joined) step(h, h->initial, (signed char)joined, 0); + else *done_length = begin(h, -1, vowel, done); + return true; + } + step(h, h->initial, vowel, 0); /* after an initial, or alone */ + return true; +} + +bool vega_hangul_back(VegaHangul *h) { + if (h->initial < 0 && h->vowel < 0) return false; + if (!h->steps) { vega_hangul_reset(h); return true; } + struct VegaJamo before = h->before[--h->steps]; + h->initial = before.initial, h->vowel = before.vowel, h->final = before.final; + return true; +} diff --git a/surfaces/input.c b/surfaces/input.c index 9403a75..e13693d 100644 --- a/surfaces/input.c +++ b/surfaces/input.c @@ -52,6 +52,7 @@ static const struct { unsigned short code; const char *name; char plain, shifted {KEY_HOME, "Home", 0, 0}, {KEY_UP, "ArrowUp", 0, 0}, {KEY_PAGEUP, "PageUp", 0, 0}, {KEY_LEFT, "ArrowLeft", 0, 0}, {KEY_RIGHT, "ArrowRight", 0, 0}, {KEY_END, "End", 0, 0}, {KEY_DOWN, "ArrowDown", 0, 0}, {KEY_PAGEDOWN, "PageDown", 0, 0}, {KEY_INSERT, "Insert", 0, 0}, {KEY_DELETE, "Delete", 0, 0}, + {KEY_HANGEUL, "Lang1", 0, 0}, {KEY_RIGHTALT, "AltRight", 0, 0}, /* 한/영, or the right Alt that stands for it */ }; static bool has(int fd, unsigned type, unsigned code) { diff --git a/surfaces/main.c b/surfaces/main.c index 010a1bb..b0c32b5 100644 --- a/surfaces/main.c +++ b/surfaces/main.c @@ -37,9 +37,12 @@ * written again replaces the running one and states keep their values by * name: aup watch app.aup -o app.pmod beside it makes a saved edit show at once. * Script commands, one per line: key KEYNAME | text HEX|- | type HEX | enter | - * tab | frame [N] | point VIEW ROW | click LINE COLUMN | time MS | tick TIMER | swap MODULE | show. key presses and + * tab | hangul | letters HEX | back | frame [N] | point VIEW ROW | click LINE COLUMN | time MS | tick TIMER | + * swap MODULE | show. key presses and * releases the input bound to that key; text commits text to the screen's text - * input; type, enter and tab act on the TUI's input line; point focuses a row + * input; type, enter and tab act on the TUI's input line, and so do hangul (the + * 한/영 key), letters (keys typed, each byte one, through Hangul when it is on) + * and back (Backspace); point focuses a row * (0 first) of a list or table; click is a mouse click on the last shown TUI * frame (1 first, as terminals report it); time sets the time the next * events see (live surfaces use the clock) and tick ticks a timer (live @@ -61,6 +64,10 @@ static VegaGrid *drawn; /* the TUI's last frame, which a click lands on */ static char entry[VEGA_TEXT]; static size_t entry_length; static bool typing; +/* Hangul typed on the keyboard (Lang1, the 한/영 key, or AltRight switches): + * the syllable being composed shows after the entry line until it is done. */ +static bool korean; +static VegaHangul hangul = {-1, -1, 0, 0, {{0, 0, 0}}}; /* The module file last read, and a digest of its bytes. A loaded module * keeps nothing of them, so one buffer, grown to the largest, serves every load. */ @@ -357,15 +364,38 @@ static bool raw_mode(void) { /* Whether the app takes Tab itself; otherwise Tab switches typing and keys. */ static bool tab_switches(void) { return screen.prompt >= 0 && key_input("Tab") < 0; } +static void append(const char *bytes, size_t length) { + if (entry_length + length < sizeof(entry)) { memcpy(entry + entry_length, bytes, length); entry_length += length; } +} + +/* The syllable being composed, done: into the entry line. */ +static void finish_hangul(void) { + char done[4]; + append(done, vega_hangul_finish(&hangul, done)); +} + +/* The entry line as shown: what was typed, then the syllable being composed. */ +static size_t shown_entry(char out[VEGA_TEXT + 4]) { + memcpy(out, entry, entry_length); + return entry_length + vega_hangul_composing(&hangul, out + entry_length); +} + /* One key from the terminal, as typed text or as a Pure key name. */ static void handle_key(const char *name) { int input; - if (!strcmp(name, "Tab") && tab_switches()) { typing = !typing; return; } + if (!strcmp(name, "Lang1") || !strcmp(name, "AltRight")) { /* 한/영 */ + finish_hangul(); + korean = !korean; + return; + } + if (!strcmp(name, "Tab") && tab_switches()) { finish_hangul(); typing = !typing; return; } if (typing && !strcmp(name, "Enter")) { + finish_hangul(); if (entry_length && commit(entry, entry_length)) entry_length = 0; return; } if (typing && !strcmp(name, "Backspace")) { + if (vega_hangul_back(&hangul)) return; /* a jamo of the syllable being composed */ while (entry_length && ((unsigned char)entry[entry_length - 1] & 0xC0) == 0x80) --entry_length; if (entry_length) --entry_length; return; @@ -375,7 +405,14 @@ static void handle_key(const char *name) { static void handle_text(const char *bytes, size_t length) { if (typing) { - if (entry_length + length < sizeof(entry)) { memcpy(entry + entry_length, bytes, length); entry_length += length; } + char done[4]; + size_t finished; + if (korean && length == 1 && vega_hangul_type(&hangul, bytes[0], done, &finished)) { + append(done, finished); + return; + } + finish_hangul(); /* anything but a letter ends the syllable */ + append(bytes, length); return; } if (length != 1) return; @@ -457,7 +494,8 @@ static int interactive(void) { /* Tick what is due, draw, then wait for input, a frame or the next tick. */ bool ticked = false; long due = vega_timers_run(&timers, &app, vega_now(), &ticked); - vega_draw(&grid, &app, &screen, entry, entry_length, typing); + char line[VEGA_TEXT + 4]; + vega_draw(&grid, &app, &screen, line, shown_entry(line), typing); paint_terminal(&grid); struct pollfd wait = {STDIN_FILENO, POLLIN, 0}; int timeout = screen.ticks ? 16 : watched ? 200 : -1; @@ -597,7 +635,8 @@ static int live(VegaInput *input, unsigned columns, unsigned rows, bool recorded bool ticked = false; long due = node ? -1 : vega_timers_run(&timers, &app, vega_now(), &ticked); PureSnapshot *snapshot = node ? node->read(node->host, &app) : NULL; - if (!node || snapshot) vega_draw(&grid, &app, &screen, entry, entry_length, typing); + char line[VEGA_TEXT + 4]; + if (!node || snapshot) vega_draw(&grid, &app, &screen, line, shown_entry(line), typing); if (snapshot) node->release(node->host, snapshot); if (!display) { /* a node's pixels: each frame into a file */ if (!write_frame(&grid, path)) { fprintf(stderr, "vega: cannot write %s\n", path); status = 1; break; } @@ -688,6 +727,14 @@ static int script(Surface surface, unsigned width, unsigned height, bool paintin } else if (!strcmp(command, "tab")) { handle_key("Tab"); acted = false; + } else if (!strcmp(command, "hangul") || !strcmp(command, "back")) { + handle_key(!strcmp(command, "hangul") ? "Lang1" : "Backspace"); + acted = false; + } else if (!strcmp(command, "letters")) { + char keys[VEGA_TEXT]; + size_t count = unhex(argument, keys, sizeof(keys)); + for (size_t i = 0; i < count; ++i) handle_text(keys + i, 1); + acted = false; } else if (!strcmp(command, "point")) { char name[PURE_MODULE_NAME]; long row = -1; @@ -728,14 +775,16 @@ static int script(Surface surface, unsigned width, unsigned height, bool paintin else if (surface == SURFACE_SAY) vega_say(stdout, &app, &screen, true); else if ((shown = true) && painting) { Out out = {0}; - vega_draw(&grid, &app, &screen, entry, entry_length, typing); + char prompt[VEGA_TEXT + 4]; + vega_draw(&grid, &app, &screen, prompt, shown_entry(prompt), typing); paint(&grid, &out); printf("paint %zu\n", out.used); fwrite(out.bytes, 1, out.used, stdout); putchar('\n'); free(out.bytes); } else { - vega_draw(&grid, &app, &screen, entry, entry_length, typing); + char prompt[VEGA_TEXT + 4]; + vega_draw(&grid, &app, &screen, prompt, shown_entry(prompt), typing); dump(&grid); printf("cells %lu\n", vega_evaluations() - counted); char path[512]; diff --git a/surfaces/vega.h b/surfaces/vega.h index fd8780b..cd6b1ee 100644 --- a/surfaces/vega.h +++ b/surfaces/vega.h @@ -153,6 +153,25 @@ bool vega_display_show(VegaDisplay *); const uint32_t *vega_display_captured(const VegaDisplay *, size_t *stride); void vega_display_close(VegaDisplay *); +/* Hangul typed on a US keyboard as the two-set layout (두벌식, hangul.c): + * letter keys give jamo that compose syllables as they come. type takes a + * letter (A-Z with Shift) and gives what it finished (UTF-8, at most one + * syllable) into done; false for a key that is not a letter. composing is + * the syllable being composed (0 bytes: none); back takes back its last jamo + * (false: none was being composed); finish ends it into out. */ +struct VegaJamo { signed char initial, vowel; unsigned char final; }; +typedef struct { + signed char initial, vowel; /* -1: none */ + unsigned char final; /* 0: none */ + unsigned char steps; /* what it was before each jamo, for Backspace */ + struct VegaJamo before[8]; +} VegaHangul; +void vega_hangul_reset(VegaHangul *); +bool vega_hangul_type(VegaHangul *, char key, char done[4], size_t *done_length); +size_t vega_hangul_composing(const VegaHangul *, char out[4]); +bool vega_hangul_back(VegaHangul *); +size_t vega_hangul_finish(VegaHangul *, char out[4]); + /* Input from evdev: keyboards (key names and the text they type on a US * layout), mice and touch screens (presses at a pixel of a width x height * screen). With count paths, recorded events are read from those files diff --git a/tests/check.py b/tests/check.py index ad30654..de26a3f 100644 --- a/tests/check.py +++ b/tests/check.py @@ -1160,6 +1160,31 @@ def live_display(vega, module, work): return {'events': len(events), 'lines': len(got)} +def hangul(vega, module): + """Hangul typed on the keyboard (두벌식, surfaces/hangul.c): letter keys + compose syllables in the entry line after the 한/영 key, and Enter commits + them. Joined vowels and finals, a final moving on to a vowel after it, + Shift's doubled consonants, a lone jamo, Backspace taking back one jamo at + a time, and a space or digit ending a syllable, each as an item.""" + cases = [('dkssudgktpdy', '', '안녕하세요'), ('rkqtdmf', '', '값을'), ('dkswk', '', '안자'), + ('dhkdnjs', '', '와원'), ('Rkdnjs', '', '까원'), ('rkqt', 'back', '갑'), ('gksrmf', '', '한글'), + ('k', '', 'ㅏ'), ('dkswk', 'back', '안ㅈ'), ('dkssud 2', '', '안녕 2')] + script = 'hangul\n' + ''.join(f'letters {keys.encode().hex()}\n' + (f'{after}\n' if after else '') + 'enter\n' + for keys, after, _ in cases) + 'show\n' + shown = run(vega, 'tui', module, '--dump', '40x16', stdin=script) + assert shown.returncode == 0, shown.stderr + labels = [line[1:].strip().split(' ')[0].strip() for line in shown.stdout.splitlines() + if line[1:].startswith(' ') or line[1:].startswith('> ')] + labels = [label.lstrip('> ').strip() for label in labels] + assert labels[:len(cases)] == [want for _, _, want in cases], (labels, shown.stdout) + # The syllable being composed shows on the entry line; Backspace takes it back jamo by jamo, then a letter. + typing = run(vega, 'tui', module, '--dump', '30x5', + stdin=f'hangul\nletters {"dkssu".encode().hex()}\nshow\nback\nshow\nback\nback\nshow\n') + prompts = [line for line in typing.stdout.splitlines() if line.startswith('|') and '>' in line[:6] and 'add' in line] + assert [p.split('>', 1)[1].split('\t')[0].strip() for p in prompts] == ['안녀', '안ㄴ', ''], typing.stdout + return {'items': len(cases)} + + def node_display(vega, serve, module, work): """Vega in a Pure node (vega-surface.so run by pure-serve --surface): the node's app, which an HTTP client gave an item, is on its display, and @@ -1247,9 +1272,10 @@ def messenger(vega, serve, module, work): """Two nodes of examples/messenger.aup, Alice's and Bob's, each with a key of its own, sealing to the other's and taking lines only from the other (pure-serve --key, --send, --seal, --sender), each showing its app - through Vega's pixels in the node. A line typed on Alice's display - (recorded keys) reaches Bob's node sealed and is drawn on his; Bob's - answer, posted by a program, is drawn on hers. Each last frame is the one + through Vega's pixels in the node. Lines typed on Alice's display + (recorded keys: one in English, one in Hangul after the 한/영 key) reach + Bob's node sealed and are drawn on his; Bob's answer, posted by a + program, is drawn on hers. Each last frame is the one the vega command draws from that node's store, and the relay between the nodes saw no line's text nor a field's name, only seals made in one session each and naming the sender's key.""" @@ -1341,9 +1367,11 @@ def messenger(vega, serve, module, work): '--surface', library, '--', 'pixels', '--symbols', face, '--size', 16, '--grid', '40x8', '--out', work / f'{name}-frame'] for name in ('alice', 'bob')} start('bob', *wiring['bob']) - codes = {'h': 35, 'e': 18, 'l': 38, 'o': 24, 'b': 48, 'space': 57, 'enter': 28} + codes = {'h': 35, 'e': 18, 'l': 38, 'o': 24, 'b': 48, 'space': 57, 'enter': 28, 'lang1': 122, 'd': 32, + 'k': 37, 's': 31, 'u': 22} events = [] - for name in ('h', 'e', 'l', 'l', 'o', 'space', 'b', 'o', 'b', 'enter'): + for name in ('h', 'e', 'l', 'l', 'o', 'space', 'b', 'o', 'b', 'enter', 'lang1', 'd', 'k', 's', 's', 'u', 'd', + 'enter'): events.extend([(1, codes[name], 1), (0, 0, 0), (1, codes[name], 0), (0, 0, 0)]) recording = work / 'alice-events.bin' recording.write_bytes(b''.join(struct.pack('llHHi', 0, 0, *event) for event in events)) @@ -1353,10 +1381,10 @@ def messenger(vega, serve, module, work): while '\nframe ' not in '\n' + printed and time.time() < deadline: if select.select([process.stdout], [], [], 1)[0]: printed += os.read(process.stdout.fileno(), 65536).decode() - assert 'hello bob' in printed and '\nframe ' in '\n' + printed, printed - assert wait_for(lambda: call('bob', 'GET', '/views/title')[1] == 'bob: 1 lines'), call('bob', 'GET', '/views/title') + assert 'hello bob' in printed and '안녕' in printed and '\nframe ' in '\n' + printed, printed + assert wait_for(lambda: call('bob', 'GET', '/views/title')[1] == 'bob: 2 lines'), call('bob', 'GET', '/views/title') assert call('bob', 'POST', '/inputs/say', b'hi alice')[0] == 200 - assert wait_for(lambda: call('alice', 'GET', '/views/title')[1] == 'alice: 2 lines') + assert wait_for(lambda: call('alice', 'GET', '/views/title')[1] == 'alice: 3 lines') drawn = {} for name in ('alice', 'bob'): # Its surface draws after each save: the last frame shows the last save. @@ -1374,7 +1402,7 @@ def messenger(vega, serve, module, work): expected = run(vega, 'pixels', module, '--store', copy, '--symbols', face, '--size', 16, '--grid', '40x8', '--out', work / f'{name}-expected', stdin='show\n') assert expected.returncode == 0, expected.stderr - assert 'hello bob' in expected.stdout and 'hi alice' in expected.stdout, expected.stdout + assert all(line in expected.stdout for line in ('hello bob', '안녕', 'hi alice')), expected.stdout assert drawn[name].read_bytes() == (work / f'{name}-expected-1.ppm').read_bytes(), name finally: for process, _ in nodes.values(): @@ -1383,10 +1411,11 @@ def messenger(vega, serve, module, work): process.wait() relay.shutdown() assert seen and all(len(headers['X-Pure-Seal'].split()) == 4 for _, headers, _ in seen), seen[:1] - assert not any(word in body for _, _, body in seen for word in (b'hello', b'hi alice', b'body', b'from')) + assert not any(word in body for _, _, body in seen + for word in (b'hello', '안녕'.encode(), b'hi alice', b'body', b'from')) sessions = {(headers['X-Pure-Source'], headers['X-Pure-Seal'].split()[1]) for _, headers, _ in seen} assert len(sessions) == 2, sessions # one session each way - return {'relayed': len(seen), 'lines': 2} + return {'relayed': len(seen), 'lines': 3} def width_of(character): @@ -1427,6 +1456,7 @@ def main(): report['interactive_sessions'] = interactive(vega, modules['todo']) + \ interactive(vega, modules['todo'], work / 'interactive-store') report['stored_surface_lines'] = stored_surfaces(vega, modules['todo'], work, range(args.seeds)) + report['hangul'] = hangul(vega, modules['todo']) report['convention_findings'] = conventions(vega, aup, work, modules) report['examples'], report['robustness_runs'] = robustness(vega, aup, work, args.rounds) if args.symbols: |
