test_lineedit.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. #include "putty.h"
  2. #include "terminal.h"
  3. void modalfatalbox(const char *p, ...)
  4. {
  5. va_list ap;
  6. fprintf(stderr, "FATAL ERROR: ");
  7. va_start(ap, p);
  8. vfprintf(stderr, p, ap);
  9. va_end(ap);
  10. fputc('\n', stderr);
  11. exit(1);
  12. }
  13. const char *const appname = "test_lineedit";
  14. char *platform_default_s(const char *name)
  15. { return NULL; }
  16. bool platform_default_b(const char *name, bool def)
  17. { return def; }
  18. int platform_default_i(const char *name, int def)
  19. { return def; }
  20. FontSpec *platform_default_fontspec(const char *name)
  21. { return fontspec_new_default(); }
  22. Filename *platform_default_filename(const char *name)
  23. { return filename_from_str(""); }
  24. struct SpecialRecord {
  25. SessionSpecialCode code;
  26. int arg;
  27. };
  28. typedef struct Mock {
  29. Terminal *term;
  30. Ldisc *ldisc;
  31. Conf *conf;
  32. struct unicode_data ucsdata[1];
  33. bool echo, edit;
  34. strbuf *to_terminal, *to_backend;
  35. struct SpecialRecord *specials;
  36. size_t nspecials, specialsize;
  37. strbuf *context; /* for printing in failed tests */
  38. bool any_test_failed;
  39. TermWin tw;
  40. Seat seat;
  41. Backend backend;
  42. } Mock;
  43. static size_t mock_output(Seat *seat, SeatOutputType type,
  44. const void *data, size_t len)
  45. {
  46. Mock *mk = container_of(seat, Mock, seat);
  47. put_data(mk->to_terminal, data, len);
  48. return 0;
  49. }
  50. static void mock_send(Backend *be, const char *buf, size_t len)
  51. {
  52. Mock *mk = container_of(be, Mock, backend);
  53. put_data(mk->to_backend, buf, len);
  54. }
  55. static void mock_special(Backend *be, SessionSpecialCode code, int arg)
  56. {
  57. Mock *mk = container_of(be, Mock, backend);
  58. sgrowarray(mk->specials, mk->specialsize, mk->nspecials);
  59. mk->specials[mk->nspecials].code = code;
  60. mk->specials[mk->nspecials].arg = arg;
  61. mk->nspecials++;
  62. }
  63. static bool mock_ldisc_option_state(Backend *be, int option)
  64. {
  65. Mock *mk = container_of(be, Mock, backend);
  66. switch (option) {
  67. case LD_ECHO:
  68. return mk->echo;
  69. case LD_EDIT:
  70. return mk->edit;
  71. default:
  72. unreachable("bad ldisc option");
  73. }
  74. }
  75. static void mock_provide_ldisc(Backend *be, Ldisc *ldisc)
  76. {
  77. Mock *mk = container_of(be, Mock, backend);
  78. mk->ldisc = ldisc;
  79. }
  80. static bool mock_sendok(Backend *be)
  81. {
  82. Mock *mk = container_of(be, Mock, backend);
  83. (void)mk;
  84. /* FIXME: perhaps make this settable, to test the input_queue system? */
  85. return true;
  86. }
  87. static void mock_set_raw_mouse_mode(TermWin *win, bool enable) {}
  88. static void mock_palette_get_overrides(TermWin *tw, Terminal *term) {}
  89. static const TermWinVtable mock_termwin_vt = {
  90. .set_raw_mouse_mode = mock_set_raw_mouse_mode,
  91. .palette_get_overrides = mock_palette_get_overrides,
  92. };
  93. static const SeatVtable mock_seat_vt = {
  94. .output = mock_output,
  95. .echoedit_update = nullseat_echoedit_update,
  96. };
  97. static const BackendVtable mock_backend_vt = {
  98. .sendok = mock_sendok,
  99. .send = mock_send,
  100. .special = mock_special,
  101. .ldisc_option_state = mock_ldisc_option_state,
  102. .provide_ldisc = mock_provide_ldisc,
  103. .id = "mock",
  104. };
  105. static Mock *mock_new(void)
  106. {
  107. Mock *mk = snew(Mock);
  108. memset(mk, 0, sizeof(*mk));
  109. mk->conf = conf_new();
  110. do_defaults(NULL, mk->conf);
  111. init_ucs_generic(mk->conf, mk->ucsdata);
  112. mk->ucsdata->line_codepage = CP_437;
  113. mk->context = strbuf_new();
  114. mk->to_terminal = strbuf_new();
  115. mk->to_backend = strbuf_new();
  116. mk->tw.vt = &mock_termwin_vt;
  117. mk->seat.vt = &mock_seat_vt;
  118. mk->backend.vt = &mock_backend_vt;
  119. return mk;
  120. }
  121. static void mock_free(Mock *mk)
  122. {
  123. strbuf_free(mk->context);
  124. strbuf_free(mk->to_terminal);
  125. strbuf_free(mk->to_backend);
  126. conf_free(mk->conf);
  127. term_free(mk->term);
  128. sfree(mk->specials);
  129. sfree(mk);
  130. }
  131. static void reset(Mock *mk)
  132. {
  133. strbuf_clear(mk->context);
  134. strbuf_clear(mk->to_terminal);
  135. strbuf_clear(mk->to_backend);
  136. mk->nspecials = 0;
  137. }
  138. static void test_context(Mock *mk, const char *fmt, ...)
  139. {
  140. strbuf_clear(mk->context);
  141. va_list ap;
  142. va_start(ap, fmt);
  143. put_fmtv(mk->context, fmt, ap);
  144. va_end(ap);
  145. }
  146. static void print_context(Mock *mk, const char *file, int line)
  147. {
  148. printf("%s:%d", file, line);
  149. if (mk->context->len)
  150. printf(" (%s)", mk->context->s);
  151. printf(": ");
  152. }
  153. #define EXPECT(mk, what, ...) \
  154. expect_ ## what(mk, __FILE__, __LINE__, __VA_ARGS__)
  155. static void expect_backend(Mock *mk, const char *file, int line,
  156. ptrlen expected)
  157. {
  158. ptrlen actual = ptrlen_from_strbuf(mk->to_backend);
  159. if (!ptrlen_eq_ptrlen(expected, actual)) {
  160. print_context(mk, file, line);
  161. printf("expected backend output \"");
  162. write_c_string_literal(stdout, expected);
  163. printf("\", got \"");
  164. write_c_string_literal(stdout, actual);
  165. printf("\"\n");
  166. mk->any_test_failed = true;
  167. }
  168. }
  169. static void expect_terminal(Mock *mk, const char *file, int line,
  170. ptrlen expected)
  171. {
  172. ptrlen actual = ptrlen_from_strbuf(mk->to_terminal);
  173. if (!ptrlen_eq_ptrlen(expected, actual)) {
  174. print_context(mk, file, line);
  175. printf("expected terminal output \"");
  176. write_c_string_literal(stdout, expected);
  177. printf("\", got \"");
  178. write_c_string_literal(stdout, actual);
  179. printf("\"\n");
  180. mk->any_test_failed = true;
  181. }
  182. }
  183. static void expect_specials(Mock *mk, const char *file, int line,
  184. size_t nspecials, ...)
  185. {
  186. va_list ap;
  187. static const char *const special_names[] = {
  188. #define SPECIAL(x) #x,
  189. #include "specials.h"
  190. #undef SPECIAL
  191. };
  192. bool match;
  193. if (nspecials != mk->nspecials) {
  194. match = false;
  195. } else {
  196. match = true;
  197. va_start(ap, nspecials);
  198. for (size_t i = 0; i < nspecials; i++) {
  199. SessionSpecialCode code = va_arg(ap, SessionSpecialCode);
  200. int arg = va_arg(ap, int);
  201. if (code != mk->specials[i].code || arg != mk->specials[i].arg)
  202. match = false;
  203. }
  204. va_end(ap);
  205. }
  206. if (!match) {
  207. print_context(mk, file, line);
  208. printf("expected specials [");
  209. va_start(ap, nspecials);
  210. for (size_t i = 0; i < nspecials; i++) {
  211. SessionSpecialCode code = va_arg(ap, SessionSpecialCode);
  212. int arg = va_arg(ap, int);
  213. printf(" %s.%d", special_names[code], arg);
  214. }
  215. va_end(ap);
  216. printf(" ], got [");
  217. for (size_t i = 0; i < mk->nspecials; i++) {
  218. printf(" %s.%d", special_names[mk->specials[i].code],
  219. mk->specials[i].arg);
  220. }
  221. printf(" ]\n");
  222. mk->any_test_failed = true;
  223. }
  224. }
  225. static void test_noedit(Mock *mk)
  226. {
  227. mk->edit = false;
  228. mk->echo = false;
  229. /*
  230. * In non-echo and non-edit mode, the channel is 8-bit clean
  231. */
  232. for (unsigned c = 0; c < 256; c++) {
  233. char buf[1];
  234. test_context(mk, "c=%02x", c);
  235. buf[0] = c;
  236. ldisc_send(mk->ldisc, buf, 1, false);
  237. EXPECT(mk, backend, make_ptrlen(buf, 1));
  238. reset(mk);
  239. }
  240. /* ... regardless of the 'interactive' flag */
  241. for (unsigned c = 0; c < 256; c++) {
  242. char buf[1];
  243. test_context(mk, "c=%02x", c);
  244. buf[0] = c;
  245. ldisc_send(mk->ldisc, buf, 1, true);
  246. EXPECT(mk, backend, make_ptrlen(buf, 1));
  247. reset(mk);
  248. }
  249. /* ... and any nonzero character does the same thing even if sent
  250. * with the magic -1 length flag */
  251. for (unsigned c = 1; c < 256; c++) {
  252. char buf[2];
  253. test_context(mk, "c=%02x", c);
  254. buf[0] = c;
  255. buf[1] = '\0';
  256. ldisc_send(mk->ldisc, buf, -1, true);
  257. EXPECT(mk, backend, make_ptrlen(buf, 1));
  258. reset(mk);
  259. }
  260. /*
  261. * Test the special-character cases for Telnet.
  262. */
  263. conf_set_int(mk->conf, CONF_protocol, PROT_TELNET);
  264. conf_set_bool(mk->conf, CONF_telnet_newline, false);
  265. conf_set_bool(mk->conf, CONF_telnet_keyboard, false);
  266. ldisc_configure(mk->ldisc, mk->conf);
  267. /* Without telnet_newline or telnet_keyboard, these all do the
  268. * normal thing */
  269. ldisc_send(mk->ldisc, "\x0D", -1, true);
  270. EXPECT(mk, backend, PTRLEN_LITERAL("\x0D"));
  271. reset(mk);
  272. ldisc_send(mk->ldisc, "\x08", -1, true);
  273. EXPECT(mk, backend, PTRLEN_LITERAL("\x08"));
  274. reset(mk);
  275. ldisc_send(mk->ldisc, "\x7F", -1, true);
  276. EXPECT(mk, backend, PTRLEN_LITERAL("\x7F"));
  277. reset(mk);
  278. ldisc_send(mk->ldisc, "\x03", -1, true);
  279. EXPECT(mk, backend, PTRLEN_LITERAL("\x03"));
  280. reset(mk);
  281. ldisc_send(mk->ldisc, "\x1A", -1, true);
  282. EXPECT(mk, backend, PTRLEN_LITERAL("\x1A"));
  283. reset(mk);
  284. /* telnet_newline controls translation of CR into SS_EOL */
  285. conf_set_bool(mk->conf, CONF_telnet_newline, true);
  286. ldisc_configure(mk->ldisc, mk->conf);
  287. ldisc_send(mk->ldisc, "\x0D", -1, true);
  288. EXPECT(mk, specials, 1, SS_EOL, 0);
  289. reset(mk);
  290. /* And telnet_keyboard controls the others */
  291. conf_set_bool(mk->conf, CONF_telnet_newline, false);
  292. conf_set_bool(mk->conf, CONF_telnet_keyboard, true);
  293. ldisc_configure(mk->ldisc, mk->conf);
  294. ldisc_send(mk->ldisc, "\x08", -1, true);
  295. EXPECT(mk, specials, 1, SS_EC, 0);
  296. reset(mk);
  297. ldisc_send(mk->ldisc, "\x7F", -1, true);
  298. EXPECT(mk, specials, 1, SS_EC, 0);
  299. reset(mk);
  300. ldisc_send(mk->ldisc, "\x03", -1, true);
  301. EXPECT(mk, specials, 1, SS_IP, 0);
  302. reset(mk);
  303. ldisc_send(mk->ldisc, "\x1A", -1, true);
  304. EXPECT(mk, specials, 1, SS_SUSP, 0);
  305. reset(mk);
  306. /*
  307. * In echo-but-no-edit mode, we also expect that every character
  308. * is echoed back to the display as a side effect, including when
  309. * it's sent as a special -1 keystroke.
  310. *
  311. * This state only comes up in Telnet, because that has protocol
  312. * options to independently configure echo and edit. Telnet is
  313. * also the most complicated of the protocols because of the above
  314. * special cases, so we stay in Telnet mode for this test.
  315. */
  316. mk->echo = true;
  317. for (unsigned c = 0; c < 256; c++) {
  318. char buf[1];
  319. test_context(mk, "c=%02x", c);
  320. buf[0] = c;
  321. ldisc_send(mk->ldisc, buf, 1, false);
  322. EXPECT(mk, terminal, make_ptrlen(buf, 1));
  323. reset(mk);
  324. }
  325. for (unsigned c = 1; c < 256; c++) {
  326. char buf[2];
  327. test_context(mk, "c=%02x", c);
  328. buf[0] = c;
  329. buf[1] = '\0';
  330. ldisc_send(mk->ldisc, buf, -1, true);
  331. EXPECT(mk, terminal, make_ptrlen(buf, 1));
  332. reset(mk);
  333. }
  334. do_defaults(NULL, mk->conf);
  335. ldisc_configure(mk->ldisc, mk->conf);
  336. }
  337. static void test_edit(Mock *mk, bool echo)
  338. {
  339. static const char *const ctls = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
  340. mk->edit = true;
  341. mk->echo = echo;
  342. #define EXPECT_TERMINAL(mk, val) do { \
  343. if (!echo) EXPECT(mk, terminal, PTRLEN_LITERAL("")); \
  344. else EXPECT(mk, terminal, val); \
  345. } while (0)
  346. /* ASCII printing characters all print when entered, but don't go
  347. * to the terminal until Return is pressed */
  348. for (unsigned c = 0x20; c < 0x7F; c++) {
  349. char buf[3];
  350. test_context(mk, "c=%02x", c);
  351. buf[0] = c;
  352. ldisc_send(mk->ldisc, buf, 1, false);
  353. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  354. EXPECT_TERMINAL(mk, make_ptrlen(buf, 1));
  355. ldisc_send(mk->ldisc, "\015", 1, false);
  356. buf[1] = '\015';
  357. buf[2] = '\012';
  358. EXPECT(mk, backend, make_ptrlen(buf, 3));
  359. EXPECT_TERMINAL(mk, make_ptrlen(buf, 3));
  360. reset(mk);
  361. }
  362. /* C0 control characters mostly show up as ^X or similar */
  363. for (unsigned c = 0; c < 0x1F; c++) {
  364. char backbuf[3];
  365. char termbuf[4];
  366. switch (ctls[c]) {
  367. case 'D': continue; /* ^D sends EOF */
  368. case 'M': continue; /* ^M is Return */
  369. case 'R': continue; /* ^R redisplays line */
  370. case 'U': continue; /* ^U deletes the line */
  371. case 'V': continue; /* ^V makes the next char literal */
  372. case 'W': continue; /* ^W deletes a word */
  373. /*
  374. * ^H / ^? are not included here. Those are treated
  375. * literally if sent as plain input bytes. Only sending
  376. * them as special via length==-1 causes them to act as
  377. * backspace, which I think was simply because there _is_
  378. * a dedicated key that can do that function, so there's
  379. * no need to also eat the Ctrl+thing combo.
  380. */
  381. /*
  382. * Also, ^C, ^Z and ^\ self-insert (when not in Telnet
  383. * mode) but have the side effect of erasing the line
  384. * buffer so far. In this loop, that doesn't show up,
  385. * because the line buffer is empty already. However, I
  386. * don't test that, because it's silly, and probably
  387. * doesn't want to keep happening!
  388. */
  389. }
  390. test_context(mk, "c=%02x", c);
  391. backbuf[0] = c;
  392. ldisc_send(mk->ldisc, backbuf, 1, false);
  393. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  394. termbuf[0] = '^';
  395. termbuf[1] = ctls[c];
  396. EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 2));
  397. ldisc_send(mk->ldisc, "\015", 1, false);
  398. backbuf[1] = '\015';
  399. backbuf[2] = '\012';
  400. EXPECT(mk, backend, make_ptrlen(backbuf, 3));
  401. termbuf[2] = '\015';
  402. termbuf[3] = '\012';
  403. EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 4));
  404. reset(mk);
  405. }
  406. /* Prefixed with ^V, the same is true of _all_ C0 controls */
  407. for (unsigned c = 0; c < 0x1F; c++) {
  408. char backbuf[3];
  409. char termbuf[4];
  410. test_context(mk, "c=%02x", c);
  411. backbuf[0] = 'V' & 0x1F;
  412. ldisc_send(mk->ldisc, backbuf, 1, false);
  413. backbuf[0] = c;
  414. ldisc_send(mk->ldisc, backbuf, 1, false);
  415. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  416. termbuf[0] = '^';
  417. termbuf[1] = ctls[c];
  418. EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 2));
  419. ldisc_send(mk->ldisc, "\015", 1, false);
  420. backbuf[1] = '\015';
  421. backbuf[2] = '\012';
  422. EXPECT(mk, backend, make_ptrlen(backbuf, 3));
  423. termbuf[2] = '\015';
  424. termbuf[3] = '\012';
  425. EXPECT_TERMINAL(mk, make_ptrlen(termbuf, 4));
  426. reset(mk);
  427. }
  428. /* Deleting an ASCII character sends a single BSB and deletes just
  429. * that byte from the buffer */
  430. ldisc_send(mk->ldisc, "ab", 2, false);
  431. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  432. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("ab"));
  433. ldisc_send(mk->ldisc, "\x08", -1, false);
  434. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  435. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("ab\x08 \x08"));
  436. ldisc_send(mk->ldisc, "\x0D", -1, false);
  437. EXPECT(mk, backend, PTRLEN_LITERAL("a\x0D\x0A"));
  438. reset(mk);
  439. /* Deleting a character written as a ^X code sends two BSBs to
  440. * wipe out the two-character display sequence */
  441. ldisc_send(mk->ldisc, "a\x02", 2, false);
  442. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  443. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^B"));
  444. ldisc_send(mk->ldisc, "\x7F", -1, false);
  445. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  446. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^B\x08 \x08\x08 \x08"));
  447. ldisc_send(mk->ldisc, "\x0D", -1, false);
  448. EXPECT(mk, backend, PTRLEN_LITERAL("a\x0D\x0A"));
  449. reset(mk);
  450. /* ^D sends the line editing buffer without a trailing Return, if
  451. * it's non-empty */
  452. ldisc_send(mk->ldisc, "abc\x04", 4, false);
  453. EXPECT(mk, backend, PTRLEN_LITERAL("abc"));
  454. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc"));
  455. ldisc_send(mk->ldisc, "\x0D", -1, false);
  456. EXPECT(mk, backend, PTRLEN_LITERAL("abc\x0D\x0A"));
  457. reset(mk);
  458. /* But if the buffer is empty, ^D sends SS_EOF */
  459. ldisc_send(mk->ldisc, "\x04", 1, false);
  460. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  461. EXPECT_TERMINAL(mk, PTRLEN_LITERAL(""));
  462. EXPECT(mk, specials, 1, SS_EOF, 0);
  463. reset(mk);
  464. /* ^R redraws the current line, after printing "^R" at the end of
  465. * the previous attempt to make it clear that that's what
  466. * happened */
  467. ldisc_send(mk->ldisc, "a\x01", 2, false);
  468. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  469. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^A"));
  470. ldisc_send(mk->ldisc, "\x12", 1, false);
  471. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  472. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^A^R\x0D\x0A" "a^A"));
  473. ldisc_send(mk->ldisc, "\x0D", -1, false);
  474. reset(mk);
  475. /* ^U deletes the whole line */
  476. ldisc_send(mk->ldisc, "a b c", 5, false);
  477. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  478. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a b c"));
  479. ldisc_send(mk->ldisc, "\x15", 1, false);
  480. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  481. EXPECT_TERMINAL(
  482. mk, PTRLEN_LITERAL(
  483. "a b c\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  484. ldisc_send(mk->ldisc, "\x0D", -1, false);
  485. EXPECT(mk, backend, PTRLEN_LITERAL("\x0D\x0A"));
  486. EXPECT_TERMINAL(
  487. mk, PTRLEN_LITERAL(
  488. "a b c\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x0D\x0A"));
  489. reset(mk);
  490. /* And it still knows that a control character written as ^X takes
  491. * two BSBs to delete */
  492. ldisc_send(mk->ldisc, "a\x02" "c", 3, false);
  493. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  494. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^Bc"));
  495. ldisc_send(mk->ldisc, "\x15", 1, false);
  496. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  497. EXPECT_TERMINAL(
  498. mk, PTRLEN_LITERAL("a^Bc\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  499. ldisc_send(mk->ldisc, "\x0D", -1, false);
  500. reset(mk);
  501. /* ^W deletes a word, which means that it deletes to the most
  502. * recent boundary with a space on the left and a nonspace on the
  503. * right. (Or the beginning of the string, whichever comes first.) */
  504. ldisc_send(mk->ldisc, "hello, world\x17", 13, false);
  505. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  506. EXPECT_TERMINAL(
  507. mk, PTRLEN_LITERAL(
  508. "hello, world\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  509. ldisc_send(mk->ldisc, "\x0D", 1, false);
  510. EXPECT(mk, backend, PTRLEN_LITERAL("hello, \x0D\x0A"));
  511. reset(mk);
  512. ldisc_send(mk->ldisc, "hello, world \x17", 14, false);
  513. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  514. EXPECT_TERMINAL(
  515. mk, PTRLEN_LITERAL(
  516. "hello, world "
  517. "\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  518. ldisc_send(mk->ldisc, "\x0D", 1, false);
  519. EXPECT(mk, backend, PTRLEN_LITERAL("hello, \x0D\x0A"));
  520. reset(mk);
  521. ldisc_send(mk->ldisc, " hello \x17", 8, false);
  522. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  523. EXPECT_TERMINAL(
  524. mk, PTRLEN_LITERAL(
  525. " hello \x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  526. ldisc_send(mk->ldisc, "\x0D", 1, false);
  527. EXPECT(mk, backend, PTRLEN_LITERAL(" \x0D\x0A"));
  528. reset(mk);
  529. ldisc_send(mk->ldisc, "hello \x17", 7, false);
  530. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  531. EXPECT_TERMINAL(
  532. mk, PTRLEN_LITERAL(
  533. "hello \x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  534. ldisc_send(mk->ldisc, "\x0D", 1, false);
  535. EXPECT(mk, backend, PTRLEN_LITERAL("\x0D\x0A"));
  536. reset(mk);
  537. /* And this too knows that a control character written as ^X takes
  538. * two BSBs to delete */
  539. ldisc_send(mk->ldisc, "a\x02" "c", 3, false);
  540. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  541. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("a^Bc"));
  542. ldisc_send(mk->ldisc, "\x17", 1, false);
  543. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  544. EXPECT_TERMINAL(
  545. mk, PTRLEN_LITERAL("a^Bc\x08 \x08\x08 \x08\x08 \x08\x08 \x08"));
  546. ldisc_send(mk->ldisc, "\x0D", -1, false);
  547. reset(mk);
  548. /* Test handling of ^C and friends in non-telnet_keyboard mode */
  549. ldisc_send(mk->ldisc, "abc\x03", 4, false);
  550. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  551. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08^C"));
  552. EXPECT(mk, specials, 1, SS_EL, 0);
  553. ldisc_send(mk->ldisc, "\x0D", -1, false);
  554. reset(mk);
  555. ldisc_send(mk->ldisc, "abc\x1a", 4, false);
  556. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  557. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08^Z"));
  558. EXPECT(mk, specials, 1, SS_EL, 0);
  559. ldisc_send(mk->ldisc, "\x0D", -1, false);
  560. reset(mk);
  561. ldisc_send(mk->ldisc, "abc\x1c", 4, false);
  562. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  563. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08^\\"));
  564. EXPECT(mk, specials, 1, SS_EL, 0);
  565. ldisc_send(mk->ldisc, "\x0D", -1, false);
  566. reset(mk);
  567. /* And in telnet_keyboard mode */
  568. conf_set_bool(mk->conf, CONF_telnet_keyboard, true);
  569. ldisc_configure(mk->ldisc, mk->conf);
  570. /* FIXME: should we _really_ be sending EL before each of these? */
  571. ldisc_send(mk->ldisc, "abc\x03", 4, false);
  572. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  573. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08"));
  574. EXPECT(mk, specials, 2, SS_EL, 0, SS_IP, 0);
  575. ldisc_send(mk->ldisc, "\x0D", -1, false);
  576. reset(mk);
  577. ldisc_send(mk->ldisc, "abc\x1a", 4, false);
  578. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  579. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08"));
  580. EXPECT(mk, specials, 2, SS_EL, 0, SS_SUSP, 0);
  581. ldisc_send(mk->ldisc, "\x0D", -1, false);
  582. reset(mk);
  583. ldisc_send(mk->ldisc, "abc\x1c", 4, false);
  584. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  585. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("abc\x08 \x08\x08 \x08\x08 \x08"));
  586. EXPECT(mk, specials, 2, SS_EL, 0, SS_ABORT, 0);
  587. ldisc_send(mk->ldisc, "\x0D", -1, false);
  588. reset(mk);
  589. conf_set_bool(mk->conf, CONF_telnet_keyboard, false);
  590. ldisc_configure(mk->ldisc, mk->conf);
  591. /* Test UTF-8 characters of various lengths and ensure deleting
  592. * one deletes the whole character from the buffer (by pressing
  593. * Return and seeing what gets sent) but sends a number of BSBs
  594. * corresponding to the character's terminal width */
  595. mk->term->utf = true;
  596. ldisc_send(mk->ldisc, "\xC2\xA0\xC2\xA1", 4, false);
  597. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  598. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1"));
  599. ldisc_send(mk->ldisc, "\x08", -1, false);
  600. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  601. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1\x08 \x08"));
  602. ldisc_send(mk->ldisc, "\x0D", -1, false);
  603. EXPECT(mk, backend, PTRLEN_LITERAL("\xC2\xA0\x0D\x0A"));
  604. reset(mk);
  605. ldisc_send(mk->ldisc, "\xE2\xA0\x80\xE2\xA0\x81", 6, false);
  606. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  607. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\xA0\x80\xE2\xA0\x81"));
  608. ldisc_send(mk->ldisc, "\x08", -1, false);
  609. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  610. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\xA0\x80\xE2\xA0\x81\x08 \x08"));
  611. ldisc_send(mk->ldisc, "\x0D", -1, false);
  612. EXPECT(mk, backend, PTRLEN_LITERAL("\xE2\xA0\x80\x0D\x0A"));
  613. reset(mk);
  614. ldisc_send(mk->ldisc, "\xF0\x90\x80\x80\xF0\x90\x80\x81", 8, false);
  615. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  616. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xF0\x90\x80\x80\xF0\x90\x80\x81"));
  617. ldisc_send(mk->ldisc, "\x08", -1, false);
  618. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  619. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xF0\x90\x80\x80\xF0\x90\x80\x81"
  620. "\x08 \x08"));
  621. ldisc_send(mk->ldisc, "\x0D", -1, false);
  622. EXPECT(mk, backend, PTRLEN_LITERAL("\xF0\x90\x80\x80\x0D\x0A"));
  623. reset(mk);
  624. /* Double-width characters (Hangul, as it happens) */
  625. ldisc_send(mk->ldisc, "\xEA\xB0\x80\xEA\xB0\x81", 6, false);
  626. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  627. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xEA\xB0\x80\xEA\xB0\x81"));
  628. ldisc_send(mk->ldisc, "\x08", -1, false);
  629. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  630. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xEA\xB0\x80\xEA\xB0\x81"
  631. "\x08 \x08\x08 \x08"));
  632. ldisc_send(mk->ldisc, "\x0D", -1, false);
  633. EXPECT(mk, backend, PTRLEN_LITERAL("\xEA\xB0\x80\x0D\x0A"));
  634. reset(mk);
  635. /* Zero-width characters */
  636. ldisc_send(mk->ldisc, "\xE2\x80\x8B\xE2\x80\x8B", 6, false);
  637. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  638. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\x80\x8B\xE2\x80\x8B"));
  639. ldisc_send(mk->ldisc, "\x08", -1, false);
  640. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  641. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xE2\x80\x8B\xE2\x80\x8B"));
  642. ldisc_send(mk->ldisc, "\x0D", -1, false);
  643. EXPECT(mk, backend, PTRLEN_LITERAL("\xE2\x80\x8B\x0D\x0A"));
  644. reset(mk);
  645. /* And reset back to non-UTF-8 mode and expect high-bit-set bytes
  646. * to be treated individually, as characters in a single-byte
  647. * charset. (In our case, given the test config, that will be
  648. * CP437, but it makes no difference to the editing behaviour.) */
  649. mk->term->utf = false;
  650. ldisc_send(mk->ldisc, "\xC2\xA0\xC2\xA1", 4, false);
  651. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  652. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1"));
  653. ldisc_send(mk->ldisc, "\x08", -1, false);
  654. EXPECT(mk, backend, PTRLEN_LITERAL(""));
  655. EXPECT_TERMINAL(mk, PTRLEN_LITERAL("\xC2\xA0\xC2\xA1\x08 \x08"));
  656. ldisc_send(mk->ldisc, "\x0D", -1, false);
  657. EXPECT(mk, backend, PTRLEN_LITERAL("\xC2\xA0\xC2\x0D\x0A"));
  658. reset(mk);
  659. /* Make sure we flush all the terminal contents at the end of this
  660. * function */
  661. ldisc_send(mk->ldisc, "\x0D", 1, false);
  662. reset(mk);
  663. #undef EXPECT_TERMINAL
  664. }
  665. const struct BackendVtable *const backends[] = { &mock_backend_vt, NULL };
  666. int main(void)
  667. {
  668. Mock *mk = mock_new();
  669. mk->term = term_init(mk->conf, mk->ucsdata, &mk->tw);
  670. Ldisc *ldisc = ldisc_create(mk->conf, mk->term, &mk->backend, &mk->seat);
  671. term_size(mk->term, 80, 24, 0);
  672. test_noedit(mk);
  673. test_edit(mk, true);
  674. test_edit(mk, false);
  675. ldisc_free(ldisc);
  676. bool failed = mk->any_test_failed;
  677. mock_free(mk);
  678. if (failed) {
  679. printf("Test suite FAILED!\n");
  680. return 1;
  681. } else {
  682. printf("Test suite passed\n");
  683. return 0;
  684. }
  685. }