foot/tests/test-config.c

1455 lines
48 KiB
C
Raw Normal View History

#if !defined(_DEBUG)
#define _DEBUG
#endif
#undef NDEBUG
#include "../log.h"
#include "../config.c"
#define ALEN(v) (sizeof(v) / sizeof((v)[0]))
/*
* Stubs
*/
void
user_notification_add_fmt(user_notifications_t *notifications,
enum user_notification_kind kind,
const char *fmt, ...)
{
}
static void
test_invalid_key(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key)
{
ctx->key = key;
ctx->value = "value for invalid key";
if (parse_fun(ctx)) {
BUG("[%s].%s: did not fail to parse as expected"
"(key should be invalid)", ctx->section, ctx->key);
}
}
static void
test_string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, char *const *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
const char *value;
bool invalid;
} input[] = {
{"a string", "a string"},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (!streq(*ptr, input[i].value)) {
BUG("[%s].%s=%s: set value (%s) not the expected one (%s)",
ctx->section, ctx->key, ctx->value,
*ptr, input[i].value);
}
}
}
}
static void
test_c32string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, char32_t *const *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
const char32_t *value;
bool invalid;
} input[] = {
{"a string", U"a string"},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (c32cmp(*ptr, input[i].value) != 0) {
BUG("[%s].%s=%s: set value (%ls) not the expected one (%ls)",
ctx->section, ctx->key, ctx->value,
(const wchar_t *)*ptr,
(const wchar_t *)input[i].value);
}
}
}
}
static void
test_boolean(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, const bool *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
bool value;
bool invalid;
} input[] = {
{"1", true}, {"0", false},
{"on", true}, {"off", false},
{"true", true}, {"false", false},
{"unittest-invalid-boolean-value", false, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (*ptr != input[i].value) {
BUG("[%s].%s=%s: set value (%s) not the expected one (%s)",
ctx->section, ctx->key, ctx->value,
*ptr ? "true" : "false",
input[i].value ? "true" : "false");
}
}
}
}
static void
test_uint16(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, const uint16_t *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
uint16_t value;
bool invalid;
} input[] = {
{"0", 0}, {"65535", 65535}, {"65536", 0, true},
{"abc", 0, true}, {"true", 0, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (*ptr != input[i].value) {
BUG("[%s].%s=%s: set value (%hu) not the expected one (%hu)",
ctx->section, ctx->key, ctx->value,
*ptr, input[i].value);
}
}
}
}
static void
test_uint32(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, const uint32_t *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
uint32_t value;
bool invalid;
} input[] = {
{"0", 0}, {"65536", 65536}, {"4294967295", 4294967295},
{"4294967296", 0, true}, {"abc", 0, true}, {"true", 0, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (*ptr != input[i].value) {
BUG("[%s].%s=%s: set value (%u) not the expected one (%u)",
ctx->section, ctx->key, ctx->value,
*ptr, input[i].value);
}
}
}
}
static void
test_float(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, const float *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
float value;
bool invalid;
} input[] = {
{"0", 0}, {"0.1", 0.1}, {"1e10", 1e10}, {"-10.7", -10.7},
{"abc", 0, true}, {"true", 0, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (*ptr != input[i].value) {
BUG("[%s].%s=%s: set value (%f) not the expected one (%f)",
ctx->section, ctx->key, ctx->value,
*ptr, input[i].value);
}
}
}
}
static void
test_pt_or_px(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, const struct pt_or_px *ptr)
{
ctx->key = key;
static const struct {
const char *option_string;
struct pt_or_px value;
bool invalid;
} input[] = {
{"12", {.pt = 12}}, {"12px", {.px = 12}},
{"unittest-invalid-pt-or-px-value", {0}, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (memcmp(ptr, &input[i].value, sizeof(*ptr)) != 0) {
BUG("[%s].%s=%s: "
"set value (pt=%f, px=%d) not the expected one (pt=%f, px=%d)",
ctx->section, ctx->key, ctx->value,
ptr->pt, ptr->px,
input[i].value.pt, input[i].value.px);
}
}
}
}
static void
test_spawn_template(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, const struct config_spawn_template *ptr)
{
static const char *const args[] = {
"command", "arg1", "arg2", "arg3 has spaces"};
ctx->key = key;
ctx->value = "command arg1 arg2 \"arg3 has spaces\"";
if (!parse_fun(ctx))
BUG("[%s].%s=%s: failed to parse", ctx->section, ctx->key, ctx->value);
if (ptr->argv.args == NULL)
BUG("[%s].%s=%s: argv is NULL", ctx->section, ctx->key, ctx->value);
for (size_t i = 0; i < ALEN(args); i++) {
if (ptr->argv.args[i] == NULL || !streq(ptr->argv.args[i], args[i])) {
BUG("[%s].%s=%s: set value not the expected one: "
"mismatch of arg #%zu: expected=\"%s\", got=\"%s\"",
ctx->section, ctx->key, ctx->value, i,
args[i], ptr->argv.args[i]);
}
}
if (ptr->argv.args[ALEN(args)] != NULL) {
BUG("[%s].%s=%s: set value not the expected one: "
"expected NULL terminator at arg #%zu, got=\"%s\"",
ctx->section, ctx->key, ctx->value,
ALEN(args), ptr->argv.args[ALEN(args)]);
}
/* Trigger parse failure */
ctx->value = "command with \"unterminated quote";
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
}
static void
test_enum(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, size_t count, const char *enum_strings[static count],
int enum_values[static count], int *ptr)
{
ctx->key = key;
for (size_t i = 0; i < count; i++) {
ctx->value = enum_strings[i];
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (*ptr != enum_values[i]) {
BUG("[%s].%s=%s: set value not the expected one: expected %d, got %d",
ctx->section, ctx->key, ctx->value, enum_values[i], *ptr);
}
}
ctx->value = "invalid-enum-value";
if (parse_fun(ctx)) {
2022-02-07 12:32:23 +00:00
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
}
static void
test_color(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, bool alpha_allowed, uint32_t *ptr)
{
ctx->key = key;
const struct {
const char *option_string;
uint32_t color;
bool invalid;
} input[] = {
{"000000", 0},
2021-12-29 19:54:21 +01:00
{"999999", 0x999999},
{"ffffff", 0xffffff},
{"ffffffff", 0xffffffff, !alpha_allowed},
2021-12-29 19:54:21 +01:00
{"aabbccdd", 0xaabbccdd, !alpha_allowed},
{"00", 0, true},
{"0000", 0, true},
{"00000", 0, true},
{"000000000", 0, true},
{"unittest-invalid-color", 0, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
uint32_t color = input[i].color;
if (alpha_allowed && strlen(input[i].option_string) == 6)
color |= 0xff000000;
if (*ptr != color) {
BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x",
ctx->section, ctx->key, ctx->value,
color, *ptr);
}
}
}
}
static void
test_two_colors(struct context *ctx, bool (*parse_fun)(struct context *ctx),
const char *key, bool alpha_allowed,
uint32_t *ptr1, uint32_t *ptr2)
{
ctx->key = key;
const struct {
const char *option_string;
uint32_t color1;
uint32_t color2;
bool invalid;
} input[] = {
{"000000 000000", 0, 0},
/* No alpha */
{"999999 888888", 0x999999, 0x888888},
{"ffffff aaaaaa", 0xffffff, 0xaaaaaa},
/* Both colors have alpha component */
{"ffffffff 00000000", 0xffffffff, 0x00000000, !alpha_allowed},
{"aabbccdd, ee112233", 0xaabbccdd, 0xee112233, !alpha_allowed},
/* Only one color has alpha component */
{"ffffffff 112233", 0xffffffff, 0x112233, !alpha_allowed},
{"ffffff ff112233", 0x00ffffff, 0xff112233, !alpha_allowed},
{"unittest-invalid-color", 0, 0, true},
};
for (size_t i = 0; i < ALEN(input); i++) {
ctx->value = input[i].option_string;
if (input[i].invalid) {
if (parse_fun(ctx)) {
BUG("[%s].%s=%s: did not fail to parse as expected",
ctx->section, ctx->key, ctx->value);
}
} else {
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s: failed to parse",
ctx->section, ctx->key, ctx->value);
}
if (*ptr1 != input[i].color1) {
BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x",
ctx->section, ctx->key, ctx->value,
input[i].color1, *ptr1);
}
if (*ptr2 != input[i].color2) {
BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x",
ctx->section, ctx->key, ctx->value,
input[i].color2, *ptr2);
}
}
}
}
static void
test_section_main(void)
{
struct config conf = {0};
struct context ctx = {.conf = &conf, .section = "main", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_main, "invalid-key");
test_string(&ctx, &parse_section_main, "shell", &conf.shell);
test_string(&ctx, &parse_section_main, "term", &conf.term);
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id);
test_string(&ctx, &parse_section_main, "utmp-helper", &conf.utmp_helper_path);
test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);
test_boolean(&ctx, &parse_section_main, "login-shell", &conf.login_shell);
test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs);
test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title);
test_boolean(&ctx, &parse_section_main, "dpi-aware", &conf.dpi_aware);
test_boolean(&ctx, &parse_section_main, "gamma-correct-blending", &conf.gamma_correct);
test_boolean(&ctx, &parse_section_main, "uppercase-regex-insert", &conf.uppercase_regex_insert);
test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test N% values too */
test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height);
test_pt_or_px(&ctx, &parse_section_main, "letter-spacing", &conf.letter_spacing);
test_pt_or_px(&ctx, &parse_section_main, "horizontal-letter-offset", &conf.horizontal_letter_offset);
test_pt_or_px(&ctx, &parse_section_main, "vertical-letter-offset", &conf.vertical_letter_offset);
test_pt_or_px(&ctx, &parse_section_main, "underline-thickness", &conf.underline_thickness);
2024-08-25 11:28:21 +03:00
test_pt_or_px(&ctx, &parse_section_main, "strikeout-thickness", &conf.strikeout_thickness);
test_uint16(&ctx, &parse_section_main, "resize-delay-ms", &conf.resize_delay_ms);
test_uint16(&ctx, &parse_section_main, "workers", &conf.render_worker_count);
test_enum(&ctx, &parse_section_main, "selection-target",
4,
(const char *[]){"none", "primary", "clipboard", "both"},
(int []){SELECTION_TARGET_NONE,
SELECTION_TARGET_PRIMARY,
SELECTION_TARGET_CLIPBOARD,
SELECTION_TARGET_BOTH},
(int *)&conf.selection_target);
test_enum(
&ctx, &parse_section_main, "initial-window-mode",
3,
(const char *[]){"windowed", "maximized", "fullscreen"},
(int []){STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN},
(int *)&conf.startup_mode);
/* TODO: font (custom) */
/* TODO: include (custom) */
/* TODO: bold-text-in-bright (enum/boolean) */
/* TODO: pad (geometry + optional string)*/
/* TODO: initial-window-size-pixels (geometry) */
/* TODO: initial-window-size-chars (geometry) */
config_free(&conf);
}
static void
test_section_security(void)
{
struct config conf = {0};
struct context ctx = {.conf = &conf, .section = "security", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_security, "invalid-key");
test_enum(
&ctx, &parse_section_security, "osc52", 4,
(const char*[]){"disabled", "copy-enabled", "paste-enabled", "enabled"},
(int []){OSC52_DISABLED, OSC52_COPY_ENABLED, OSC52_PASTE_ENABLED, OSC52_ENABLED},
(int *)&conf.security.osc52);
config_free(&conf);
}
2021-12-25 23:03:11 +01:00
static void
test_section_bell(void)
{
struct config conf = {0};
struct context ctx = {.conf = &conf, .section = "bell", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_bell, "invalid-key");
test_boolean(&ctx, &parse_section_bell, "urgent", &conf.bell.urgent);
test_boolean(&ctx, &parse_section_bell, "notify", &conf.bell.notify);
test_boolean(&ctx, &parse_section_bell, "system", &conf.bell.system_bell);
2021-12-25 23:03:11 +01:00
test_boolean(&ctx, &parse_section_bell, "command-focused",
&conf.bell.command_focused);
test_spawn_template(&ctx, &parse_section_bell, "command",
&conf.bell.command);
2021-12-25 23:03:11 +01:00
config_free(&conf);
2021-12-25 23:03:11 +01:00
}
2024-07-23 06:59:46 +02:00
static void
test_section_desktop_notifications(void)
{
struct config conf = {0};
struct context ctx = {.conf = &conf, .section = "desktop-notifications", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_desktop_notifications, "invalid-key");
test_boolean(&ctx, &parse_section_desktop_notifications, "inhibit-when-focused", &conf.desktop_notifications.inhibit_when_focused);
test_spawn_template(&ctx, &parse_section_desktop_notifications, "command", &conf.desktop_notifications.command);
test_spawn_template(&ctx, &parse_section_desktop_notifications, "command-action-argument", &conf.desktop_notifications.command_action_arg);
test_spawn_template(&ctx, &parse_section_desktop_notifications, "close", &conf.desktop_notifications.close);
2024-07-23 06:59:46 +02:00
config_free(&conf);
}
static void
test_section_scrollback(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "scrollback", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_scrollback, "invalid-key");
test_uint32(&ctx, &parse_section_scrollback, "lines",
&conf.scrollback.lines);
test_float(&ctx, parse_section_scrollback, "multiplier", &conf.scrollback.multiplier);
test_enum(
&ctx, &parse_section_scrollback, "indicator-position",
3,
(const char *[]){"none", "fixed", "relative"},
(int []){SCROLLBACK_INDICATOR_POSITION_NONE,
SCROLLBACK_INDICATOR_POSITION_FIXED,
SCROLLBACK_INDICATOR_POSITION_RELATIVE},
(int *)&conf.scrollback.indicator.position);
/* TODO: indicator-format (enum, sort-of) */
config_free(&conf);
}
2021-12-29 19:10:49 +01:00
static void
test_section_url(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "url", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_url, "invalid-key");
test_spawn_template(&ctx, &parse_section_url, "launch", &conf.url.launch);
test_enum(&ctx, &parse_section_url, "osc8-underline",
2,
(const char *[]){"url-mode", "always"},
(int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS},
(int *)&conf.url.osc8_underline);
test_c32string(&ctx, &parse_section_url, "label-letters", &conf.url.label_letters);
2021-12-29 19:10:49 +01:00
config_free(&conf);
2021-12-29 19:10:49 +01:00
}
2021-12-29 19:15:15 +01:00
static void
test_section_cursor(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "cursor", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_cursor, "invalid-key");
test_enum(
&ctx, &parse_section_cursor, "style",
3,
(const char *[]){"block", "beam", "underline"},
(int []){CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE},
(int *)&conf.cursor.style);
test_enum(
&ctx, &parse_section_cursor, "unfocused-style",
3,
(const char *[]){"unchanged", "hollow", "none"},
(int []){CURSOR_UNFOCUSED_UNCHANGED, CURSOR_UNFOCUSED_HOLLOW, CURSOR_UNFOCUSED_NONE},
(int *)&conf.cursor.unfocused_style);
test_boolean(&ctx, &parse_section_cursor, "blink", &conf.cursor.blink.enabled);
test_uint32(&ctx, &parse_section_cursor, "blink-rate", &conf.cursor.blink.rate_ms);
2021-12-29 19:15:15 +01:00
test_pt_or_px(&ctx, &parse_section_cursor, "beam-thickness",
&conf.cursor.beam_thickness);
test_pt_or_px(&ctx, &parse_section_cursor, "underline-thickness",
&conf.cursor.underline_thickness);
/* TODO: color (two RRGGBB values) */
config_free(&conf);
2021-12-29 19:15:15 +01:00
}
2021-12-29 19:18:25 +01:00
static void
test_section_mouse(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "mouse", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_mouse, "invalid-key");
test_boolean(&ctx, &parse_section_mouse, "hide-when-typing",
&conf.mouse.hide_when_typing);
test_boolean(&ctx, &parse_section_mouse, "alternate-scroll-mode",
&conf.mouse.alternate_scroll_mode);
config_free(&conf);
2021-12-29 19:18:25 +01:00
}
static void
test_section_touch(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "touch", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_touch, "invalid-key");
test_uint32(&ctx, &parse_section_touch, "long-press-delay",
&conf.touch.long_press_delay);
config_free(&conf);
}
2021-12-29 19:54:21 +01:00
static void
test_section_colors(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "colors", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_colors, "invalid-key");
test_color(&ctx, &parse_section_colors, "foreground", false, &conf.colors.fg);
test_color(&ctx, &parse_section_colors, "background", false, &conf.colors.bg);
test_color(&ctx, &parse_section_colors, "regular0", false, &conf.colors.table[0]);
test_color(&ctx, &parse_section_colors, "regular1", false, &conf.colors.table[1]);
test_color(&ctx, &parse_section_colors, "regular2", false, &conf.colors.table[2]);
test_color(&ctx, &parse_section_colors, "regular3", false, &conf.colors.table[3]);
test_color(&ctx, &parse_section_colors, "regular4", false, &conf.colors.table[4]);
test_color(&ctx, &parse_section_colors, "regular5", false, &conf.colors.table[5]);
test_color(&ctx, &parse_section_colors, "regular6", false, &conf.colors.table[6]);
test_color(&ctx, &parse_section_colors, "regular7", false, &conf.colors.table[7]);
test_color(&ctx, &parse_section_colors, "bright0", false, &conf.colors.table[8]);
test_color(&ctx, &parse_section_colors, "bright1", false, &conf.colors.table[9]);
test_color(&ctx, &parse_section_colors, "bright2", false, &conf.colors.table[10]);
test_color(&ctx, &parse_section_colors, "bright3", false, &conf.colors.table[11]);
test_color(&ctx, &parse_section_colors, "bright4", false, &conf.colors.table[12]);
test_color(&ctx, &parse_section_colors, "bright5", false, &conf.colors.table[13]);
test_color(&ctx, &parse_section_colors, "bright6", false, &conf.colors.table[14]);
test_color(&ctx, &parse_section_colors, "bright7", false, &conf.colors.table[15]);
test_color(&ctx, &parse_section_colors, "dim0", false, &conf.colors.dim[0]);
test_color(&ctx, &parse_section_colors, "dim1", false, &conf.colors.dim[1]);
test_color(&ctx, &parse_section_colors, "dim2", false, &conf.colors.dim[2]);
test_color(&ctx, &parse_section_colors, "dim3", false, &conf.colors.dim[3]);
test_color(&ctx, &parse_section_colors, "dim4", false, &conf.colors.dim[4]);
test_color(&ctx, &parse_section_colors, "dim5", false, &conf.colors.dim[5]);
test_color(&ctx, &parse_section_colors, "dim6", false, &conf.colors.dim[6]);
test_color(&ctx, &parse_section_colors, "dim7", false, &conf.colors.dim[7]);
test_color(&ctx, &parse_section_colors, "selection-foreground", false, &conf.colors.selection_fg);
test_color(&ctx, &parse_section_colors, "selection-background", false, &conf.colors.selection_bg);
test_color(&ctx, &parse_section_colors, "urls", false, &conf.colors.url);
test_two_colors(&ctx, &parse_section_colors, "jump-labels", false,
&conf.colors.jump_label.fg,
&conf.colors.jump_label.bg);
test_two_colors(&ctx, &parse_section_colors, "scrollback-indicator", false,
&conf.colors.scrollback_indicator.fg,
&conf.colors.scrollback_indicator.bg);
test_two_colors(&ctx, &parse_section_colors, "search-box-no-match", false,
&conf.colors.search_box.no_match.fg,
&conf.colors.search_box.no_match.bg);
test_two_colors(&ctx, &parse_section_colors, "search-box-match", false,
&conf.colors.search_box.match.fg,
&conf.colors.search_box.match.bg);
2021-12-29 19:54:21 +01:00
test_two_colors(&ctx, &parse_section_colors, "cursor", false,
&conf.colors.cursor.text,
&conf.colors.cursor.cursor);
2025-04-14 17:00:07 +02:00
test_enum(&ctx, &parse_section_colors, "alpha-mode", 3,
(const char *[]){"default", "matching", "all"},
(int []){ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL},
(int *)&conf.colors.alpha_mode);
2021-12-29 19:54:21 +01:00
for (size_t i = 0; i < 255; i++) {
char key_name[4];
sprintf(key_name, "%zu", i);
test_color(&ctx, &parse_section_colors, key_name, false,
&conf.colors.table[i]);
}
test_invalid_key(&ctx, &parse_section_colors, "256");
2021-12-29 19:54:21 +01:00
/* TODO: alpha (float in range 0-1, converted to uint16_t) */
config_free(&conf);
2021-12-29 19:54:21 +01:00
}
2021-12-29 19:33:18 +01:00
static void
test_section_csd(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "csd", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_csd, "invalid-key");
test_enum(
&ctx, &parse_section_csd, "preferred",
3,
(const char *[]){"none", "client", "server"},
(int []){CONF_CSD_PREFER_NONE,
CONF_CSD_PREFER_CLIENT,
CONF_CSD_PREFER_SERVER},
(int *)&conf.csd.preferred);
test_uint16(&ctx, &parse_section_csd, "size", &conf.csd.title_height);
2021-12-29 19:43:11 +01:00
test_color(&ctx, &parse_section_csd, "color", true, &conf.csd.color.title);
2021-12-29 19:33:18 +01:00
test_uint16(&ctx, &parse_section_csd, "border-width",
&conf.csd.border_width_visible);
2021-12-29 19:43:11 +01:00
test_color(&ctx, &parse_section_csd, "border-color", true,
&conf.csd.color.border);
2021-12-29 19:33:18 +01:00
test_uint16(&ctx, &parse_section_csd, "button-width",
&conf.csd.button_width);
2021-12-29 19:43:11 +01:00
test_color(&ctx, &parse_section_csd, "button-color", true,
&conf.csd.color.buttons);
test_color(&ctx, &parse_section_csd, "button-minimize-color", true,
&conf.csd.color.minimize);
test_color(&ctx, &parse_section_csd, "button-maximize-color", true,
&conf.csd.color.maximize);
test_color(&ctx, &parse_section_csd, "button-close-color", true,
&conf.csd.color.quit);
test_boolean(&ctx, &parse_section_csd, "hide-when-maximized",
&conf.csd.hide_when_maximized);
test_boolean(&ctx, &parse_section_csd, "double-click-to-maximize",
&conf.csd.double_click_to_maximize);
2021-12-29 19:43:11 +01:00
/* TODO: verify the set bit is actually set for colors */
2021-12-29 19:33:18 +01:00
/* TODO: font */
config_free(&conf);
2021-12-29 19:33:18 +01:00
}
static bool
have_modifier(const config_modifier_list_t *mods, const char *mod)
{
tll_foreach(*mods, it) {
if (strcmp(it->item, mod) == 0)
return true;
}
return false;
}
static void
test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx),
int action, int max_action, const char *const *map,
struct config_key_binding_list *bindings,
enum key_binding_type type, bool need_argv, bool need_section_id)
{
xassert(map[action] != NULL);
xassert(bindings->count == 0);
const char *key = map[action];
/* “Randomize” which modifiers to enable */
const bool ctrl = action % 2;
const bool alt = action % 3;
const bool shift = action % 4;
const bool super = action % 5;
const bool argv = need_argv;
const bool section_id = need_section_id;
xassert(!(argv && section_id));
static const char *const args[] = {
"command", "arg1", "arg2", "arg3 has spaces"};
/* Generate the modifier part of the value */
char modifier_string[32];
sprintf(modifier_string, "%s%s%s%s",
ctrl ? XKB_MOD_NAME_CTRL "+" : "",
alt ? XKB_MOD_NAME_ALT "+" : "",
shift ? XKB_MOD_NAME_SHIFT "+" : "",
super ? XKB_MOD_NAME_LOGO "+" : "");
/* Use a unique symbol for this action (key bindings) */
const xkb_keysym_t sym = XKB_KEY_a + action;
/* Mouse button (mouse bindings) */
const int button_idx = action % ALEN(button_map);
const int button = button_map[button_idx].code;
const int click_count = action % 3 + 1;
/* Finally, generate the value (e.g. “Control+shift+x”) */
char value[128] = {0};
ctx->key = key;
ctx->value = value;
/* First, try setting the empty string */
if (parse_fun(ctx)) {
BUG("[%s].%s=<empty>: did not fail to parse as expected",
ctx->section, ctx->key);
}
switch (type) {
case KEY_BINDING: {
char sym_name[16];
xkb_keysym_get_name(sym, sym_name, sizeof(sym_name));
snprintf(value, sizeof(value), "%s%s%s",
argv ? "[command arg1 arg2 \"arg3 has spaces\"] " : section_id ? "[foobar]" : "",
modifier_string, sym_name);
break;
}
case MOUSE_BINDING: {
const char *const button_name = button_map[button_idx].name;
int chars = snprintf(
value, sizeof(value), "%s%s%s",
argv ? "[command arg1 arg2 \"arg3 has spaces\"] " : section_id ? "[foobar]" : "",
modifier_string, button_name);
xassert(click_count > 0);
if (click_count > 1)
snprintf(&value[chars], sizeof(value) - chars, "-%d", click_count);
break;
}
}
if (!parse_fun(ctx)) {
BUG("[%s].%s=%s failed to parse",
ctx->section, ctx->key, ctx->value);
}
const struct config_key_binding *binding =
&bindings->arr[bindings->count - 1];
if (argv) {
if (binding->aux.pipe.args == NULL) {
BUG("[%s].%s=%s: pipe argv is NULL",
ctx->section, ctx->key, ctx->value);
}
for (size_t i = 0; i < ALEN(args); i++) {
if (binding->aux.pipe.args[i] == NULL ||
!streq(binding->aux.pipe.args[i], args[i]))
{
BUG("[%s].%s=%s: pipe argv not the expected one: "
"mismatch of arg #%zu: expected=\"%s\", got=\"%s\"",
ctx->section, ctx->key, ctx->value, i,
args[i], binding->aux.pipe.args[i]);
}
}
if (binding->aux.pipe.args[ALEN(args)] != NULL) {
BUG("[%s].%s=%s: pipe argv not the expected one: "
"expected NULL terminator at arg #%zu, got=\"%s\"",
ctx->section, ctx->key, ctx->value,
ALEN(args), binding->aux.pipe.args[ALEN(args)]);
}
} else if (section_id) {
if (binding->aux.regex_name == NULL) {
BUG("[%s].%s=%s: regex name is NULL",
ctx->section, ctx->key, ctx->value);
}
if (!streq(binding->aux.regex_name, "foobar")) {
BUG("[%s].%s=%s: regex name not the expected one: "
"expected=\"%s\", got=\"%s\"",
ctx->section, ctx->key, ctx->value,
"foobar", binding->aux.regex_name);
}
} else {
if (binding->aux.pipe.args != NULL) {
BUG("[%s].%s=%s: pipe argv not NULL",
ctx->section, ctx->key, ctx->value);
}
}
if (binding->action != action) {
BUG("[%s].%s=%s: action mismatch: %d != %d",
ctx->section, ctx->key, ctx->value, binding->action, action);
}
bool have_ctrl = have_modifier(&binding->modifiers, XKB_MOD_NAME_CTRL);
bool have_alt = have_modifier(&binding->modifiers, XKB_MOD_NAME_ALT);
bool have_shift = have_modifier(&binding->modifiers, XKB_MOD_NAME_SHIFT);
bool have_super = have_modifier(&binding->modifiers, XKB_MOD_NAME_LOGO);
if (have_ctrl != ctrl || have_alt != alt ||
have_shift != shift || have_super != super)
{
BUG("[%s].%s=%s: modifier mismatch:\n"
" have: ctrl=%d, alt=%d, shift=%d, super=%d\n"
" expected: ctrl=%d, alt=%d, shift=%d, super=%d",
ctx->section, ctx->key, ctx->value,
have_ctrl, have_alt, have_shift, have_super,
ctrl, alt, shift, super);
}
switch (type) {
case KEY_BINDING:
if (binding->k.sym != sym) {
BUG("[%s].%s=%s: key symbol mismatch: %d != %d",
ctx->section, ctx->key, ctx->value, binding->k.sym, sym);
}
break;
case MOUSE_BINDING:;
if (binding->m.button != button) {
BUG("[%s].%s=%s: mouse button mismatch: %d != %d",
ctx->section, ctx->key, ctx->value, binding->m.button, button);
}
if (binding->m.count != click_count) {
BUG("[%s].%s=%s: mouse button click count mismatch: %d != %d",
ctx->section, ctx->key, ctx->value,
binding->m.count, click_count);
}
break;
}
free_key_binding_list(bindings);
}
enum collision_test_mode {
FAIL_DIFFERENT_ACTION,
FAIL_DIFFERENT_ARGV,
FAIL_MOUSE_OVERRIDE,
2023-03-29 00:31:49 +03:00
SUCCEED_SAME_ACTION_AND_ARGV,
};
2021-12-11 18:49:59 +01:00
static void
_test_binding_collisions(struct context *ctx,
int max_action, const char *const *map,
enum key_binding_type type,
enum collision_test_mode test_mode)
2021-12-11 18:49:59 +01:00
{
struct config_key_binding *bindings_array =
xcalloc(2, sizeof(bindings_array[0]));
2021-12-11 18:49:59 +01:00
struct config_key_binding_list bindings = {
.count = 2,
.arr = bindings_array,
};
2021-12-11 18:49:59 +01:00
/* First, verify we get a collision when trying to assign the same
* key combo to multiple actions */
bindings.arr[0] = (struct config_key_binding){
.action = (test_mode == FAIL_DIFFERENT_ACTION
? max_action - 1 : max_action),
.modifiers = tll_init(),
.path = "unittest",
};
tll_push_back(bindings.arr[0].modifiers, xstrdup(XKB_MOD_NAME_CTRL));
bindings.arr[1] = (struct config_key_binding){
.action = max_action,
.modifiers = tll_init(),
.path = "unittest",
};
tll_push_back(bindings.arr[1].modifiers, xstrdup(XKB_MOD_NAME_CTRL));
2021-12-11 18:49:59 +01:00
switch (type) {
case KEY_BINDING:
bindings.arr[0].k.sym = XKB_KEY_a;
bindings.arr[1].k.sym = XKB_KEY_a;
break;
case MOUSE_BINDING:
bindings.arr[0].m.button = BTN_LEFT;
bindings.arr[0].m.count = 1;
bindings.arr[1].m.button = BTN_LEFT;
bindings.arr[1].m.count = 1;
break;
2021-12-11 18:49:59 +01:00
}
switch (test_mode) {
case FAIL_DIFFERENT_ACTION:
break;
case FAIL_MOUSE_OVERRIDE:
tll_free_and_free(ctx->conf->mouse.selection_override_modifiers, free);
tll_push_back(ctx->conf->mouse.selection_override_modifiers, xstrdup(XKB_MOD_NAME_CTRL));
break;
case FAIL_DIFFERENT_ARGV:
2023-03-29 00:31:49 +03:00
case SUCCEED_SAME_ACTION_AND_ARGV:
bindings.arr[0].aux.type = BINDING_AUX_PIPE;
bindings.arr[0].aux.master_copy = true;
bindings.arr[0].aux.pipe.args = xcalloc(
4, sizeof(bindings.arr[0].aux.pipe.args[0]));
bindings.arr[0].aux.pipe.args[0] = xstrdup("/usr/bin/foobar");
bindings.arr[0].aux.pipe.args[1] = xstrdup("hello");
bindings.arr[0].aux.pipe.args[2] = xstrdup("world");
bindings.arr[1].aux.type = BINDING_AUX_PIPE;
bindings.arr[1].aux.master_copy = true;
bindings.arr[1].aux.pipe.args = xcalloc(
4, sizeof(bindings.arr[1].aux.pipe.args[0]));
bindings.arr[1].aux.pipe.args[0] = xstrdup("/usr/bin/foobar");
bindings.arr[1].aux.pipe.args[1] = xstrdup("hello");
2023-03-29 00:31:49 +03:00
if (test_mode == SUCCEED_SAME_ACTION_AND_ARGV)
bindings.arr[1].aux.pipe.args[2] = xstrdup("world");
break;
2021-12-11 18:49:59 +01:00
}
bool expected_result =
2023-03-29 00:31:49 +03:00
test_mode == SUCCEED_SAME_ACTION_AND_ARGV ? true : false;
2021-12-11 18:49:59 +01:00
if (resolve_key_binding_collisions(
ctx->conf, ctx->section, map, &bindings, type) != expected_result)
2021-12-11 18:49:59 +01:00
{
BUG("[%s].%s vs. %s: %s",
ctx->section, map[max_action - 1], map[max_action],
(expected_result == true
? "invalid key combo collision detected"
: "key combo collision not detected"));
2021-12-11 18:49:59 +01:00
}
if (expected_result == false) {
if (bindings.count != 1)
BUG("[%s]: colliding binding not removed", ctx->section);
2021-12-11 18:49:59 +01:00
if (bindings.arr[0].action !=
(test_mode == FAIL_DIFFERENT_ACTION ? max_action - 1 : max_action))
{
BUG("[%s]: wrong binding removed", ctx->section);
}
2021-12-11 18:49:59 +01:00
}
free_key_binding_list(&bindings);
}
2021-12-11 18:49:59 +01:00
static void
test_binding_collisions(struct context *ctx,
int max_action, const char *const *map,
enum key_binding_type type)
{
_test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ACTION);
_test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ARGV);
2023-03-29 00:31:49 +03:00
_test_binding_collisions(ctx, max_action, map, type, SUCCEED_SAME_ACTION_AND_ARGV);
if (type == MOUSE_BINDING) {
_test_binding_collisions(
ctx, max_action, map, type, FAIL_MOUSE_OVERRIDE);
}
2021-12-11 18:49:59 +01:00
}
static void
test_section_key_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "key-bindings", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_key_bindings, "invalid-key");
for (int action = 0; action < BIND_ACTION_KEY_COUNT; action++) {
if (binding_action_map[action] == NULL)
continue;
test_key_binding(
&ctx, &parse_section_key_bindings,
action, BIND_ACTION_KEY_COUNT - 1,
binding_action_map, &conf.bindings.key, KEY_BINDING,
action >= BIND_ACTION_PIPE_SCROLLBACK && action <= BIND_ACTION_PIPE_COMMAND_OUTPUT,
action >= BIND_ACTION_REGEX_LAUNCH && action <= BIND_ACTION_REGEX_COPY);
}
config_free(&conf);
}
static void
test_section_key_bindings_collisions(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "key-bindings", .path = "unittest"};
test_binding_collisions(
&ctx, BIND_ACTION_KEY_COUNT - 1, binding_action_map, KEY_BINDING);
config_free(&conf);
}
static void
test_section_search_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "search-bindings", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_search_bindings, "invalid-key");
for (int action = 0; action < BIND_ACTION_SEARCH_COUNT; action++) {
if (search_binding_action_map[action] == NULL)
continue;
test_key_binding(
&ctx, &parse_section_search_bindings,
action, BIND_ACTION_SEARCH_COUNT - 1,
search_binding_action_map, &conf.bindings.search, KEY_BINDING,
false, false);
}
config_free(&conf);
}
static void
test_section_search_bindings_collisions(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "search-bindings", .path = "unittest"};
test_binding_collisions(
&ctx,
BIND_ACTION_SEARCH_COUNT - 1, search_binding_action_map, KEY_BINDING);
config_free(&conf);
}
static void
test_section_url_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "rul-bindings", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_url_bindings, "invalid-key");
for (int action = 0; action < BIND_ACTION_URL_COUNT; action++) {
if (url_binding_action_map[action] == NULL)
continue;
test_key_binding(
&ctx, &parse_section_url_bindings,
action, BIND_ACTION_URL_COUNT - 1,
url_binding_action_map, &conf.bindings.url, KEY_BINDING,
false, false);
}
config_free(&conf);
}
static void
test_section_url_bindings_collisions(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "url-bindings", .path = "unittest"};
test_binding_collisions(
&ctx,
BIND_ACTION_URL_COUNT - 1, url_binding_action_map, KEY_BINDING);
config_free(&conf);
}
2021-12-11 18:49:59 +01:00
static void
test_section_mouse_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "mouse-bindings", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_mouse_bindings, "invalid-key");
for (int action = 0; action < BIND_ACTION_COUNT; action++) {
if (binding_action_map[action] == NULL)
continue;
test_key_binding(
2021-12-11 18:49:59 +01:00
&ctx, &parse_section_mouse_bindings,
action, BIND_ACTION_COUNT - 1,
binding_action_map, &conf.bindings.mouse, MOUSE_BINDING,
false, false);
2021-12-11 18:49:59 +01:00
}
config_free(&conf);
2021-12-11 18:49:59 +01:00
}
static void
test_section_mouse_bindings_collisions(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "mouse-bindings", .path = "unittest"};
test_binding_collisions(
&ctx,
BIND_ACTION_COUNT - 1, binding_action_map, MOUSE_BINDING);
config_free(&conf);
}
static void
test_section_text_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "text-bindings", .path = "unittest"};
ctx.key = "abcd";
ctx.value = XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT "+x";
xassert(parse_section_text_bindings(&ctx));
ctx.key = "\\x07";
xassert(parse_section_text_bindings(&ctx));
ctx.key = "\\x1g";
xassert(!parse_section_text_bindings(&ctx));
ctx.key = "\\x1";
xassert(!parse_section_text_bindings(&ctx));
ctx.key = "\\x";
xassert(!parse_section_text_bindings(&ctx));
ctx.key = "\\";
xassert(!parse_section_text_bindings(&ctx));
ctx.key = "\\y";
xassert(!parse_section_text_bindings(&ctx));
#if 0
/* Invalid modifier and key names are detected later, when a
* layout is applied */
ctx.key = "abcd";
ctx.value = "InvalidMod+y";
xassert(!parse_section_text_bindings(&ctx));
#endif
config_free(&conf);
}
static void
test_section_environment(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "environment", .path = "unittest"};
/* A single variable */
ctx.key = "FOO";
ctx.value = "bar";
xassert(parse_section_environment(&ctx));
xassert(tll_length(conf.env_vars) == 1);
xassert(streq(tll_front(conf.env_vars).name, "FOO"));
xassert(streq(tll_front(conf.env_vars).value, "bar"));
/* Add a second variable */
ctx.key = "BAR";
ctx.value = "123";
xassert(parse_section_environment(&ctx));
xassert(tll_length(conf.env_vars) == 2);
xassert(streq(tll_back(conf.env_vars).name, "BAR"));
xassert(streq(tll_back(conf.env_vars).value, "123"));
/* Replace the *value* of the first variable */
ctx.key = "FOO";
ctx.value = "456";
xassert(parse_section_environment(&ctx));
xassert(tll_length(conf.env_vars) == 2);
xassert(streq(tll_front(conf.env_vars).name, "FOO"));
xassert(streq(tll_front(conf.env_vars).value, "456"));
xassert(streq(tll_back(conf.env_vars).name, "BAR"));
xassert(streq(tll_back(conf.env_vars).value, "123"));
config_free(&conf);
}
2022-01-13 12:16:57 +01:00
static void
test_section_tweak(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "tweak", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_tweak, "invalid-key");
test_enum(
&ctx, &parse_section_tweak, "scaling-filter",
5,
(const char *[]){"none", "nearest", "bilinear", "cubic", "lanczos3"},
(int []){FCFT_SCALING_FILTER_NONE,
FCFT_SCALING_FILTER_NEAREST,
FCFT_SCALING_FILTER_BILINEAR,
FCFT_SCALING_FILTER_CUBIC,
FCFT_SCALING_FILTER_LANCZOS3},
(int *)&conf.tweak.fcft_filter);
test_boolean(&ctx, &parse_section_tweak, "overflowing-glyphs",
&conf.tweak.overflowing_glyphs);
test_enum(
&ctx, &parse_section_tweak, "render-timer",
4,
(const char *[]){"none", "osd", "log", "both"},
(int []){RENDER_TIMER_NONE,
RENDER_TIMER_OSD,
RENDER_TIMER_LOG,
RENDER_TIMER_BOTH},
(int *)&conf.tweak.render_timer);
test_float(&ctx, &parse_section_tweak, "box-drawing-base-thickness",
2022-01-13 12:16:57 +01:00
&conf.tweak.box_drawing_base_thickness);
test_boolean(&ctx, &parse_section_tweak, "box-drawing-solid-shades",
&conf.tweak.box_drawing_solid_shades);
#if 0 /* Must be less than 16ms */
test_uint32(&ctx, &parse_section_tweak, "delayed-render-lower",
&conf.tweak.delayed_render_lower_ns);
test_uint32(&ctx, &parse_section_tweak, "delayed-render-upper",
&conf.tweak.delayed_render_upper_ns);
#endif
test_boolean(&ctx, &parse_section_tweak, "damage-whole-window",
&conf.tweak.damage_whole_window);
#if defined(FOOT_GRAPHEME_CLUSTERING)
2022-01-13 12:16:57 +01:00
test_boolean(&ctx, &parse_section_tweak, "grapheme-shaping",
&conf.tweak.grapheme_shaping);
#else
/* TODO: the setting still exists, but is always forced to false. */
#endif
2022-01-13 12:16:57 +01:00
test_enum(
&ctx, &parse_section_tweak, "grapheme-width-method",
3,
(const char *[]){"wcswidth", "double-width", "max"},
(int []){GRAPHEME_WIDTH_WCSWIDTH,
GRAPHEME_WIDTH_DOUBLE,
GRAPHEME_WIDTH_MAX},
(int *)&conf.tweak.grapheme_width_method);
test_boolean(&ctx, &parse_section_tweak, "font-monospace-warn",
&conf.tweak.font_monospace_warn);
test_float(&ctx, &parse_section_tweak, "bold-text-in-bright-amount",
&conf.bold_in_bright.amount);
2022-01-13 12:16:57 +01:00
#if 0 /* Must be equal to, or less than INT32_MAX */
test_uint32(&ctx, &parse_section_tweak, "max-shm-pool-size-mb",
&conf.tweak.max_shm_pool_size);
#endif
config_free(&conf);
2022-01-13 12:16:57 +01:00
}
int
main(int argc, const char *const *argv)
{
FcInit();
log_init(LOG_COLORIZE_AUTO, false, 0, LOG_CLASS_ERROR);
test_section_main();
test_section_security();
2021-12-25 23:03:11 +01:00
test_section_bell();
2024-07-23 06:59:46 +02:00
test_section_desktop_notifications();
test_section_scrollback();
2021-12-29 19:10:49 +01:00
test_section_url();
2021-12-29 19:15:15 +01:00
test_section_cursor();
2021-12-29 19:18:25 +01:00
test_section_mouse();
test_section_touch();
2021-12-29 19:54:21 +01:00
test_section_colors();
2021-12-29 19:33:18 +01:00
test_section_csd();
test_section_key_bindings();
test_section_key_bindings_collisions();
test_section_search_bindings();
test_section_search_bindings_collisions();
test_section_url_bindings();
test_section_url_bindings_collisions();
2021-12-11 18:49:59 +01:00
test_section_mouse_bindings();
test_section_mouse_bindings_collisions();
test_section_text_bindings();
test_section_environment();
2022-01-13 12:16:57 +01:00
test_section_tweak();
log_deinit();
FcFini();
return 0;
}