/*
* Copyright © 2017, 2018 Christian Persch
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
using namespace std::literals;
#include
#include "parser.hh"
#include "parser-glue.hh"
#include "parser-charset-tables.hh"
#ifdef PARSER_INCLUDE_NOP
#define _VTE_NOP(...) _VTE_CMD(__VA_ARGS__)
#define _VTE_NOQ(...) _VTE_SEQ(__VA_ARGS__)
#else
#define _VTE_NOP(...)
#define _VTE_NOQ(...)
#endif
using namespace vte::parser;
Parser parser{};
Sequence seq{parser};
#if 0
static char const*
seq_to_str(unsigned int type)
{
switch (type) {
case VTE_SEQ_NONE: return "NONE";
case VTE_SEQ_IGNORE: return "IGNORE";
case VTE_SEQ_GRAPHIC: return "GRAPHIC";
case VTE_SEQ_CONTROL: return "CONTROL";
case VTE_SEQ_ESCAPE: return "ESCAPE";
case VTE_SEQ_CSI: return "CSI";
case VTE_SEQ_DCS: return "DCS";
case VTE_SEQ_OSC: return "OSC";
case VTE_SEQ_APC: return "APC";
case VTE_SEQ_PM: return "PM";
case VTE_SEQ_SOS: return "SOS";
case VTE_SEQ_SCI: return "SCI";
default:
g_assert_not_reached();
}
}
static char const*
cmd_to_str(unsigned int command)
{
switch (command) {
#define _VTE_CMD(cmd) case VTE_CMD_##cmd: return #cmd;
#include "parser-cmd.hh"
#undef _VTE_CMD
default:
static char buf[32];
snprintf(buf, sizeof(buf), "UNKOWN(%u)", command);
return buf;
}
}
static char const*
charset_to_str(unsigned int cs)
{
switch (cs) {
#define _VTE_CHARSET_PASTE(name) case VTE_CHARSET_##name: return #name;
#define _VTE_CHARSET(name) _VTE_CHARSET_PASTE(name)
#define _VTE_CHARSET_ALIAS_PASTE(name1,name2)
#define _VTE_CHARSET_ALIAS(name1,name2)
#include "parser-charset.hh"
#undef _VTE_CHARSET_PASTE
#undef _VTE_CHARSET
#undef _VTE_CHARSET_ALIAS_PASTE
#undef _VTE_CHARSET_ALIAS
default:
static char buf[32];
snprintf(buf, sizeof(buf), "UNKOWN(%u)", cs);
return buf;
}
}
#endif
static const char c0str[][6] = {
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US",
"SPACE"
};
static const char c1str[][5] = {
"DEL",
"0x80", "0x81", "BPH", "NBH", "0x84", "NEL", "SSA", "ESA",
"HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
"DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
"SOS", "0x99", "SCI", "CSI", "ST", "OSC", "PM", "APC"
};
static void
print_escaped(std::u32string const& s)
{
for (auto it : s) {
uint32_t c = (char32_t)it;
if (c <= 0x20)
g_print("%s ", c0str[c]);
else if (c < 0x7f)
g_print("%c ", c);
else if (c < 0xa0)
g_print("%s ", c1str[c - 0x7f]);
else
g_print("U+%04X", c);
}
g_print("\n");
}
#if 0
static void
print_seq()
{
auto c = seq.terminator();
if (seq.command() == VTE_CMD_GRAPHIC) {
char buf[7];
buf[g_unichar_to_utf8(c, buf)] = 0;
g_print("%s U+%04X [%s]\n", cmd_to_str(seq.command()),
c,
g_unichar_isprint(c) ? buf : "�");
} else {
g_print("%s", cmd_to_str(seq.command()));
if (seq.size()) {
g_print(" ");
for (unsigned int i = 0; i < seq.size(); i++) {
if (i > 0)
g_print(";");
g_print("%d", seq.param(i));
}
}
g_print("\n");
}
}
#endif
class vte_seq_builder : public u32SequenceBuilder {
public:
vte_seq_builder(unsigned int type,
uint32_t f)
: u32SequenceBuilder(type, f)
{
}
vte_seq_builder(unsigned int type,
u32SequenceBuilder::string_type const& str)
: u32SequenceBuilder(type)
{
set_string(str);
}
void set_intermediates(uint32_t* i,
unsigned int ni) noexcept
{
for (unsigned int n = 0; n < ni; ++n)
append_intermediate(i[n]);
}
void set_params(int* params,
unsigned int n) noexcept
{
for (unsigned int i = 0; i < n; ++i)
append_param(params[i]);
}
void print(bool c1 = false) const noexcept
{
std::u32string s;
to_string(s, c1);
print_escaped(s);
}
};
static int
feed_parser(std::u32string const& s)
{
int rv = VTE_SEQ_NONE;
for (auto it : s) {
rv = parser.feed((uint32_t)(char32_t)it);
if (rv < 0)
break;
}
return rv;
}
static int
feed_parser(vte_seq_builder& b,
bool c1 = false)
{
std::u32string s;
b.to_string(s, c1);
return feed_parser(s);
}
static void
test_seq_arg(void)
{
/* Basic test */
vte_seq_arg_t arg = VTE_SEQ_ARG_INIT_DEFAULT;
g_assert_false(vte_seq_arg_started(arg));
g_assert_true(vte_seq_arg_default(arg));
vte_seq_arg_push(&arg, '1');
vte_seq_arg_push(&arg, '2');
vte_seq_arg_push(&arg, '3');
vte_seq_arg_finish(&arg);
g_assert_cmpint(vte_seq_arg_value(arg), ==, 123);
g_assert_false(vte_seq_arg_default(arg));
/* Test max value */
arg = VTE_SEQ_ARG_INIT_DEFAULT;
vte_seq_arg_push(&arg, '6');
vte_seq_arg_push(&arg, '5');
vte_seq_arg_push(&arg, '5');
vte_seq_arg_push(&arg, '3');
vte_seq_arg_push(&arg, '6');
vte_seq_arg_finish(&arg);
g_assert_cmpint(vte_seq_arg_value(arg), ==, 65535);
}
static void
test_seq_string(void)
{
vte_seq_string_t str;
vte_seq_string_init(&str);
size_t len;
auto buf = vte_seq_string_get(&str, &len);
g_assert_cmpuint(len, ==, 0);
for (unsigned int i = 0; i < VTE_SEQ_STRING_MAX_CAPACITY; ++i) {
auto rv = vte_seq_string_push(&str, 0xfffdU);
g_assert_true(rv);
buf = vte_seq_string_get(&str, &len);
g_assert_cmpuint(len, ==, i + 1);
}
/* Try one more */
auto rv = vte_seq_string_push(&str, 0xfffdU);
g_assert_false(rv);
buf = vte_seq_string_get(&str, &len);
for (unsigned int i = 0; i < len; i++)
g_assert_cmpuint(buf[i], ==, 0xfffdU);
vte_seq_string_reset(&str);
buf = vte_seq_string_get(&str, &len);
g_assert_cmpuint(len, ==, 0);
vte_seq_string_free(&str);
}
static void
test_seq_control(void)
{
static struct {
uint32_t c;
unsigned int cmd;
} const controls [] = {
#define _VTE_SEQ(cmd,type,f,pi,ni,i0,flags) { f, VTE_CMD_##cmd },
#include "parser-c01.hh"
#undef _VTE_SEQ
};
for (unsigned int i = 0; i < G_N_ELEMENTS(controls); i++) {
parser.reset();
auto rv = parser.feed(controls[i].c);
g_assert_cmpuint(rv, ==, VTE_SEQ_CONTROL);
g_assert_cmpuint(controls[i].cmd, ==, seq.command());
}
}
static void
test_seq_esc_invalid(void)
{
/* Tests invalid ESC 0/n and ESC 1/n sequences, which should never result in
* an VTE_SEQ_ESCAPE type sequence, but instead always in the C0 control.
*/
for (uint32_t f = 0x0; f < 0x20; f++) {
parser.reset();
vte_seq_builder b{VTE_SEQ_ESCAPE, f};
auto rv = feed_parser(b);
g_assert_cmpint(rv, !=, VTE_SEQ_ESCAPE);
}
}
static void
test_seq_esc(uint32_t f,
uint32_t i[],
unsigned int ni)
{
vte_seq_builder b{VTE_SEQ_ESCAPE, f};
b.set_intermediates(i, ni);
parser.reset();
auto rv = feed_parser(b);
if (rv != VTE_SEQ_ESCAPE)
return;
b.assert_equal(seq);
}
static void
test_seq_esc_nF(void)
{
/* Tests nF sequences, that is ESC 2/n [2/m..] F with F being 3/0..7/14.
* They could have any number of itermediates, but we only test up to 4.
*/
uint32_t i[4];
for (uint32_t f = 0x30; f < 0x7f; f++) {
test_seq_esc(f, i, 0);
for (i[0] = 0x20; i[0] < 0x30; i[0]++) {
test_seq_esc(f, i, 1);
for (i[1] = 0x20; i[1] < 0x30; i[1]++) {
test_seq_esc(f, i, 2);
for (i[2] = 0x20; i[2] < 0x30; i[2]++) {
test_seq_esc(f, i, 3);
for (i[3] = 0x20; i[3] < 0x30; i[3]++) {
test_seq_esc(f, i, 4);
}
}
}
}
}
}
static void
test_seq_esc_charset(uint32_t f, /* final */
uint32_t i[], /* intermediates */
unsigned int ni, /* number of intermediates */
unsigned int cmd, /* expected command */
unsigned int cs /* expected charset */,
unsigned int slot /* expected slot */)
{
vte_seq_builder b{VTE_SEQ_ESCAPE, f};
b.set_intermediates(i, ni);
parser.reset();
auto rv = feed_parser(b);
g_assert_cmpint(rv, ==, VTE_SEQ_ESCAPE);
b.assert_equal(seq);
g_assert_cmpint(seq.command(), ==, cmd);
g_assert_cmpint(seq.charset(), ==, cs);
g_assert_cmpint(seq.slot(), ==, slot);
}
static void
test_seq_esc_charset(uint32_t i[], /* intermediates */
unsigned int ni, /* number of intermediates */
uint8_t const* const table, /* table */
unsigned int ntable, /* number of table entries */
uint32_t ts, /* start of table */
unsigned int cmd, /* expected command */
unsigned int defaultcs /* default charset */,
unsigned int slot /* expected slot */)
{
for (uint32_t f = 0x30; f < 0x7f; f++) {
int cs;
if (f >= ts && f < (ts + ntable))
cs = table[f - ts];
else if (f == 0x7e &&
cmd != VTE_CMD_DOCS &&
defaultcs != VTE_CHARSET_DRCS)
cs = VTE_CHARSET_EMPTY;
else
cs = defaultcs;
test_seq_esc_charset(f, i, ni, cmd, cs, slot);
}
}
static void
test_seq_esc_charset_94(void)
{
uint32_t i[4];
/* Single byte 94-sets */
for (i[0] = 0x28; i[0] <= 0x2b; i[0]++) {
int slot = i[0] - 0x28;
test_seq_esc_charset(i, 1,
charset_graphic_94,
G_N_ELEMENTS(charset_graphic_94),
0x30, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
i[1] = 0x20;
test_seq_esc_charset(i, 2, nullptr, 0, 0,
VTE_CMD_GnDm, VTE_CHARSET_DRCS, slot);
i[1] = 0x21;
test_seq_esc_charset(i, 2,
charset_graphic_94_with_2_1,
G_N_ELEMENTS(charset_graphic_94_with_2_1),
0x40, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
i[1] = 0x22;
test_seq_esc_charset(i, 2,
charset_graphic_94_with_2_2,
G_N_ELEMENTS(charset_graphic_94_with_2_2),
0x30, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
i[1] = 0x23;
test_seq_esc_charset(i, 2, nullptr, 0,
0x30, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
/* 2/4 is multibyte charsets */
i[1] = 0x25;
test_seq_esc_charset(i, 2,
charset_graphic_94_with_2_5,
G_N_ELEMENTS(charset_graphic_94_with_2_5),
0x30, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
i[1] = 0x26;
test_seq_esc_charset(i, 2,
charset_graphic_94_with_2_6,
G_N_ELEMENTS(charset_graphic_94_with_2_6),
0x30, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
i[1] = 0x27;
test_seq_esc_charset(i, 2, nullptr, 0, 0,
VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
}
}
static void
test_seq_esc_charset_96(void)
{
uint32_t i[4];
/* Single byte 96-sets */
for (i[0] = 0x2d; i[0] <= 0x2f; i[0]++) {
int slot = i[0] - 0x2c;
test_seq_esc_charset(i, 1,
charset_graphic_96,
G_N_ELEMENTS(charset_graphic_96),
0x30, VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
i[1] = 0x20;
test_seq_esc_charset(i, 2, nullptr, 0, 0,
VTE_CMD_GnDm, VTE_CHARSET_DRCS, slot);
/* 2/4 is multibyte charsets, 2/5 is DOCS. Other indermediates may be present
* in Fp sequences, but none are actually in use.
*/
for (i[1] = 0x21; i[1] < 0x28; i[1]++) {
if (i[1] == 0x24 || i[1] == 0x25)
continue;
test_seq_esc_charset(i, 2, nullptr, 0, 0,
VTE_CMD_GnDm, VTE_CHARSET_NONE, slot);
}
}
}
static void
test_seq_esc_charset_94_n(void)
{
uint32_t i[4];
/* Multibyte 94-sets */
i[0] = 0x24;
for (i[1] = 0x28; i[1] <= 0x2b; i[1]++) {
int slot = i[1] - 0x28;
test_seq_esc_charset(i, 2,
charset_graphic_94_n,
G_N_ELEMENTS(charset_graphic_94_n),
0x30, VTE_CMD_GnDMm, VTE_CHARSET_NONE, slot);
i[2] = 0x20;
test_seq_esc_charset(i, 3, nullptr, 0, 0,
VTE_CMD_GnDMm, VTE_CHARSET_DRCS, slot);
i[2] = 0x21;
test_seq_esc_charset(i, 3,
charset_graphic_94_n_with_2_1,
G_N_ELEMENTS(charset_graphic_94_n_with_2_1),
0x30, VTE_CMD_GnDMm, VTE_CHARSET_NONE, slot);
/* There could be one more intermediate byte. */
for (i[2] = 0x22; i[2] < 0x28; i[2]++) {
if (i[2] == 0x24) /* TODO */
continue;
test_seq_esc_charset(i, 3, nullptr, 0, 0,
VTE_CMD_GnDMm, VTE_CHARSET_NONE, slot);
}
}
/* As a special exception, ESC 2/4 4/[012] are also possible */
test_seq_esc_charset(0x40, i, 1, VTE_CMD_GnDMm, charset_graphic_94_n[0x40 - 0x30], 0);
test_seq_esc_charset(0x41, i, 1, VTE_CMD_GnDMm, charset_graphic_94_n[0x41 - 0x30], 0);
test_seq_esc_charset(0x42, i, 1, VTE_CMD_GnDMm, charset_graphic_94_n[0x42 - 0x30], 0);
}
static void
test_seq_esc_charset_96_n(void)
{
uint32_t i[4];
/* Multibyte 94-sets */
i[0] = 0x24;
for (i[1] = 0x2d; i[1] <= 0x2f; i[1]++) {
int slot = i[1] - 0x2c;
test_seq_esc_charset(i, 2, nullptr, 0, 0,
VTE_CMD_GnDMm, VTE_CHARSET_NONE, slot);
i[2] = 0x20;
test_seq_esc_charset(i, 3, nullptr, 0, 0,
VTE_CMD_GnDMm, VTE_CHARSET_DRCS, slot);
/* There could be one more intermediate byte. */
for (i[2] = 0x21; i[2] < 0x28; i[2]++) {
test_seq_esc_charset(i, 3, nullptr, 0, 0,
VTE_CMD_GnDMm, VTE_CHARSET_NONE, slot);
}
}
}
static void
test_seq_esc_charset_control(void)
{
uint32_t i[4];
/* C0 controls: ESC 2/1 F */
i[0] = 0x21;
test_seq_esc_charset(i, 1,
charset_control_c0,
G_N_ELEMENTS(charset_control_c0),
0x40, VTE_CMD_CnD, VTE_CHARSET_NONE, 0);
/* C1 controls: ESC 2/2 F */
i[0] = 0x22;
test_seq_esc_charset(i, 1,
charset_control_c1,
G_N_ELEMENTS(charset_control_c1),
0x40, VTE_CMD_CnD, VTE_CHARSET_NONE, 1);
}
static void
test_seq_esc_charset_other(void)
{
uint32_t i[4];
/* Other coding systems: ESC 2/5 F or ESC 2/5 I F */
i[0] = 0x25;
test_seq_esc_charset(i, 1,
charset_ocs,
G_N_ELEMENTS(charset_ocs),
0x30, VTE_CMD_DOCS, VTE_CHARSET_NONE, 0);
i[1] = 0x20;
test_seq_esc_charset(i, 2,
charset_ocs_with_2_0,
G_N_ELEMENTS(charset_ocs_with_2_0),
0x30, VTE_CMD_DOCS, VTE_CHARSET_NONE, 0);
i[1] = 0x2f;
test_seq_esc_charset(i, 2,
charset_ocs_with_2_15,
G_N_ELEMENTS(charset_ocs_with_2_15),
0x40, VTE_CMD_DOCS, VTE_CHARSET_NONE, 0);
}
static void
test_seq_esc_Fpes(void)
{
/* Tests Fp, Fe and Ft sequences, that is ESC 3/n .. ESC 7/14 */
for (uint32_t f = 0x30; f < 0x7f; f++) {
parser.reset();
vte_seq_builder b{VTE_SEQ_ESCAPE, f};
auto rv = feed_parser(b);
int expected_rv;
switch (f) {
case 'P': /* DCS */
case 'X': /* SOS */
case 'Z': /* SCI */
case '_': /* APC */
case '[': /* CSI */
case ']': /* OSC */
case '^': /* PM */
expected_rv = VTE_SEQ_NONE;
break;
default:
expected_rv = VTE_SEQ_ESCAPE;
break;
}
g_assert_cmpint(rv, ==, expected_rv);
if (rv != VTE_SEQ_NONE)
b.assert_equal(seq);
}
}
static void
test_seq_esc_known(uint32_t f,
uint32_t i,
unsigned int cmd)
{
vte_seq_builder b{VTE_SEQ_ESCAPE, f};
if (i != 0)
b.set_intermediates(&i, 1);
auto rv = feed_parser(b);
g_assert_cmpint(rv, ==, VTE_SEQ_ESCAPE);
g_assert_cmpint(seq.command(), ==, cmd);
}
static void
test_seq_esc_known(void)
{
parser.reset();
#define _VTE_SEQ(cmd,type,f,p,ni,i,flags) \
test_seq_esc_known(f, VTE_SEQ_INTERMEDIATE_CHAR_##i, VTE_CMD_##cmd);
#include "parser-esc.hh"
#undef _VTE_SEQ
}
static void
test_seq_csi(uint32_t f,
uint32_t p,
vte_seq_arg_t params[16],
uint32_t i[4],
unsigned int ni)
{
vte_seq_builder b{VTE_SEQ_CSI, f};
b.set_intermediates(i, ni);
b.set_param_intro(p);
int expected_rv = (f & 0xF0) == 0x30 ? VTE_SEQ_NONE : VTE_SEQ_CSI;
for (unsigned int n = 0; n <= 16; n++) {
b.reset_params();
b.set_params(params, n);
parser.reset();
/* First with C0 CSI */
auto rv = feed_parser(b, false);
g_assert_cmpint(rv, ==, expected_rv);
if (rv != VTE_SEQ_NONE)
b.assert_equal_full(seq);
/* Now with C1 CSI */
rv = feed_parser(b, true);
if (rv != VTE_SEQ_NONE)
b.assert_equal_full(seq);
}
}
static void
test_seq_csi(uint32_t p,
vte_seq_arg_t params[16])
{
uint32_t i[4];
for (uint32_t f = 0x30; f < 0x7f; f++) {
test_seq_csi(f, p, params, i, 0);
for (i[0] = 0x20; i[0] < 0x30; i[0]++) {
test_seq_csi(f, p, params, i, 1);
for (i[1] = 0x20; i[1] < 0x30; i[1]++) {
test_seq_csi(f, p, params, i, 2);
}
}
}
}
static void
test_seq_csi(vte_seq_arg_t params[16])
{
test_seq_csi(0, params);
for (uint32_t p = 0x3c; p <= 0x3f; p++)
test_seq_csi(p, params);
}
static void
test_seq_csi(void)
{
/* Tests CSI sequences, that is sequences of the form
* CSI P...P I...I F
* with parameter bytes P from 3/0..3/15, intermediate bytes I from 2/0..2/15 and
* final byte F from 4/0..7/14.
* There could be any number of intermediate bytes, but we only test up to 2.
* There could be any number of extra params bytes, but we only test up to 1.
* CSI can be either the C1 control itself, or ESC [
*/
vte_seq_arg_t params1[16]{ -1, 0, 1, 9, 10, 99, 100, 999,
1000, 9999, 10000, 65534, 65535, 65536, -1, -1 };
test_seq_csi(params1);
vte_seq_arg_t params2[16]{ 1, -1, -1, -1, 1, -1, 1, 1,
1, -1, -1, -1, -1, 1, 1, 1 };
test_seq_csi(params2);
}
static void
test_seq_sci(uint32_t f,
bool valid)
{
vte_seq_builder b{VTE_SEQ_SCI, f};
/* First with C0 SCI */
auto rv = feed_parser(b, false);
if (valid) {
g_assert_cmpint(rv, ==, VTE_SEQ_SCI);
b.assert_equal_full(seq);
} else
g_assert_cmpint(rv, !=, VTE_SEQ_SCI);
/* Now with C1 SCI */
rv = feed_parser(b, true);
if (valid) {
g_assert_cmpint(rv, ==, VTE_SEQ_SCI);
b.assert_equal_full(seq);
} else
g_assert_cmpint(rv, !=, VTE_SEQ_SCI);
}
static void
test_seq_sci(void)
{
/* Tests SCI sequences, that is sequences of the form SCI F
* with final byte 0/8..0/13 or 2/0..7/14
* SCI can be either the C1 control itself, or ESC Z
*/
parser.reset();
for (uint32_t f = 0x8; f <= 0xd; ++f)
test_seq_sci(f, true);
for (uint32_t f = 0x20; f <= 0x7e; ++f)
test_seq_sci(f, true);
for (uint32_t f = 0x7f; f <= 0xff; ++f)
test_seq_sci(f, false);
}
G_GNUC_UNUSED
static void
test_seq_sci_known(uint32_t f,
unsigned int cmd)
{
vte_seq_builder b{VTE_SEQ_SCI, f};
auto rv = feed_parser(b);
g_assert_cmpint(rv, ==, VTE_SEQ_SCI);
g_assert_cmpint(seq.command(), ==, cmd);
}
static void
test_seq_sci_known(void)
{
parser.reset();
#define _VTE_SEQ(cmd,type,f,p,ni,i,flags) \
test_seq_sci_known(f, VTE_CMD_##cmd);
#include "parser-sci.hh"
#undef _VTE_SEQ
}
static void
test_seq_csi_known(uint32_t f,
uint32_t p,
uint32_t i,
unsigned int cmd)
{
vte_seq_builder b{VTE_SEQ_CSI, f};
if (p != 0)
b.set_param_intro(p);
if (i != 0)
b.set_intermediates(&i, 1);
auto rv = feed_parser(b);
g_assert_cmpint(rv, ==, VTE_SEQ_CSI);
g_assert_cmpint(seq.command(), ==, cmd);
}
static void
test_seq_csi_known(void)
{
parser.reset();
#define _VTE_SEQ(cmd,type,f,p,ni,i,flags) \
test_seq_csi_known(f, VTE_SEQ_PARAMETER_CHAR_##p, VTE_SEQ_INTERMEDIATE_CHAR_##i, VTE_CMD_##cmd);
#include "parser-csi.hh"
#undef _VTE_SEQ
}
static void
test_seq_dcs(uint32_t f,
uint32_t p,
vte_seq_arg_t params[16],
uint32_t i[4],
unsigned int ni,
std::u32string const& str,
int expected_rv = VTE_SEQ_DCS)
{
vte_seq_builder b{VTE_SEQ_DCS, f};
b.set_intermediates(i, ni);
b.set_param_intro(p);
b.set_string(str);
int expected_rv0 = (f & 0xF0) == 0x30 || expected_rv == VTE_SEQ_NONE ? VTE_SEQ_ESCAPE /* the C0 ST */ : expected_rv;
int expected_rv1 = (f & 0xF0) == 0x30 ? VTE_SEQ_NONE : expected_rv;
for (unsigned int n = 0; n <= 16; n++) {
b.reset_params();
b.set_params(params, n);
parser.reset();
/* First with C0 DCS */
auto rv0 = feed_parser(b, false);
g_assert_cmpint(rv0, ==, expected_rv0);
if (rv0 != VTE_SEQ_ESCAPE && rv0 != VTE_SEQ_NONE)
b.assert_equal_full(seq);
if (rv0 == VTE_SEQ_ESCAPE)
g_assert_cmpint(seq.command(), ==, VTE_CMD_ST);
/* Now with C1 DCS */
auto rv1 = feed_parser(b, true);
g_assert_cmpint(rv1, ==, expected_rv1);
if (rv1 != VTE_SEQ_NONE)
b.assert_equal_full(seq);
}
}
static void
test_seq_dcs(uint32_t p,
vte_seq_arg_t params[16],
std::u32string const& str,
int expected_rv = VTE_SEQ_DCS)
{
uint32_t i[4];
for (uint32_t f = 0x40; f < 0x7f; f++) {
test_seq_dcs(f, p, params, i, 0, str, expected_rv);
}
for (uint32_t f = 0x30; f < 0x7f; f++) {
for (i[0] = 0x20; i[0] < 0x30; i[0]++) {
test_seq_dcs(f, p, params, i, 1, str, expected_rv);
for (i[1] = 0x20; i[1] < 0x30; i[1]++) {
test_seq_dcs(f, p, params, i, 2, str, expected_rv);
}
}
}
}
static void
test_seq_dcs(vte_seq_arg_t params[16],
std::u32string const& str,
int expected_rv = VTE_SEQ_DCS)
{
test_seq_dcs(0, params, str);
for (uint32_t p = 0x3c; p <= 0x3f; p++)
test_seq_dcs(p, params, str, expected_rv);
}
static void
test_seq_dcs(std::u32string const& str,
int expected_rv = VTE_SEQ_DCS)
{
/* Tests DCS sequences, that is sequences of the form
* DCS P...P I...I F D...D ST
* with parameter bytes P from 3/0..3/15, intermediate bytes I from 2/0..2/15 and
* final byte F from 4/0..7/14.
* There could be any number of intermediate bytes, but we only test up to 2.
* There could be any number of extra params bytes, but we only test up to 1.
* DCS can be either the C1 control itself, or ESC [; ST can be either the C1
* control itself, or ESC \\
*/
vte_seq_arg_t params1[16]{ -1, 0, 1, 9, 10, 99, 100, 999,
1000, 9999, 10000, 65534, 65535, 65536, -1, -1 };
test_seq_dcs(params1, str, expected_rv);
vte_seq_arg_t params2[16]{ 1, -1, -1, -1, 1, -1, 1, 1,
1, -1, -1, -1, -1, 1, 1, 1 };
test_seq_dcs(params2, str, expected_rv);
}
static void
test_seq_dcs_simple(std::u32string const& str,
int expected_rv = VTE_SEQ_DCS)
{
vte_seq_arg_t params[16]{ 1, -1, -1, -1, 1, -1, 1, 1,
1, -1, -1, -1, -1, 1, 1, 1 };
uint32_t i[4];
test_seq_dcs(0x40, 0, params, i, 0, str, expected_rv);
}
static void
test_seq_dcs(void)
{
/* Length exceeded */
test_seq_dcs_simple(std::u32string(VTE_SEQ_STRING_MAX_CAPACITY + 1, 0x100000), VTE_SEQ_NONE);
test_seq_dcs(U""s);
test_seq_dcs(U"123;TESTING"s);
}
static void
test_seq_dcs_known(uint32_t f,
uint32_t p,
uint32_t i,
unsigned int cmd)
{
vte_seq_builder b{VTE_SEQ_DCS, f};
if (p != 0)
b.set_param_intro(p);
if (i != 0)
b.set_intermediates(&i, 1);
auto rv = feed_parser(b);
g_assert_cmpint(rv, ==, VTE_SEQ_DCS);
g_assert_cmpint(seq.command(), ==, cmd);
}
static void
test_seq_dcs_known(void)
{
parser.reset();
#define _VTE_SEQ(cmd,type,f,p,ni,i,flags) \
test_seq_dcs_known(f, VTE_SEQ_PARAMETER_CHAR_##p, VTE_SEQ_INTERMEDIATE_CHAR_##i, VTE_CMD_##cmd);
#include "parser-dcs.hh"
#undef _VTE_SEQ
}
static void
test_seq_parse(char const* str)
{
std::u32string s;
s.push_back(0x9B); /* CSI */
for (unsigned int i = 0; str[i]; i++)
s.push_back(str[i]);
s.push_back(0x6d); /* m = SGR */
parser.reset();
auto rv = feed_parser(s);
g_assert_cmpint(rv, ==, VTE_SEQ_CSI);
}
static void
test_seq_csi_param(char const* str,
std::vector args,
std::vector args_nonfinal)
{
g_assert_cmpuint(args.size(), ==, args_nonfinal.size());
test_seq_parse(str);
if (seq.size() < VTE_PARSER_ARG_MAX)
g_assert_cmpuint(seq.size(), ==, args.size());
unsigned int n_final_args = 0;
for (unsigned int i = 0; i < seq.size(); i++) {
g_assert_cmpint(seq.param(i), ==, args[i]);
auto is_nonfinal = args_nonfinal[i];
if (!is_nonfinal)
n_final_args++;
g_assert_cmpint(seq.param_nonfinal(i), ==, is_nonfinal);
}
g_assert_cmpuint(seq.size_final(), ==, n_final_args);
}
static void
test_seq_csi_param(void)
{
/* Tests that CSI parameters and subparameters are parsed correctly. */
test_seq_csi_param("", { }, { });
test_seq_csi_param(";", { -1, -1 }, { false, false });
test_seq_csi_param(":", { -1, -1 }, { true, false });
test_seq_csi_param(";:", { -1, -1, -1 }, { false, true, false });
test_seq_csi_param("::;;", { -1, -1, -1, -1, -1 }, { true, true, false, false, false });
test_seq_csi_param("1;2:3:4:5:6;7:8;9:0",
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 },
{ false, true, true, true, true, false, true, false, true, false });
test_seq_csi_param("1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1",
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false });
test_seq_csi_param("1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, false });
}
static void
test_seq_csi_clear(void)
{
/* Check that parameters are cleared from when a sequence was aborted. */
vte_seq_builder b0{VTE_SEQ_CSI, 'm'};
b0.set_param_intro(VTE_SEQ_PARAMETER_CHAR_WHAT);
for (unsigned int i = 0; i < VTE_PARSER_ARG_MAX; ++i)
b0.append_param(127 * i + 17);
std::u32string str0;
b0.to_string(str0);
parser.reset();
for (size_t len0 = 1; len0 <= str0.size(); ++len0) {
for (unsigned int n_args = 0; n_args < VTE_PARSER_ARG_MAX; ++n_args) {
feed_parser(str0.substr(0, len0));
vte_seq_builder b1{VTE_SEQ_CSI, 'n'};
b1.set_param_intro(VTE_SEQ_PARAMETER_CHAR_GT);
for (unsigned int i = 0; i < n_args; ++i)
b1.append_param(257 * i + 31);
std::u32string str1;
b1.to_string(str1);
auto rv = feed_parser(str1);
g_assert_cmpint(rv, ==, VTE_SEQ_CSI);
b1.assert_equal_full(seq);
for (unsigned int n = seq.size(); n < VTE_PARSER_ARG_MAX; n++)
g_assert_true(seq.param_default(n));
}
}
}
static void
test_seq_csi_max(std::u32string const& start,
std::u32string const& more,
int expected_rv = VTE_SEQ_NONE)
{
parser.reset();
feed_parser(start);
feed_parser(more);
auto rv = feed_parser(U"m"s); /* final character */
g_assert_cmpint(rv, ==, expected_rv);
}
static void
test_seq_csi_max(void)
{
/* Check that an excessive number of parameters causes the
* sequence to be ignored.
*
* Since SequenceBuilder is limited in the same number of
* parameters as the parser, can't use it directly to
* produce a sequence with too may parameters.
*/
vte_seq_builder b{VTE_SEQ_CSI, 'm'};
b.set_param_intro(VTE_SEQ_PARAMETER_CHAR_WHAT);
for (unsigned int i = 0; i < VTE_PARSER_ARG_MAX; ++i)
b.append_param(i);
std::u32string str;
b.to_string(str);
/* The sequence with VTE_PARSER_ARG_MAX args must be parsed */
auto rv = feed_parser(str);
g_assert_cmpint(rv, ==, VTE_SEQ_CSI);
/* Now test that adding one more parameter (whether with an
* explicit value, or default, causes the sequence to be ignored.
*/
str.pop_back(); /* erase final character */
test_seq_csi_max(str, U":"s);
test_seq_csi_max(str, U";"s);
test_seq_csi_max(str, U":12345"s);
test_seq_csi_max(str, U";12345"s);
test_seq_csi_max(str, U":12345;"s);
test_seq_csi_max(str, U";12345:"s);
test_seq_csi_max(str, U":12345;"s);
test_seq_csi_max(str, U":12345:"s);
}
static void
test_seq_glue_arg(char const* str,
unsigned int n_args,
unsigned int n_final_args)
{
test_seq_parse(str);
auto raw_seq = *seq.seq_ptr();
g_assert_cmpuint(seq.size(), ==, n_args);
g_assert_cmpuint(raw_seq->n_args, ==, n_args);
g_assert_cmpuint(seq.size_final(), ==, n_final_args);
g_assert_cmpuint(raw_seq->n_final_args, ==, n_final_args);
g_assert_cmpuint(seq.type(), ==, raw_seq->type);
g_assert_cmpuint(seq.command(), ==, raw_seq->command);
g_assert_cmpuint(seq.terminator(), ==, raw_seq->terminator);
for (unsigned int i = 0; i < raw_seq->n_args; i++)
g_assert_cmpuint(seq.param(i), ==, vte_seq_arg_value(raw_seq->args[i]));
}
static void
test_seq_glue_arg(void)
{
test_seq_glue_arg(":0:1000;2;3;4;:;", 9, 6);
g_assert_cmpuint(seq.cbegin(), ==, 0);
g_assert_cmpuint(seq.cend(), ==, 9);
auto it = seq.cbegin();
g_assert_cmpuint(it, ==, 0);
it = seq.next(it);
g_assert_cmpuint(it, ==, 3);
it = seq.next(it);
g_assert_cmpuint(it, ==, 4);
it = seq.next(it);
g_assert_cmpuint(it, ==, 5);
it = seq.next(it);
g_assert_cmpuint(it, ==, 6);
it = seq.next(it);
g_assert_cmpuint(it, ==, 8);
it = seq.next(it);
g_assert_cmpuint(it, ==, 9);
it = seq.cbegin();
g_assert_cmpint(seq.param(it++), ==, -1);
g_assert_cmpint(seq.param(it++), ==, 0);
g_assert_cmpint(seq.param(it++), ==, 1000);
g_assert_cmpint(seq.param(it++), ==, 2);
g_assert_cmpint(seq.param(it++), ==, 3);
g_assert_cmpint(seq.param(it++), ==, 4);
g_assert_cmpint(seq.param(it++), ==, -1);
g_assert_cmpint(seq.param(it++), ==, -1);
g_assert_cmpint(seq.param(it++), ==, -1);
g_assert_cmpint(it, ==, seq.cend());
it = seq.cbegin();
g_assert_cmpint(seq.param(it, -2), ==, -2);
g_assert_cmpint(seq.param(it, -2, 0, 100), ==, 0);
it++; it++;
g_assert_cmpint(seq.param(it, -2), ==, seq.param(it));
g_assert_cmpint(seq.param(it, -2, 20, 100), ==, 100);
g_assert_cmpint(seq.param(it, -2, 200, 2000), ==, 1000);
g_assert_cmpint(seq.param(it, -2, 2000, 4000), ==, 2000);
int a, b, c,d ;
it = seq.cbegin();
g_assert_false(seq.collect(it, {&a, &b, &c}));
g_assert_true(seq.collect_subparams(it, {&a}));
g_assert_true(seq.collect_subparams(it, {&a, &b}));
g_assert_true(seq.collect_subparams(it, {&a, &b, &c}));
g_assert_cmpint(a, ==, -1);
g_assert_cmpint(b, ==, 0);
g_assert_cmpint(c, ==, 1000);
g_assert_false(seq.collect_subparams(it, {&a, &b, &c, &d}));
it = seq.next(it);
g_assert_true(seq.collect(it, {&a}));
g_assert_true(seq.collect(it, {&a, &b}));
g_assert_true(seq.collect(it, {&a, &b, &c}));
g_assert_cmpint(a, ==, 2);
g_assert_cmpint(b, ==, 3);
g_assert_cmpint(c, ==, 4);
g_assert_false(seq.collect(it, {&a, &b, &c, &d}));
it = seq.next(it);
it = seq.next(it);
it = seq.next(it);
g_assert_false(seq.collect(it, {&a}));
g_assert_true(seq.collect_subparams(it, {&a}));
g_assert_true(seq.collect_subparams(it, {&a, &b}));
g_assert_cmpint(a, ==, -1);
g_assert_cmpint(b, ==, -1);
g_assert_false(seq.collect_subparams(it, {&a, &b, &c}));
it = seq.next(it);
g_assert_true(seq.collect(it, {&a}));
g_assert_cmpint(a, ==, -1);
g_assert_true(seq.collect(it, {&a, &b})); /* past-the-end params are final and default */
g_assert_cmpint(a, ==, -1);
g_assert_cmpint(b, ==, -1);
g_assert_true(seq.collect(it, {&a, &b, &c})); /* past-the-end params are final and default */
g_assert_cmpint(a, ==, -1);
g_assert_cmpint(b, ==, -1);
g_assert_cmpint(c, ==, -1);
it = seq.cbegin();
g_assert_cmpint(seq.collect1(it, -2), ==, -2);
it = seq.next(it);
g_assert_cmpint(seq.collect1(it), ==, 2);
g_assert_cmpint(seq.collect1(it), ==, 2);
it = seq.next(it);
g_assert_cmpint(seq.collect1(it), ==, 3);
it = seq.next(it);
g_assert_cmpint(seq.collect1(it), ==, 4);
it = seq.next(it);
g_assert_cmpint(seq.collect1(it, -3), ==, -3);
it = seq.next(it);
g_assert_cmpint(seq.collect1(it), ==, -1);
g_assert_cmpint(seq.collect1(it, 42), ==, 42);
g_assert_cmpint(seq.collect1(it, -1, 0, 100), ==, 0);
g_assert_cmpint(seq.collect1(it, 42, 0, 100), ==, 42);
g_assert_cmpint(seq.collect1(it, 42, 0, 10), ==, 10);
g_assert_cmpint(seq.collect1(it, 42, 100, 200), ==, 100);
}
static int
feed_parser_st(vte_seq_builder& b,
bool c1 = false,
ssize_t max_arg_str_len = -1,
u32SequenceBuilder::Introducer introducer = u32SequenceBuilder::Introducer::DEFAULT,
u32SequenceBuilder::ST st = u32SequenceBuilder::ST::DEFAULT)
{
std::u32string s;
b.to_string(s, c1, max_arg_str_len, introducer, st);
auto rv = feed_parser(s);
if (rv != VTE_SEQ_OSC)
return rv;
switch (st) {
case u32SequenceBuilder::ST::NONE:
g_assert_cmpuint(seq.st(), ==, 0);
break;
case u32SequenceBuilder::ST::DEFAULT:
g_assert_cmpuint(seq.st(), ==, c1 ? 0x9c /* ST */ : 0x5c /* BACKSLASH */);
break;
case u32SequenceBuilder::ST::C0:
g_assert_cmpuint(seq.st(), ==, 0x5c /* BACKSLASH */);
break;
case u32SequenceBuilder::ST::C1:
g_assert_cmpuint(seq.st(), ==, 0x9c /* ST */);
break;
case u32SequenceBuilder::ST::BEL:
g_assert_cmpuint(seq.st(), ==, 0x7 /* BEL */);
break;
}
return rv;
}
static void
test_seq_osc(std::u32string const& str,
int expected_rv = VTE_SEQ_OSC,
bool c1 = true,
ssize_t max_arg_str_len = -1,
u32SequenceBuilder::Introducer introducer = u32SequenceBuilder::Introducer::DEFAULT,
u32SequenceBuilder::ST st = u32SequenceBuilder::ST::DEFAULT)
{
vte_seq_builder b{VTE_SEQ_OSC, str};
parser.reset();
auto rv = feed_parser_st(b, c1, max_arg_str_len, introducer, st);
g_assert_cmpint(rv, ==, expected_rv);
#if 0
if (rv != VTE_SEQ_NONE)
b.assert_equal(seq);
#endif
if (expected_rv != VTE_SEQ_OSC)
return;
if (max_arg_str_len < 0 || size_t(max_arg_str_len) == str.size())
g_assert_true(seq.string() == str);
else
g_assert_true(seq.string() == str.substr(0, max_arg_str_len));
}
static int
controls_match(bool c1,
u32SequenceBuilder::Introducer introducer,
u32SequenceBuilder::ST st,
bool allow_bel,
int expected_rv)
{
if (introducer == u32SequenceBuilder::Introducer::DEFAULT)
introducer = c1 ? u32SequenceBuilder::Introducer::C1 : u32SequenceBuilder::Introducer::C0;
if (st == u32SequenceBuilder::ST::DEFAULT)
st = c1 ? u32SequenceBuilder::ST::C1 : u32SequenceBuilder::ST::C0;
if ((introducer == u32SequenceBuilder::Introducer::C0 &&
(st == u32SequenceBuilder::ST::C0 || (allow_bel && st == u32SequenceBuilder::ST::BEL))) ||
(introducer == u32SequenceBuilder::Introducer::C1 &&
st == u32SequenceBuilder::ST::C1))
return expected_rv;
return VTE_SEQ_IGNORE;
}
static void
test_seq_osc(void)
{
/* Simple */
test_seq_osc(U""s);
test_seq_osc(U"TEST"s);
/* String of any supported length */
for (unsigned int len = 0; len < VTE_SEQ_STRING_MAX_CAPACITY; ++len)
test_seq_osc(std::u32string(len, 0x10000+len));
/* Length exceeded */
test_seq_osc(std::u32string(VTE_SEQ_STRING_MAX_CAPACITY + 1, 0x100000), VTE_SEQ_IGNORE);
/* Test all introducer/ST combinations */
for (auto introducer : { u32SequenceBuilder::Introducer::DEFAULT,
u32SequenceBuilder::Introducer::C0,
u32SequenceBuilder::Introducer::C1 }) {
for (auto st : {u32SequenceBuilder::ST::DEFAULT,
u32SequenceBuilder::ST::C0,
u32SequenceBuilder::ST::C1,
u32SequenceBuilder::ST::BEL }) {
for (auto c1 : { false, true }) {
int expected_rv = controls_match(c1, introducer, st, true, VTE_SEQ_OSC);
test_seq_osc(U"TEST"s, expected_rv, c1, -1, introducer, st);
}
}
}
}
static void
test_seq_glue_string(void)
{
std::u32string str{U"TEST"s};
test_seq_osc(str);
g_assert_true(seq.string() == str);
}
static void
test_seq_glue_string_tokeniser(void)
{
std::string str{"a;1b:17:test::b:;3;5;def;17 a;ghi;"s};
StringTokeniser tokeniser{str, ';'};
auto start = tokeniser.cbegin();
auto end = tokeniser.cend();
auto pit = start;
for (auto it : {"a"s, "1b:17:test::b:"s, "3"s, "5"s, "def"s, "17 a"s, "ghi"s, ""s}) {
g_assert_true(it == *pit);
/* Use std::find to see if the InputIterator implementation
* is complete and correct.
*/
auto fit = std::find(start, end, it);
g_assert_true(fit == pit);
++pit;
}
g_assert_true(pit == end);
auto len = str.size();
size_t pos = 0;
pit = start;
for (auto it : {1, 14, 1, 1, 3, 4, 3, 0}) {
g_assert_cmpuint(it, ==, pit.size());
g_assert_cmpuint(len, ==, pit.size_remaining());
g_assert_true(pit.string_remaining() == str.substr(pos, std::string::npos));
len -= it + 1;
pos += it + 1;
++pit;
}
g_assert_cmpuint(len + 1, ==, 0);
g_assert_cmpuint(pos, ==, str.size() + 1);
pit = start;
for (auto it : {-2, -2, 3, 5, -2, -2, -2, -1}) {
int num;
bool v = pit.number(num);
if (it == -2)
g_assert_false(v);
else
g_assert_cmpint(it, ==, num);
++pit;
}
/* Test range for */
for (auto it : tokeniser)
;
/* Test different separator */
pit = start;
++pit;
auto substr = *pit;
StringTokeniser subtokeniser{substr, ':'};
auto subpit = subtokeniser.cbegin();
for (auto it : {"1b"s, "17"s, "test"s, ""s, "b"s, ""s}) {
g_assert_true(it == *subpit);
++subpit;
}
g_assert_true(subpit == subtokeniser.cend());
/* Test another string, one that doesn't end with an empty token */
std::string str2{"abc;defghi"s};
StringTokeniser tokeniser2{str2, ';'};
g_assert_cmpint(std::distance(tokeniser2.cbegin(), tokeniser2.cend()), ==, 2);
auto pit2 = tokeniser2.cbegin();
g_assert_true(*pit2 == "abc"s);
++pit2;
g_assert_true(*pit2 == "defghi"s);
++pit2;
g_assert_true(pit2 == tokeniser2.cend());
/* Test another string, one that starts with an empty token */
std::string str3{";abc"s};
StringTokeniser tokeniser3{str3, ';'};
g_assert_cmpint(std::distance(tokeniser3.cbegin(), tokeniser3.cend()), ==, 2);
auto pit3 = tokeniser3.cbegin();
g_assert_true(*pit3 == ""s);
++pit3;
g_assert_true(*pit3 == "abc"s);
++pit3;
g_assert_true(pit3 == tokeniser3.cend());
/* And try an empty string, which should split into one empty token */
std::string str4{""s};
StringTokeniser tokeniser4{str4, ';'};
g_assert_cmpint(std::distance(tokeniser4.cbegin(), tokeniser4.cend()), ==, 1);
auto pit4 = tokeniser4.cbegin();
g_assert_true(*pit4 == ""s);
++pit4;
g_assert_true(pit4 == tokeniser4.cend());
}
static void
test_seq_glue_sequence_builder(void)
{
/* This is sufficiently tested by being used in all the other tests,
* but if there's anything remaining to be tested, do it here.
*/
}
static void
test_seq_glue_reply_builder(void)
{
/* Nothing to test here; ReplyBuilder is just a constructor for
* SequenceBuilder.
*/
}
int
main(int argc,
char* argv[])
{
g_test_init(&argc, &argv, nullptr);
g_test_add_func("/vte/parser/sequences/arg", test_seq_arg);
g_test_add_func("/vte/parser/sequences/string", test_seq_string);
g_test_add_func("/vte/parser/sequences/glue/arg", test_seq_glue_arg);
g_test_add_func("/vte/parser/sequences/glue/string", test_seq_glue_string);
g_test_add_func("/vte/parser/sequences/glue/string-tokeniser", test_seq_glue_string_tokeniser);
g_test_add_func("/vte/parser/sequences/glue/sequence-builder", test_seq_glue_sequence_builder);
g_test_add_func("/vte/parser/sequences/glue/reply-builder", test_seq_glue_reply_builder);
g_test_add_func("/vte/parser/sequences/control", test_seq_control);
g_test_add_func("/vte/parser/sequences/escape/invalid", test_seq_esc_invalid);
g_test_add_func("/vte/parser/sequences/escape/charset/94", test_seq_esc_charset_94);
g_test_add_func("/vte/parser/sequences/escape/charset/96", test_seq_esc_charset_96);
g_test_add_func("/vte/parser/sequences/escape/charset/94^n", test_seq_esc_charset_94_n);
g_test_add_func("/vte/parser/sequences/escape/charset/96^n", test_seq_esc_charset_96_n);
g_test_add_func("/vte/parser/sequences/escape/charset/control", test_seq_esc_charset_control);
g_test_add_func("/vte/parser/sequences/escape/charset/other", test_seq_esc_charset_other);
g_test_add_func("/vte/parser/sequences/escape/nF", test_seq_esc_nF);
g_test_add_func("/vte/parser/sequences/escape/F[pes]", test_seq_esc_Fpes);
g_test_add_func("/vte/parser/sequences/escape/known", test_seq_esc_known);
g_test_add_func("/vte/parser/sequences/csi", test_seq_csi);
g_test_add_func("/vte/parser/sequences/csi/known", test_seq_csi_known);
g_test_add_func("/vte/parser/sequences/csi/parameters", test_seq_csi_param);
g_test_add_func("/vte/parser/sequences/csi/clear", test_seq_csi_clear);
g_test_add_func("/vte/parser/sequences/csi/max", test_seq_csi_max);
g_test_add_func("/vte/parser/sequences/sci", test_seq_sci);
g_test_add_func("/vte/parser/sequences/sci/known", test_seq_sci_known);
g_test_add_func("/vte/parser/sequences/dcs", test_seq_dcs);
g_test_add_func("/vte/parser/sequences/dcs/known", test_seq_dcs_known);
g_test_add_func("/vte/parser/sequences/osc", test_seq_osc);
return g_test_run();
}