diff --git a/tests/test-config.c b/tests/test-config.c index befe4864..94e211f0 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -265,7 +265,8 @@ test_section_main(void) 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) + struct config_key_binding_list *bindings, + enum config_key_binding_type type) { xassert(map[action] != NULL); xassert(bindings->count == 0); @@ -280,20 +281,43 @@ test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx), /* Generate the modifier part of the ‘value’ */ char modifier_string[32]; - int chars = 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 "+" : ""); + 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 */ - xkb_keysym_t sym = XKB_KEY_a + action; - char sym_name[8]; - xkb_keysym_get_name(sym, sym_name, sizeof(sym_name)); + /* 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[chars + strlen(sym_name) + 1]; - sprintf(value, "%s%s", modifier_string, sym_name); + char value[128]; + + 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", 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", modifier_string, button_name); + + xassert(click_count > 0); + if (click_count > 1) + snprintf(&value[chars], sizeof(value) - chars, "-%d", click_count); + break; + } + } ctx->key = key; ctx->value = value; @@ -313,242 +337,153 @@ test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx), ctx->section, ctx->key, ctx->value, binding->action, action); } - if (binding->sym != sym) { - BUG("[%s].%s=%s: key symbol mismatch: %d != %d", - ctx->section, ctx->key, ctx->value, binding->sym, sym); - } - if (binding->modifiers.ctrl != ctrl || binding->modifiers.alt != alt || binding->modifiers.shift != shift || - binding->modifiers.meta != super) + binding->modifiers.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, binding->modifiers.ctrl, binding->modifiers.alt, - binding->modifiers.shift, binding->modifiers.meta, + binding->modifiers.shift, binding->modifiers.super, ctrl, alt, shift, super); } - key_binding_list_free(bindings); - bindings->arr = NULL; - bindings->count = 0; + 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; - if (action >= max_action) - return; + 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); + } - /* - * Test collisions - */ + 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, + SUCCED_SAME_ACTION_AND_ARGV +}; + +static void +_test_binding_collisions(struct context *ctx, + int max_action, const char *const *map, + enum config_key_binding_type type, + enum collision_test_mode test_mode) +{ + struct config_key_binding *bindings_array = + xcalloc(2, sizeof(bindings_array[0])); + + struct config_key_binding_list bindings = { + .count = 2, + .arr = bindings_array, + }; /* First, verify we get a collision when trying to assign the same * key combo to multiple actions */ - bindings->count = 1; - bindings->arr = xmalloc(sizeof(bindings->arr[0])); - bindings->arr[0] = (struct config_key_binding){ - .action = action + 1, - .sym = XKB_KEY_a, - .modifiers = { - .ctrl = true, - }, + bindings.arr[0] = (struct config_key_binding){ + .action = (test_mode == FAIL_DIFFERENT_ACTION + ? max_action - 1 : max_action), + .modifiers = {.ctrl = true}, + .path = "unittest", + }; + bindings.arr[1] = (struct config_key_binding){ + .action = max_action, + .modifiers = {.ctrl = true}, + .path = "unittest", }; - xkb_keysym_get_name(XKB_KEY_a, sym_name, sizeof(sym_name)); + switch (type) { + case KEY_BINDING: + bindings.arr[0].k.sym = XKB_KEY_a; + bindings.arr[1].k.sym = XKB_KEY_a; + break; - char collision[128]; - snprintf(collision, sizeof(collision), "%s+%s", XKB_MOD_NAME_CTRL, sym_name); - - ctx->value = collision; - if (parse_fun(ctx)) { - BUG("[%s].%s=%s: key combo collision not detected", - ctx->section, ctx->key, ctx->value); + 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; } - /* Next, verify we get a collision when trying to assign the same - * key combo to the same action, but with different pipe argvs */ - bindings->arr[0].action = action; - bindings->arr[0].pipe.master_copy = true; - bindings->arr[0].pipe.argv.args = xmalloc(4 * sizeof(bindings->arr[0].pipe.argv.args[0])); - bindings->arr[0].pipe.argv.args[0] = xstrdup("/usr/bin/foobar"); - bindings->arr[0].pipe.argv.args[1] = xstrdup("hello"); - bindings->arr[0].pipe.argv.args[2] = xstrdup("world"); - bindings->arr[0].pipe.argv.args[3] = NULL; + switch (test_mode) { + case FAIL_DIFFERENT_ACTION: + break; - snprintf(collision, sizeof(collision), - "[/usr/bin/foobar hello] %s+%s", - XKB_MOD_NAME_CTRL, sym_name); + case FAIL_DIFFERENT_ARGV: + case SUCCED_SAME_ACTION_AND_ARGV: + bindings.arr[0].pipe.master_copy = true; + bindings.arr[0].pipe.argv.args = xcalloc( + 4, sizeof(bindings.arr[0].pipe.argv.args[0])); + bindings.arr[0].pipe.argv.args[0] = xstrdup("/usr/bin/foobar"); + bindings.arr[0].pipe.argv.args[1] = xstrdup("hello"); + bindings.arr[0].pipe.argv.args[2] = xstrdup("world"); - ctx->value = collision; - if (parse_fun(ctx)) { - BUG("[%s].%s=%s: key combo collision not detected", - ctx->section, ctx->key, ctx->value); + bindings.arr[1].pipe.master_copy = true; + bindings.arr[1].pipe.argv.args = xcalloc( + 4, sizeof(bindings.arr[1].pipe.argv.args[0])); + bindings.arr[1].pipe.argv.args[0] = xstrdup("/usr/bin/foobar"); + bindings.arr[1].pipe.argv.args[1] = xstrdup("hello"); + + if (test_mode == SUCCED_SAME_ACTION_AND_ARGV) + bindings.arr[1].pipe.argv.args[2] = xstrdup("world"); + break; } - /* Finally, verify we do *not* get a collision when assigning the - * same key combo to the same action, with matching argvs */ - snprintf(collision, sizeof(collision), - "[/usr/bin/foobar hello world] %s+%s", - XKB_MOD_NAME_CTRL, sym_name); + bool expected_result = + test_mode == SUCCED_SAME_ACTION_AND_ARGV ? true : false; - ctx->value = collision; - if (!parse_fun(ctx)) { - BUG("[%s].%s=%s: invalid key combo collision", - ctx->section, ctx->key, ctx->value); + if (resolve_key_binding_collisions( + ctx->conf, ctx->section, map, &bindings, type) != expected_result) + { + 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")); } - key_binding_list_free(bindings); - bindings->arr = NULL; - bindings->count = 0; + if (expected_result == false) { + if (bindings.count != 1) + BUG("[%s]: colliding binding not removed", ctx->section); + + if (bindings.arr[0].action != + (test_mode == FAIL_DIFFERENT_ACTION ? max_action - 1 : max_action)) + { + BUG("[%s]: wrong binding removed", ctx->section); + } + } + + free_key_binding_list(&bindings); } static void -test_mouse_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx), - int action, int max_action, const char *const *map, - struct config_mouse_binding_list *bindings) +test_binding_collisions(struct context *ctx, + int max_action, const char *const *map, + enum config_key_binding_type type) { - 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; - - /* Generate the modifier part of the ‘value’ */ - char modifier_string[32]; - int chars = 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 "+" : ""); - - const int button_idx = action % ALEN(button_map); - const int button = button_map[button_idx].code; - const char *const button_name = button_map[button_idx].name; - const int click_count = action % 3 + 1; - - xassert(click_count > 0); - - /* Finally, generate the ‘value’ (e.g. “Control+shift+x”) */ - char value[chars + strlen(button_name) + 2 + 1]; - chars = sprintf(value, "%s%s", modifier_string, button_name); - if (click_count > 1) - sprintf(&value[chars], "-%d", click_count); - - ctx->key = key; - ctx->value = value; - - if (!parse_fun(ctx)) { - BUG("[%s].%s=%s failed to parse", - ctx->section, ctx->key, ctx->value); - } - - const struct config_mouse_binding *binding = - &bindings->arr[bindings->count - 1]; - - xassert(binding->pipe.argv.args == NULL); - - if (binding->action != action) { - BUG("[%s].%s=%s: action mismatch: %d != %d", - ctx->section, ctx->key, ctx->value, binding->action, action); - } - - if (binding->button != button) { - BUG("[%s].%s=%s: button mismatch: %d != %d", - ctx->section, ctx->key, ctx->value, binding->button, button); - } - - if (binding->count != click_count) { - BUG("[%s].%s=%s: button click count mismatch: %d != %d", - ctx->section, ctx->key, ctx->value, binding->count, click_count); - } - - if (binding->modifiers.ctrl != ctrl || - binding->modifiers.alt != alt || - binding->modifiers.shift != shift || - binding->modifiers.meta != 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, - binding->modifiers.ctrl, binding->modifiers.alt, - binding->modifiers.shift, binding->modifiers.meta, - ctrl, alt, shift, super); - } - - mouse_binding_list_free(bindings); - bindings->arr = NULL; - bindings->count = 0; - - if (action >= max_action) - return; - - /* - * Test collisions - */ - - /* First, verify we get a collision when trying to assign the same - * key combo to multiple actions */ - bindings->count = 1; - bindings->arr = xmalloc(sizeof(bindings->arr[0])); - bindings->arr[0] = (struct config_mouse_binding){ - .action = action + 1, - .button = BTN_LEFT, - .count = 1, - .modifiers = { - .ctrl = true, - }, - }; - - char collision[128]; - snprintf(collision, sizeof(collision), "%s+BTN_LEFT", XKB_MOD_NAME_CTRL); - - ctx->value = collision; - if (parse_fun(ctx)) { - BUG("[%s].%s=%s: mouse combo collision not detected", - ctx->section, ctx->key, ctx->value); - } - - /* Next, verify we get a collision when trying to assign the same - * key combo to the same action, but with different pipe argvs */ - bindings->arr[0].action = action; - bindings->arr[0].pipe.master_copy = true; - bindings->arr[0].pipe.argv.args = xmalloc(4 * sizeof(bindings->arr[0].pipe.argv.args[0])); - bindings->arr[0].pipe.argv.args[0] = xstrdup("/usr/bin/foobar"); - bindings->arr[0].pipe.argv.args[1] = xstrdup("hello"); - bindings->arr[0].pipe.argv.args[2] = xstrdup("world"); - bindings->arr[0].pipe.argv.args[3] = NULL; - - snprintf(collision, sizeof(collision), - "[/usr/bin/foobar hello] %s+BTN_LEFT", XKB_MOD_NAME_CTRL); - - ctx->value = collision; - if (parse_fun(ctx)) { - BUG("[%s].%s=%s: key combo collision not detected", - ctx->section, ctx->key, ctx->value); - } - -#if 0 /* BUG! (should be fixed by https://codeberg.org/dnkl/foot/pulls/832) */ - /* Finally, verify we do *not* get a collision when assigning the - * same key combo to the same action, with matching argvs */ - snprintf(collision, sizeof(collision), - "[/usr/bin/foobar hello world] %s+BTN_LEFT", XKB_MOD_NAME_CTRL); - - ctx->value = collision; - if (!parse_fun(ctx)) { - BUG("[%s].%s=%s: invalid mouse combo collision", - ctx->section, ctx->key, ctx->value); - } -#endif - mouse_binding_list_free(bindings); - bindings->arr = NULL; - bindings->count = 0; + _test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ACTION); + _test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ARGV); + _test_binding_collisions(ctx, max_action, map, type, SUCCED_SAME_ACTION_AND_ARGV); } static void @@ -567,13 +502,25 @@ test_section_key_bindings(void) test_key_binding( &ctx, &parse_section_key_bindings, action, BIND_ACTION_KEY_COUNT - 1, - binding_action_map, &conf.bindings.key); + binding_action_map, &conf.bindings.key, KEY_BINDING); } config_free(conf); } -#if 0 +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) { @@ -590,12 +537,26 @@ test_section_search_bindings(void) test_key_binding( &ctx, &parse_section_search_bindings, action, BIND_ACTION_SEARCH_COUNT - 1, - search_binding_action_map, &conf.bindings.search); + search_binding_action_map, &conf.bindings.search, KEY_BINDING); } 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) { @@ -612,12 +573,25 @@ test_section_url_bindings(void) test_key_binding( &ctx, &parse_section_url_bindings, action, BIND_ACTION_URL_COUNT - 1, - url_binding_action_map, &conf.bindings.url); + url_binding_action_map, &conf.bindings.url, KEY_BINDING); } config_free(conf); } -#endif + +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); +} static void test_section_mouse_bindings(void) @@ -632,26 +606,42 @@ test_section_mouse_bindings(void) if (binding_action_map[action] == NULL) continue; - test_mouse_binding( + test_key_binding( &ctx, &parse_section_mouse_bindings, action, BIND_ACTION_COUNT - 1, - binding_action_map, &conf.bindings.mouse); + binding_action_map, &conf.bindings.mouse, MOUSE_BINDING); } config_free(conf); } +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); +} + int main(int argc, const char *const *argv) { log_init(LOG_COLORIZE_AUTO, false, 0, LOG_CLASS_ERROR); test_section_main(); test_section_key_bindings(); -#if 0 + test_section_key_bindings_collisions(); test_section_search_bindings(); + test_section_search_bindings_collisions(); test_section_url_bindings(); -#endif + test_section_url_bindings_collisions(); test_section_mouse_bindings(); + test_section_mouse_bindings_collisions(); log_deinit(); return 0; }