diff --git a/terminal/terminal.h b/terminal/terminal.h index 00f0208b..c8b54e59 100644 --- a/terminal/terminal.h +++ b/terminal/terminal.h @@ -1,9 +1,6 @@ /* - * Internals of the Terminal structure, for those other modules - * which need to look inside it. It would be nice if this could be - * folded back into terminal.c in future, with an abstraction layer - * to handle everything that other modules need to know about it; - * but for the moment, this will do. + * Internals of the Terminal structure, used by other modules in the + * terminal subdirectory and by test suites. */ #ifndef PUTTY_TERMINAL_H diff --git a/test/test_terminal.c b/test/test_terminal.c new file mode 100644 index 00000000..0f068c1c --- /dev/null +++ b/test/test_terminal.c @@ -0,0 +1,182 @@ +#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; + + 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"); +} + +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 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'); +} + +int main(void) +{ + Mock *mk = mock_new(); + mk->term = term_init(mk->conf, mk->ucsdata, &mk->tw); + + test_hello_world(mk); + + mock_free(mk); + return 0; +} diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 33e33d40..0fd96e60 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -236,3 +236,11 @@ add_executable(test_lineedit ${CMAKE_SOURCE_DIR}/stubs/no-timing.c) target_link_libraries(test_lineedit guiterminal settings eventloop charset utils ${platform_libraries}) + +add_executable(test_terminal + ${CMAKE_SOURCE_DIR}/test/test_terminal.c + ${CMAKE_SOURCE_DIR}/stubs/no-gss.c + ${CMAKE_SOURCE_DIR}/stubs/no-storage.c + ${CMAKE_SOURCE_DIR}/stubs/no-timing.c) +target_link_libraries(test_terminal + guiterminal settings eventloop charset utils ${platform_libraries}) diff --git a/unix/platform.h b/unix/platform.h index 22ea96eb..a75d70a6 100644 --- a/unix/platform.h +++ b/unix/platform.h @@ -333,6 +333,7 @@ void gtk_setup_config_box( #define DEFAULT_CODEPAGE 0xFFFF #define CP_UTF8 CS_UTF8 /* from libcharset */ #define CP_437 CS_CP437 /* used for test suites */ +#define CP_ISO8859_1 CS_ISO8859_1 /* used for test suites */ #define strnicmp strncasecmp #define stricmp strcasecmp diff --git a/windows/platform.h b/windows/platform.h index 0b207473..7a333a0e 100644 --- a/windows/platform.h +++ b/windows/platform.h @@ -196,6 +196,7 @@ void centre_window(HWND hwnd); #define USES_VTLINE_HACK #define CP_UTF8 65001 #define CP_437 437 /* used for test suites */ +#define CP_ISO8859_1 0x10001 /* used for test suites */ #ifndef NO_GSSAPI /*