Files
Hermit/tests/test_keymap.c
T
WHC 7d3ec9fcb9
ci / linux (clang) (push) Canceled after 0s
ci / linux (gcc) (push) Canceled after 0s
ci / sanitizers (push) Canceled after 0s
ci / musl (push) Canceled after 0s
ci / macos (push) Canceled after 0s
Extensions: skills, plugins, MCP, sub-agents; configurable keys
- Skills (SKILL.md discovery, skill tool, /skills /skill)
- plugin.json exec tools with permission prompts, timeouts and workspace trust (/plugins)
- MCP stdio client: JSON-RPC initialize/tools/list/tools/call, mcp__server__tool, /mcp and sidebar status
- task tool with general/explore/plan and .agent/agents/*.md roles, parallel read-only runs, cancel propagation, /agents
- Keymap with Ctrl+X leader, keybinds.json overrides and /keys panel
- Collapsed thinking, throttled streaming Markdown, leaner tool output, parallel read-only tools
- Spawn: close-on-exec pipes and a process-wide fork lock
- Tests for keymap, skills, plugins, MCP, sub-agents, shell compaction; docs/plugins.md
2026-10-05 14:55:31 +08:00

127 lines
4.9 KiB
C

#include "cst_keymap.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK(c) \
do { \
if (!(c)) { \
fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \
exit(1); \
} \
} while (0)
static int lookup1(const cst_keymap_t *km, int k, int *a) {
int seq[1] = {k};
return cst_keymap_lookup(km, seq, 1, a);
}
static int lookup2(const cst_keymap_t *km, int k1, int k2, int *a) {
int seq[2] = {k1, k2};
return cst_keymap_lookup(km, seq, 2, a);
}
static void test_parse(void) {
char b[32];
CHECK(cst_key_parse("ctrl+p") == 16);
CHECK(cst_key_parse("Ctrl+X") == 24);
CHECK(cst_key_parse("tab") == 9);
CHECK(cst_key_parse("shift+tab") == CST_K_BTAB);
CHECK(cst_key_parse("alt+enter") == (13 | CST_KEY_ALT));
CHECK(cst_key_parse("alt+up") == CST_K_ALT_UP);
CHECK(cst_key_parse("ctrl+home") == CST_K_CTRL_HOME);
CHECK(cst_key_parse("f1") == CST_K_F1);
CHECK(cst_key_parse("f12") == CST_K_F1 + 11);
CHECK(cst_key_parse("pgdn") == CST_K_PGDN);
CHECK(cst_key_parse("n") == 'n');
CHECK(cst_key_parse("é") == 0xE9);
CHECK(cst_key_parse("ctrl+") == -1);
CHECK(cst_key_parse("hyper+x") == -1);
CHECK(cst_key_parse("f13") == -1);
CHECK(cst_key_parse("ctrl+f1") == -1);
CHECK(strcmp(cst_key_name(24, b, sizeof(b)), "ctrl+x") == 0);
CHECK(strcmp(cst_key_name(CST_K_BTAB, b, sizeof(b)), "shift+tab") == 0);
CHECK(strcmp(cst_key_name(13 | CST_KEY_ALT, b, sizeof(b)), "alt+enter") ==
0);
CHECK(strcmp(cst_key_name(CST_K_F1 + 1, b, sizeof(b)), "f2") == 0);
for (int a = 1; a < CST_KA_COUNT; a++) {
const cst_key_action_info_t *in = cst_key_action_info(a);
CHECK(in && in->name && in->group && in->desc);
CHECK(cst_key_action_find(in->name) == a);
}
}
static void test_defaults(void) {
cst_keymap_t km;
cst_keymap_defaults(&km);
int a = 0;
CHECK(lookup1(&km, 16, &a) == CST_KM_MATCH && a == CST_KA_PALETTE);
CHECK(lookup1(&km, 24, &a) == CST_KM_PREFIX);
CHECK(lookup2(&km, 24, 'n', &a) == CST_KM_MATCH && a == CST_KA_NEW_SESSION);
CHECK(lookup2(&km, 24, 'z', &a) == CST_KM_NONE);
CHECK(lookup1(&km, 'q', &a) == CST_KM_NONE);
CHECK(lookup1(&km, CST_K_F1, &a) == CST_KM_MATCH && a == CST_KA_KEYS);
char d[64];
cst_keymap_describe(&km, CST_KA_KEYS, d, sizeof(d));
CHECK(strcmp(d, "ctrl+x h, f1") == 0);
char conf[256];
CHECK(cst_keymap_conflicts(&km, conf, sizeof(conf)) == 0);
}
static void test_json(void) {
cst_keymap_t km;
cst_keymap_defaults(&km);
char err[128];
CHECK(cst_keymap_apply_json(&km,
"{\"leader\":\"ctrl+g\",\"palette\":\"f2\","
"\"todo\":\"none\",\"model\":[\"<leader> M\","
"\"alt+m\"]}",
err, sizeof(err)) == 0);
int a = 0;
CHECK(lookup1(&km, 16, &a) == CST_KM_NONE);
CHECK(lookup1(&km, CST_K_F1 + 1, &a) == CST_KM_MATCH && a == CST_KA_PALETTE);
CHECK(lookup1(&km, 20, &a) == CST_KM_NONE); /* ctrl+t unbound */
CHECK(lookup1(&km, 24, &a) == CST_KM_NONE); /* ctrl+x no longer leader */
CHECK(lookup2(&km, 7, 'n', &a) == CST_KM_MATCH && a == CST_KA_NEW_SESSION);
CHECK(lookup2(&km, 7, 'M', &a) == CST_KM_MATCH && a == CST_KA_MODEL);
CHECK(lookup2(&km, 7, 'm', &a) == CST_KM_NONE);
CHECK(lookup1(&km, 'm' | CST_KEY_ALT, &a) == CST_KM_MATCH &&
a == CST_KA_MODEL);
int user = 0;
for (int i = 0; i < km.n; i++)
if (km.b[i].action == CST_KA_MODEL)
user += km.b[i].user;
CHECK(user == 2);
/* bad entries are reported, the rest still applies */
cst_keymap_defaults(&km);
CHECK(cst_keymap_apply_json(&km,
"{\"nope\":\"f3\",\"undo\":\"ctrl+\","
"\"retry\":\"f4\"}",
err, sizeof(err)) == -1);
CHECK(strstr(err, "nope") != NULL);
CHECK(lookup1(&km, CST_K_F1 + 3, &a) == CST_KM_MATCH && a == CST_KA_RETRY);
CHECK(cst_keymap_apply_json(&km, "[1]", err, sizeof(err)) == -1);
/* conflicts: same key twice, and a single key shadowing a prefix */
cst_keymap_defaults(&km);
CHECK(cst_keymap_apply_json(&km, "{\"undo\":\"ctrl+o\",\"redo_none\":1}",
err, sizeof(err)) == -1);
char conf[256];
CHECK(cst_keymap_conflicts(&km, conf, sizeof(conf)) == 1);
CHECK(strstr(conf, "ctrl+o") && strstr(conf, "undo"));
cst_keymap_defaults(&km);
CHECK(cst_keymap_apply_json(&km, "{\"copy\":\"ctrl+x\"}", err,
sizeof(err)) == 0);
CHECK(cst_keymap_conflicts(&km, conf, sizeof(conf)) > 0);
}
int main(void) {
test_parse();
test_defaults();
test_json();
puts("test_keymap: ok");
return 0;
}