123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774 |
- #include "putty.h"
- #include "terminal.h"
- void modalfatalbox(const char *p, ...)
- {
- va_list ap;
- fprintf(stderr, "FATAL ERROR: ");
- va_start(ap, p);
- vfprintf(stderr, p, ap);
- va_end(ap);
- fputc('\n', stderr);
- exit(1);
- }
- const char *const appname = "test_lineedit";
- char *platform_default_s(const char *name)
- { return NULL; }
- bool platform_default_b(const char *name, bool def)
- { return def; }
- int platform_default_i(const char *name, int def)
- { return def; }
- FontSpec *platform_default_fontspec(const char *name)
- { return fontspec_new_default(); }
- Filename *platform_default_filename(const char *name)
- { return filename_from_str(""); }
- struct SpecialRecord {
- SessionSpecialCode code;
- int arg;
- };
- typedef struct Mock {
- Terminal *term;
- Ldisc *ldisc;
- Conf *conf;
- struct unicode_data ucsdata[1];
- bool echo, edit;
- strbuf *to_terminal, *to_backend;
- struct SpecialRecord *specials;
- size_t nspecials, specialsize;
- strbuf *context; /* for printing in failed tests */
- bool any_test_failed;
- TermWin tw;
- Seat seat;
- Backend backend;
- } Mock;
- static size_t mock_output(Seat *seat, SeatOutputType type,
- const void *data, size_t len)
- {
- Mock *mk = container_of(seat, Mock, seat);
- put_data(mk->to_terminal, data, len);
- return 0;
- }
- static void mock_send(Backend *be, const char *buf, size_t len)
- {
- Mock *mk = container_of(be, Mock, backend);
- put_data(mk->to_backend, buf, len);
- }
- static void mock_special(Backend *be, SessionSpecialCode code, int arg)
- {
- Mock *mk = container_of(be, Mock, backend);
- sgrowarray(mk->specials, mk->specialsize, mk->nspecials);
- mk->specials[mk->nspecials].code = code;
- mk->specials[mk->nspecials].arg = arg;
- mk->nspecials++;
- }
- static bool mock_ldisc_option_state(Backend *be, int option)
- {
- Mock *mk = container_of(be, Mock, backend);
- switch (option) {
- case LD_ECHO:
- return mk->echo;
- case LD_EDIT:
- return mk->edit;
- default:
- unreachable("bad ldisc option");
- }
- }
- static void mock_provide_ldisc(Backend *be, Ldisc *ldisc)
- {
- Mock *mk = container_of(be, Mock, backend);
- mk->ldisc = ldisc;
- }
- static bool mock_sendok(Backend *be)
- {
- Mock *mk = container_of(be, Mock, backend);
- (void)mk;
- /* FIXME: perhaps make this settable, to test the input_queue system? */
- return true;
- }
- static void mock_set_raw_mouse_mode(TermWin *win, bool enable) {}
- static void mock_palette_get_overrides(TermWin *tw, Terminal *term) {}
- static const TermWinVtable mock_termwin_vt = {
- .set_raw_mouse_mode = mock_set_raw_mouse_mode,
- .palette_get_overrides = mock_palette_get_overrides,
- };
- static const SeatVtable mock_seat_vt = {
- .output = mock_output,
- .echoedit_update = nullseat_echoedit_update,
- };
- static const BackendVtable mock_backend_vt = {
- .sendok = mock_sendok,
- .send = mock_send,
- .special = mock_special,
- .ldisc_option_state = mock_ldisc_option_state,
- .provide_ldisc = mock_provide_ldisc,
- .id = "mock",
- };
- static Mock *mock_new(void)
- {
- Mock *mk = snew(Mock);
- memset(mk, 0, sizeof(*mk));
- mk->conf = conf_new();
- do_defaults(NULL, mk->conf);
- init_ucs_generic(mk->conf, mk->ucsdata);
- mk->ucsdata->line_codepage = CP_437;
- mk->context = strbuf_new();
- mk->to_terminal = strbuf_new();
- mk->to_backend = strbuf_new();
- mk->tw.vt = &mock_termwin_vt;
- mk->seat.vt = &mock_seat_vt;
- mk->backend.vt = &mock_backend_vt;
- return mk;
- }
- static void mock_free(Mock *mk)
- {
- strbuf_free(mk->context);
- strbuf_free(mk->to_terminal);
- strbuf_free(mk->to_backend);
- conf_free(mk->conf);
- term_free(mk->term);
- sfree(mk->specials);
- sfree(mk);
- }
- static void reset(Mock *mk)
- {
- strbuf_clear(mk->context);
- strbuf_clear(mk->to_terminal);
- strbuf_clear(mk->to_backend);
- mk->nspecials = 0;
- }
- static void test_context(Mock *mk, const char *fmt, ...)
- {
- strbuf_clear(mk->context);
- va_list ap;
- va_start(ap, fmt);
- put_fmtv(mk->context, fmt, ap);
- va_end(ap);
- }
- static void print_context(Mock *mk, const char *file, int line)
- {
- printf("%s:%d", file, line);
- if (mk->context->len)
- printf(" (%s)", mk->context->s);
- printf(": ");
- }
- #define EXPECT(mk, what, ...) \
- expect_ ## what(mk, __FILE__, __LINE__, __VA_ARGS__)
- static void expect_backend(Mock *mk, const char *file, int line,
- ptrlen expected)
- {
- ptrlen actual = ptrlen_from_strbuf(mk->to_backend);
- if (!ptrlen_eq_ptrlen(expected, actual)) {
- print_context(mk, file, line);
- printf("expected backend output \"");
- write_c_string_literal(stdout, expected);
- printf("\", got \"");
- write_c_string_literal(stdout, actual);
- printf("\"\n");
- mk->any_test_failed = true;
- }
- }
- static void expect_terminal(Mock *mk, const char *file, int line,
- ptrlen expected)
- {
- ptrlen actual = ptrlen_from_strbuf(mk->to_terminal);
- if (!ptrlen_eq_ptrlen(expected, actual)) {
- print_context(mk, file, line);
- printf("expected terminal output \"");
- write_c_string_literal(stdout, expected);
- printf("\", got \"");
- write_c_string_literal(stdout, actual);
- printf("\"\n");
- mk->any_test_failed = true;
- }
- }
- static void expect_specials(Mock *mk, const char *file, int line,
- size_t nspecials, ...)
- {
- va_list ap;
- static const char *const special_names[] = {
- #define SPECIAL(x) #x,
- #include "specials.h"
- #undef SPECIAL
- };
- bool match;
- if (nspecials != mk->nspecials) {
- match = false;
- } else {
- match = true;
- va_start(ap, nspecials);
- for (size_t i = 0; i < nspecials; i++) {
- SessionSpecialCode code = va_arg(ap, SessionSpecialCode);
- int arg = va_arg(ap, int);
- if (code != mk->specials[i].code || arg != mk->specials[i].arg)
- match = false;
- }
- va_end(ap);
- }
- if (!match) {
- print_context(mk, file, line);
- printf("expected specials [");
- va_start(ap, nspecials);
- for (size_t i = 0; i < nspecials; i++) {
- SessionSpecialCode code = va_arg(ap, SessionSpecialCode);
- int arg = va_arg(ap, int);
- printf(" %s.%d", special_names[code], arg);
- }
- va_end(ap);
- printf(" ], got [");
- for (size_t i = 0; i < mk->nspecials; i++) {
- printf(" %s.%d", special_names[mk->specials[i].code],
- mk->specials[i].arg);
- }
- printf(" ]\n");
- mk->any_test_failed = true;
- }
- }
- static void test_noedit(Mock *mk)
- {
- mk->edit = false;
- mk->echo = false;
- /*
- * In non-echo and non-edit mode, the channel is 8-bit clean
- */
- for (unsigned c = 0; c < 256; c++) {
- char buf[1];
- test_context(mk, "c=%02x", c);
- buf[0] = c;
- ldisc_send(mk->ldisc, buf, 1, false);
- EXPECT(mk, backend, make_ptrlen(buf, 1));
- reset(mk);
- }
- /* ... regardless of the 'interactive' flag */
- for (unsigned c = 0; c < 256; c++) {
- char buf[1];
- test_context(mk, "c=%02x", c);
- buf[0] = c;
- ldisc_send(mk->ldisc, buf, 1, true);
- EXPECT(mk, backend, make_ptrlen(buf, 1));
- reset(mk);
- }
- /* ... and any nonzero character does the same thing even if sent
- * with the magic -1 length flag */
- for (unsigned c = 1; c < 256; c++) {
- char buf[2];
- test_context(mk, "c=%02x", c);
- buf[0] = c;
- buf[1] = '\0';
- ldisc_send(mk->ldisc, buf, -1, true);
- EXPECT(mk, backend, make_ptrlen(buf, 1));
- reset(mk);
- }
- /*
- * Test the special-character cases for Telnet.
- */
- conf_set_int(mk->conf, CONF_protocol, PROT_TELNET);
- conf_set_bool(mk->conf, CONF_telnet_newline, false);
- conf_set_bool(mk->conf, CONF_telnet_keyboard, false);
- ldisc_configure(mk->ldisc, mk->conf);
- /* Without telnet_newline or telnet_keyboard, these all do the
- * normal thing */
- ldisc_send(mk->ldisc, "\x0D", -1, true);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x0D"));
- reset(mk);
- ldisc_send(mk->ldisc, "\x08", -1, true);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x08"));
- reset(mk);
- ldisc_send(mk->ldisc, "\x7F", -1, true);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x7F"));
- reset(mk);
- ldisc_send(mk->ldisc, "\x03", -1, true);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x03"));
- reset(mk);
- ldisc_send(mk->ldisc, "\x1A", -1, true);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x1A"));
- reset(mk);
- /* telnet_newline controls translation of CR into SS_EOL */
- conf_set_bool(mk->conf, CONF_telnet_newline, true);
- ldisc_configure(mk->ldisc, mk->conf);
- ldisc_send(mk->ldisc, "\x0D", -1, true);
- EXPECT(mk, specials, 1, SS_EOL, 0);
- reset(mk);
- /* And telnet_keyboard controls the others */
- conf_set_bool(mk->conf, CONF_telnet_newline, false);
- conf_set_bool(mk->conf, CONF_telnet_keyboard, true);
- ldisc_configure(mk->ldisc, mk->conf);
- ldisc_send(mk->ldisc, "\x08", -1, true);
- EXPECT(mk, specials, 1, SS_EC, 0);
- reset(mk);
- ldisc_send(mk->ldisc, "\x7F", -1, true);
- EXPECT(mk, specials, 1, SS_EC, 0);
- reset(mk);
- ldisc_send(mk->ldisc, "\x03", -1, true);
- EXPECT(mk, specials, 1, SS_IP, 0);
- reset(mk);
- ldisc_send(mk->ldisc, "\x1A", -1, true);
- EXPECT(mk, specials, 1, SS_SUSP, 0);
- reset(mk);
- /*
- * In echo-but-no-edit mode, we also expect that every character
- * is echoed back to the display as a side effect, including when
- * it's sent as a special -1 keystroke.
- *
- * This state only comes up in Telnet, because that has protocol
- * options to independently configure echo and edit. Telnet is
- * also the most complicated of the protocols because of the above
- * special cases, so we stay in Telnet mode for this test.
- */
- mk->echo = true;
- for (unsigned c = 0; c < 256; c++) {
- char buf[1];
- test_context(mk, "c=%02x", c);
- buf[0] = c;
- ldisc_send(mk->ldisc, buf, 1, false);
- EXPECT(mk, terminal, make_ptrlen(buf, 1));
- reset(mk);
- }
- for (unsigned c = 1; c < 256; c++) {
- char buf[2];
- test_context(mk, "c=%02x", c);
- buf[0] = c;
- buf[1] = '\0';
- ldisc_send(mk->ldisc, buf, -1, true);
- EXPECT(mk, terminal, make_ptrlen(buf, 1));
- reset(mk);
- }
- do_defaults(NULL, mk->conf);
- ldisc_configure(mk->ldisc, mk->conf);
- }
- static void test_edit(Mock *mk, bool echo)
- {
- static const char *const ctls = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
- mk->edit = true;
- mk->echo = echo;
- #define EXPECT_TERMINAL(mk, val) do { \
- if (!echo) EXPECT(mk, terminal, PTRLEN_LITERAL("")); \
- else EXPECT(mk, terminal, val); \
- } while (0)
- /* ASCII printing characters all print when entered, but don't go
- * to the terminal until Return is pressed */
- for (unsigned c = 0x20; c < 0x7F; c++) {
- char buf[3];
- test_context(mk, "c=%02x", c);
- buf[0] = c;
- ldisc_send(mk->ldisc, buf, 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, make_ptrlen(buf, 1));
- ldisc_send(mk->ldisc, "\015", 1, false);
- buf[1] = '\015';
- buf[2] = '\012';
- EXPECT(mk, backend, make_ptrlen(buf, 3));
- EXPECT_TERMINAL(mk, make_ptrlen(buf, 3));
- reset(mk);
- }
- /* C0 control characters mostly show up as ^X or similar */
- for (unsigned c = 0; c < 0x1F; c++) {
- char backbuf[3];
- char termbuf[4];
- switch (ctls[c]) {
- case 'D': continue; /* ^D sends EOF */
- case 'M': continue; /* ^M is Return */
- case 'R': continue; /* ^R redisplays line */
- case 'U': continue; /* ^U deletes the line */
- case 'V': continue; /* ^V makes the next char literal */
- case 'W': continue; /* ^W deletes a word */
- /*
- * ^H / ^? are not included here. Those are treated
- * literally if sent as plain input bytes. Only sending
- * them as special via length==-1 causes them to act as
- * backspace, which I think was simply because there _is_
- * a dedicated key that can do that function, so there's
- * no need to also eat the Ctrl+thing combo.
- */
- /*
- * Also, ^C, ^Z and ^\ self-insert (when not in Telnet
- * mode) but have the side effect of erasing the line
- * buffer so far. In this loop, that doesn't show up,
- * because the line buffer is empty already. However, I
- * don't test that, because it's silly, and probably
- * doesn't want to keep happening!
- */
- }
- test_context(mk, "c=%02x", c);
- backbuf[0] = c;
- ldisc_send(mk->ldisc, backbuf, 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- termbuf[0] = '^';
- termbuf[1] = ctls[c];
- EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 2));
- ldisc_send(mk->ldisc, "\015", 1, false);
- backbuf[1] = '\015';
- backbuf[2] = '\012';
- EXPECT(mk, backend, make_ptrlen(backbuf, 3));
- termbuf[2] = '\015';
- termbuf[3] = '\012';
- EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 4));
- reset(mk);
- }
- /* Prefixed with ^V, the same is true of _all_ C0 controls */
- for (unsigned c = 0; c < 0x1F; c++) {
- char backbuf[3];
- char termbuf[4];
- test_context(mk, "c=%02x", c);
- backbuf[0] = 'V' & 0x1F;
- ldisc_send(mk->ldisc, backbuf, 1, false);
- backbuf[0] = c;
- ldisc_send(mk->ldisc, backbuf, 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- termbuf[0] = '^';
- termbuf[1] = ctls[c];
- EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 2));
- ldisc_send(mk->ldisc, "\015", 1, false);
- backbuf[1] = '\015';
- backbuf[2] = '\012';
- EXPECT(mk, backend, make_ptrlen(backbuf, 3));
- termbuf[2] = '\015';
- termbuf[3] = '\012';
- EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 4));
- reset(mk);
- }
- /* Deleting an ASCII character sends a single BSB and deletes just
- * that byte from the buffer */
- ldisc_send(mk->ldisc, "ab", 2, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("ab"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("ab\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("a\x0D\x0A"));
- reset(mk);
- /* Deleting a character written as a ^X code sends two BSBs to
- * wipe out the two-character display sequence */
- ldisc_send(mk->ldisc, "a\x02", 2, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^B"));
- ldisc_send(mk->ldisc, "\x7F", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^B\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("a\x0D\x0A"));
- reset(mk);
- /* ^D sends the line editing buffer without a trailing Return, if
- * it's non-empty */
- ldisc_send(mk->ldisc, "abc\x04", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("abc"));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("abc\x0D\x0A"));
- reset(mk);
- /* But if the buffer is empty, ^D sends SS_EOF */
- ldisc_send(mk->ldisc, "\x04", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL(""));
- EXPECT(mk, specials, 1, SS_EOF, 0);
- reset(mk);
- /* ^R redraws the current line, after printing "^R" at the end of
- * the previous attempt to make it clear that that's what
- * happened */
- ldisc_send(mk->ldisc, "a\x01", 2, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^A"));
- ldisc_send(mk->ldisc, "\x12", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^A^R\x0D\x0A" "a^A"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- /* ^U deletes the whole line */
- ldisc_send(mk->ldisc, "a b c", 5, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a b c"));
- ldisc_send(mk->ldisc, "\x15", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL(
- "a b c\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x0D\x0A"));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL(
- "a b c\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x0D\x0A"));
- reset(mk);
- /* And it still knows that a control character written as ^X takes
- * two BSBs to delete */
- ldisc_send(mk->ldisc, "a\x02" "c", 3, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^Bc"));
- ldisc_send(mk->ldisc, "\x15", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL("a^Bc\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- /* ^W deletes a word, which means that it deletes to the most
- * recent boundary with a space on the left and a nonspace on the
- * right. (Or the beginning of the string, whichever comes first.) */
- ldisc_send(mk->ldisc, "hello, world\x17", 13, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL(
- "hello, world\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("hello, \x0D\x0A"));
- reset(mk);
- ldisc_send(mk->ldisc, "hello, world \x17", 14, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL(
- "hello, world "
- "\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("hello, \x0D\x0A"));
- reset(mk);
- ldisc_send(mk->ldisc, " hello \x17", 8, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL(
- " hello \x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(" \x0D\x0A"));
- reset(mk);
- ldisc_send(mk->ldisc, "hello \x17", 7, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL(
- "hello \x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\x0D\x0A"));
- reset(mk);
- /* And this too knows that a control character written as ^X takes
- * two BSBs to delete */
- ldisc_send(mk->ldisc, "a\x02" "c", 3, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^Bc"));
- ldisc_send(mk->ldisc, "\x17", 1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(
- mk, PTRLEN_LITERAL("a^Bc\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- /* Test handling of ^C and friends in non-telnet_keyboard mode */
- ldisc_send(mk->ldisc, "abc\x03", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08^C"));
- EXPECT(mk, specials, 1, SS_EL, 0);
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- ldisc_send(mk->ldisc, "abc\x1a", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08^Z"));
- EXPECT(mk, specials, 1, SS_EL, 0);
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- ldisc_send(mk->ldisc, "abc\x1c", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08^\\"));
- EXPECT(mk, specials, 1, SS_EL, 0);
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- /* And in telnet_keyboard mode */
- conf_set_bool(mk->conf, CONF_telnet_keyboard, true);
- ldisc_configure(mk->ldisc, mk->conf);
- /* FIXME: should we _really_ be sending EL before each of these? */
- ldisc_send(mk->ldisc, "abc\x03", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08"));
- EXPECT(mk, specials, 2, SS_EL, 0, SS_IP, 0);
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- ldisc_send(mk->ldisc, "abc\x1a", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08"));
- EXPECT(mk, specials, 2, SS_EL, 0, SS_SUSP, 0);
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- ldisc_send(mk->ldisc, "abc\x1c", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08"));
- EXPECT(mk, specials, 2, SS_EL, 0, SS_ABORT, 0);
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- reset(mk);
- conf_set_bool(mk->conf, CONF_telnet_keyboard, false);
- ldisc_configure(mk->ldisc, mk->conf);
- /* Test UTF-8 characters of various lengths and ensure deleting
- * one deletes the whole character from the buffer (by pressing
- * Return and seeing what gets sent) but sends a number of BSBs
- * corresponding to the character's terminal width */
- mk->term->utf = true;
- ldisc_send(mk->ldisc, "\xC2\xA0\xC2\xA1", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\xC2\xA0\x0D\x0A"));
- reset(mk);
- ldisc_send(mk->ldisc, "\xE2\xA0\x80\xE2\xA0\x81", 6, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\xA0\x80\xE2\xA0\x81"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\xA0\x80\xE2\xA0\x81\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\xE2\xA0\x80\x0D\x0A"));
- reset(mk);
- ldisc_send(mk->ldisc, "\xF0\x90\x80\x80\xF0\x90\x80\x81", 8, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xF0\x90\x80\x80\xF0\x90\x80\x81"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xF0\x90\x80\x80\xF0\x90\x80\x81"
- "\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\xF0\x90\x80\x80\x0D\x0A"));
- reset(mk);
- /* Double-width characters (Hangul, as it happens) */
- ldisc_send(mk->ldisc, "\xEA\xB0\x80\xEA\xB0\x81", 6, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xEA\xB0\x80\xEA\xB0\x81"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xEA\xB0\x80\xEA\xB0\x81"
- "\x08 \x08\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\xEA\xB0\x80\x0D\x0A"));
- reset(mk);
- /* Zero-width characters */
- ldisc_send(mk->ldisc, "\xE2\x80\x8B\xE2\x80\x8B", 6, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\x80\x8B\xE2\x80\x8B"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\x80\x8B\xE2\x80\x8B"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\xE2\x80\x8B\x0D\x0A"));
- reset(mk);
- /* And reset back to non-UTF-8 mode and expect high-bit-set bytes
- * to be treated individually, as characters in a single-byte
- * charset. (In our case, given the test config, that will be
- * CP437, but it makes no difference to the editing behaviour.) */
- mk->term->utf = false;
- ldisc_send(mk->ldisc, "\xC2\xA0\xC2\xA1", 4, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1"));
- ldisc_send(mk->ldisc, "\x08", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL(""));
- EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1\x08 \x08"));
- ldisc_send(mk->ldisc, "\x0D", -1, false);
- EXPECT(mk, backend, PTRLEN_LITERAL("\xC2\xA0\xC2\x0D\x0A"));
- reset(mk);
- /* Make sure we flush all the terminal contents at the end of this
- * function */
- ldisc_send(mk->ldisc, "\x0D", 1, false);
- reset(mk);
- #undef EXPECT_TERMINAL
- }
- const struct BackendVtable *const backends[] = { &mock_backend_vt, NULL };
- int main(void)
- {
- Mock *mk = mock_new();
- mk->term = term_init(mk->conf, mk->ucsdata, &mk->tw);
- Ldisc *ldisc = ldisc_create(mk->conf, mk->term, &mk->backend, &mk->seat);
- term_size(mk->term, 80, 24, 0);
- test_noedit(mk);
- test_edit(mk, true);
- test_edit(mk, false);
- ldisc_free(ldisc);
- bool failed = mk->any_test_failed;
- mock_free(mk);
- if (failed) {
- printf("Test suite FAILED!\n");
- return 1;
- } else {
- printf("Test suite passed\n");
- return 0;
- }
- }
|