123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- #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(""); }
- const struct BackendVtable *const backends[] = { NULL };
- typedef struct Mock {
- Terminal *term;
- Conf *conf;
- struct unicode_data ucsdata[1];
- strbuf *context;
- bool any_test_failed;
- TermWin tw;
- } Mock;
- static bool mock_setup_draw_ctx(TermWin *win) { return false; }
- static void mock_draw_text(TermWin *win, int x, int y, wchar_t *text, int len,
- unsigned long attrs, int lattrs, truecolour tc) {}
- static void mock_draw_cursor(TermWin *win, int x, int y, wchar_t *text,
- int len, unsigned long attrs, int lattrs,
- truecolour tc) {}
- static void mock_set_raw_mouse_mode(TermWin *win, bool enable) {}
- static void mock_set_raw_mouse_mode_pointer(TermWin *win, bool enable) {}
- static void mock_palette_set(TermWin *win, unsigned start, unsigned ncolours,
- const rgb *colours) {}
- static void mock_palette_get_overrides(TermWin *tw, Terminal *term) {}
- static const TermWinVtable mock_termwin_vt = {
- .setup_draw_ctx = mock_setup_draw_ctx,
- .draw_text = mock_draw_text,
- .draw_cursor = mock_draw_cursor,
- .set_raw_mouse_mode = mock_set_raw_mouse_mode,
- .set_raw_mouse_mode_pointer = mock_set_raw_mouse_mode_pointer,
- .palette_set = mock_palette_set,
- .palette_get_overrides = mock_palette_get_overrides,
- };
- 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_ISO8859_1;
- mk->context = strbuf_new();
- mk->tw.vt = &mock_termwin_vt;
- return mk;
- }
- static void mock_free(Mock *mk)
- {
- strbuf_free(mk->context);
- conf_free(mk->conf);
- term_free(mk->term);
- sfree(mk);
- }
- static void reset(Mock *mk)
- {
- term_pwron(mk->term, true);
- term_size(mk->term, 24, 80, 0);
- term_set_trust_status(mk->term, false);
- strbuf_clear(mk->context);
- }
- #if 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);
- }
- #endif
- static void report_fail(Mock *mk, const char *file, int line,
- const char *fmt, ...)
- {
- printf("%s:%d", file, line);
- if (mk->context->len)
- printf(" (%s)", mk->context->s);
- printf(": ");
- va_list ap;
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
- printf("\n");
- mk->any_test_failed = true;
- }
- static inline void check_iequal(Mock *mk, const char *file, int line,
- long long lhs, long long rhs)
- {
- if (lhs != rhs)
- report_fail(mk, file, line, "%lld != %lld / %#llx != %#llx",
- lhs, rhs, lhs, rhs);
- }
- #define IEQUAL(lhs, rhs) check_iequal(mk, __FILE__, __LINE__, lhs, rhs)
- static inline void term_datapl(Terminal *term, ptrlen pl)
- {
- term_data(term, pl.ptr, pl.len);
- }
- static struct termchar get_termchar(Terminal *term, int x, int y)
- {
- termline *tl = term_get_line(term, y);
- termchar tc;
- if (0 <= x && x < tl->cols)
- tc = tl->chars[x];
- else
- tc = term->erase_char;
- term_release_line(tl);
- return tc;
- }
- static unsigned short get_lineattr(Terminal *term, int y)
- {
- termline *tl = term_get_line(term, y);
- unsigned short lattr = tl->lattr;
- term_release_line(tl);
- return lattr;
- }
- static void test_hello_world(Mock *mk)
- {
- /* A trivial test just to kick off this test framework */
- mk->ucsdata->line_codepage = CP_ISO8859_1;
- reset(mk);
- term_datapl(mk->term, PTRLEN_LITERAL("hello, world"));
- IEQUAL(mk->term->curs.x, 12);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(get_termchar(mk->term, 0, 0).chr, CSET_ASCII | 'h');
- IEQUAL(get_termchar(mk->term, 1, 0).chr, CSET_ASCII | 'e');
- IEQUAL(get_termchar(mk->term, 2, 0).chr, CSET_ASCII | 'l');
- IEQUAL(get_termchar(mk->term, 3, 0).chr, CSET_ASCII | 'l');
- IEQUAL(get_termchar(mk->term, 4, 0).chr, CSET_ASCII | 'o');
- IEQUAL(get_termchar(mk->term, 5, 0).chr, CSET_ASCII | ',');
- IEQUAL(get_termchar(mk->term, 6, 0).chr, CSET_ASCII | ' ');
- IEQUAL(get_termchar(mk->term, 7, 0).chr, CSET_ASCII | 'w');
- IEQUAL(get_termchar(mk->term, 8, 0).chr, CSET_ASCII | 'o');
- IEQUAL(get_termchar(mk->term, 9, 0).chr, CSET_ASCII | 'r');
- IEQUAL(get_termchar(mk->term, 10, 0).chr, CSET_ASCII | 'l');
- IEQUAL(get_termchar(mk->term, 11, 0).chr, CSET_ASCII | 'd');
- }
- static void test_wrap(Mock *mk)
- {
- /* Test behaviour when printing characters wrap to the next line */
- mk->ucsdata->line_codepage = CP_UTF8;
- /* Print 'abc' without enough space for the c, in wrapping mode */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* The 'a' prints without anything unusual happening */
- term_datapl(mk->term, PTRLEN_LITERAL("a"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- /* The 'b' prints, leaving the cursor where it is with wrapnext set */
- term_datapl(mk->term, PTRLEN_LITERAL("b"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 1);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
- /* And now the 'c' causes a deferred wrap and goes to the next line */
- term_datapl(mk->term, PTRLEN_LITERAL("c"));
- IEQUAL(mk->term->curs.x, 1);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
- IEQUAL(get_termchar(mk->term, 0, 1).chr, CSET_ASCII | 'c');
- /* If we backspace once, the cursor moves back on to the c */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- /* Now backspace again, and the cursor returns to the b */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /* Now try it with a double-width character in place of ab */
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* The DW character goes directly to the wrapnext state */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 1);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
- /* And the 'c' causes a deferred wrap as before */
- term_datapl(mk->term, PTRLEN_LITERAL("c"));
- IEQUAL(mk->term->curs.x, 1);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
- IEQUAL(get_termchar(mk->term, 0, 1).chr, CSET_ASCII | 'c');
- /* If we backspace once, the cursor moves back on to the c */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- /* Now backspace again, and the cursor goes to the RHS of the DW char */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /* Now put the DW character in place of bc */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* The 'a' prints as before */
- term_datapl(mk->term, PTRLEN_LITERAL("a"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- /* The DW character wraps, setting LATTR_WRAPPED2 */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
- IEQUAL(mk->term->curs.x, 2);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED | LATTR_WRAPPED2);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | ' ');
- IEQUAL(get_termchar(mk->term, 0, 1).chr, 0xAC00);
- IEQUAL(get_termchar(mk->term, 1, 1).chr, UCSWIDE);
- /* If we backspace once, cursor moves to the RHS of the DW char */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 1);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- /* Backspace again, and cursor moves from RHS to LHS of that char */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- /* Now backspace again, and the cursor skips the empty column so
- * that it can return to the previous logical character, to wit, the a */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 78);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /* Print 'ab' up to the rightmost column, and then backspace */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* As before, the 'ab' put us in the rightmost column with wrapnext set */
- term_datapl(mk->term, PTRLEN_LITERAL("ab"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 1);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
- /* Backspacing just clears the wrapnext flag, so we're logically
- * back on the b again */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /* For completeness, the easy case: just print 'a' then backspace */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* 'a' printed in column n-1 takes us to column n */
- term_datapl(mk->term, PTRLEN_LITERAL("a"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- /* Backspacing moves us back a space on to the a */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 78);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /*
- * Now test the special cases that arise when the terminal is only
- * one column wide!
- */
- reset(mk);
- term_size(mk->term, 24, 1, 0);
- mk->term->curs.x = 0;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* Printing a single-width character takes us into wrapnext immediately */
- term_datapl(mk->term, PTRLEN_LITERAL("a"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 1);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 0, 0).chr, CSET_ASCII | 'a');
- /* Printing a second one wraps, and takes us _back_ to wrapnext */
- term_datapl(mk->term, PTRLEN_LITERAL("b"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 1);
- IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED);
- IEQUAL(get_termchar(mk->term, 0, 0).chr, CSET_ASCII | 'a');
- IEQUAL(get_termchar(mk->term, 0, 1).chr, CSET_ASCII | 'b');
- /* Backspacing once clears the wrapnext flag, putting us on the b */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 1);
- IEQUAL(mk->term->wrapnext, 0);
- /* Backspacing again returns to the previous line, putting us on the a */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /* And now try with a double-width character */
- reset(mk);
- term_size(mk->term, 24, 1, 0);
- mk->term->curs.x = 0;
- mk->term->curs.y = 0;
- mk->term->wrap = true;
- /* DW character won't fit at all, so it transforms into U+FFFD
- * REPLACEMENT CHARACTER and then behaves like a SW char */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
- IEQUAL(mk->term->curs.x, 0);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 1);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 0, 0).chr, 0xFFFD);
- }
- static void test_nonwrap(Mock *mk)
- {
- /* Test behaviour when printing characters hit end of line without wrap.
- * The wrapnext flag is never set in this mode. */
- mk->ucsdata->line_codepage = CP_UTF8;
- /* Print 'abc' without enough space for the c */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = false;
- /* The 'a' prints without anything unusual happening */
- term_datapl(mk->term, PTRLEN_LITERAL("a"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- /* The 'b' prints, leaving the cursor where it is */
- term_datapl(mk->term, PTRLEN_LITERAL("b"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
- /* The 'c' overwrites the b */
- term_datapl(mk->term, PTRLEN_LITERAL("c"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'c');
- /* Since wrapnext was never set, backspacing returns us to the a */
- term_datapl(mk->term, PTRLEN_LITERAL("\b"));
- IEQUAL(mk->term->curs.x, 78);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- /* Now try it with a double-width character in place of ab */
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = false;
- /* The DW character occupies the rightmost two columns */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
- /* The 'c' must overprint the RHS of the DW char, clearing the LHS */
- term_datapl(mk->term, PTRLEN_LITERAL("c"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | ' ');
- IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'c');
- /* Now put the DW char in place of the bc */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = false;
- /* The 'a' prints as before */
- term_datapl(mk->term, PTRLEN_LITERAL("a"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- /* The DW char won't fit, so turns into U+FFFD REPLACEMENT CHARACTER */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
- IEQUAL(get_termchar(mk->term, 79, 0).chr, 0xFFFD);
- /* Just for completeness, try both of those together */
- reset(mk);
- mk->term->curs.x = 78;
- mk->term->curs.y = 0;
- mk->term->wrap = false;
- /* First DW character occupies the rightmost columns */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
- IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
- /* Second DW char becomes U+FFFD, overwriting RHS of the first one */
- term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x81"));
- IEQUAL(mk->term->curs.x, 79);
- IEQUAL(mk->term->curs.y, 0);
- IEQUAL(mk->term->wrapnext, 0);
- IEQUAL(get_lineattr(mk->term, 0), 0);
- IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | ' ');
- IEQUAL(get_termchar(mk->term, 79, 0).chr, 0xFFFD);
- }
- int main(void)
- {
- Mock *mk = mock_new();
- mk->term = term_init(mk->conf, mk->ucsdata, &mk->tw);
- test_hello_world(mk);
- test_wrap(mk);
- test_nonwrap(mk);
- 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;
- }
- }
|