1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-10 23:58:06 -05:00

Initial work on a terminal test program.

This has all the basic necessities to become a test of the terminal's
behaviour, in terms of how its data structures evolve as output is
sent to it, and perhaps also (by filling in the stub TermWin more
usefully) testing what it draws during updates and what it sends in
response to query sequences.

For the moment, all I've done is to set up the framework, and add one
demo test of printing some ordinary text and observing that it appears
in the data structures and the cursor has moved.

I expect that writing a full test of terminal.c will be a very big
job. But perhaps I or someone else will find time to prod it gradually
in the background of other work. In particular, when I'm _modifying_
any part of the terminal code, it would be good to add some tests for
the part I'm changing, before making the change, and check they still
work afterwards.
This commit is contained in:
Simon Tatham 2023-03-04 17:47:01 +00:00
parent c890449d76
commit 57536cb7a3
5 changed files with 194 additions and 5 deletions

View File

@ -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

182
test/test_terminal.c Normal file
View File

@ -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;
}

View File

@ -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})

View File

@ -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

View File

@ -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
/*