#include #include #include #include "../src/keyd.h" #define MAX_EVENTS 1024 struct key_event output[MAX_EVENTS]; size_t noutput = 0; static uint64_t get_time_ns() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)(ts.tv_sec*1E9)+(uint64_t)ts.tv_nsec; } static uint8_t lookup_code(const char *name) { size_t i; if (!strcmp(name, "control")) return KEYD_LEFTCTRL; if (!strcmp(name, "shift")) return KEYD_LEFTSHIFT; if (!strcmp(name, "meta")) return KEYD_LEFTMETA; if (!strcmp(name, "alt")) return KEYD_LEFTALT; for (i = 0; i < ARRAY_SIZE(keycode_table); i++) if (keycode_table[i].name && !strcmp(keycode_table[i].name, name)) return i; return 0; } static void send_key(uint8_t code, uint8_t pressed) { output[noutput].code = code; output[noutput].pressed = pressed; noutput++; } static char *read_file(const char *path) { int fd = open(path, O_RDONLY); static char buf[4096]; size_t sz = 0; ssize_t n; if (fd < 0) { perror("open"); exit(-1); } while ((n = read(fd, buf, sizeof(buf) - sz)) > 0) { sz += n; assert(sz < sizeof buf); } buf[sz] = 0; return buf; } static int cmp_events(struct key_event *input, size_t nin, struct key_event *output, size_t nout) { size_t i; if (nin != nout) return -1; for (i = 0; i < nin; i++) { if (input[i].code != output[i].code || input[i].pressed != output[i].pressed) return -1; } return 0; } static int print_diff(struct key_event *expected, size_t nexp, struct key_event *output, size_t nout) { int i; int n = nout > nexp ? nout : nexp; int ret = 0; printf("\n%-30s%s\n\n", "Expected", "Output"); for (i = 0; i < n; i++) { int np = 0; int match = i < nexp && i < nout && output[i].code == expected[i].code && output[i].pressed == expected[i].pressed; if (!match) ret = -1; if (!match) printf("\033[32;1m"); np = 0; if (i < nexp) np = printf("%s %s", keycode_table[expected[i].code].name, expected[i].pressed ? "down" : "up"); while (np++ < 30) printf(" "); if (!match) printf("\033[0m\033[31;1m"); if (i < nout) printf("%s %s", keycode_table[output[i].code].name, output[i].pressed ? "down" : "up"); if (!match) printf("\033[0m"); printf("\n"); } return ret; } static int parse_events(char *s, struct key_event in[MAX_EVENTS], size_t *nin, struct key_event out[MAX_EVENTS], size_t *nout) { int ret; int time = 0; int ln = 0; int n = 0; struct key_event *events = in; char *line = s; *nin = 0; *nout = 0; while (1) { int len; char *end = strchr(line, '\n'); if (!end) break; *end = 0; len = strlen(line); ln++; if (line[0] == '#') goto next; while (line[0] == ' ') line++; if (!line[0]) { *nin = n; events = out; n = 0; goto next; } if (len >= 2 && line[len - 1] == 's' && line[len - 2] == 'm') { time += atoi(line); } else { uint8_t code; char *k = strtok(line, " "); char *v = strtok(NULL, " \n"); if (!v || (strcmp(v, "up") && strcmp(v, "down"))) { printf("%d: Invalid line\n", ln); goto next; } if (!(code = lookup_code(k))) { printf("%d: %s is not a valid key\n", ln, k); goto next; } assert(n < MAX_EVENTS); events[n].code = code; events[n].pressed = !strcmp(v, "down"); events[n].timestamp = time; n++; } next: line = end + 1; } *nout = n; return 0; } uint64_t run_test(struct keyboard *kbd, const char *path) { uint64_t time; char *data = read_file(path); struct key_event input[MAX_EVENTS]; size_t ninput; struct key_event expected[MAX_EVENTS]; size_t nexpected; if (parse_events(data, input, &ninput, expected, &nexpected) < 0) { fprintf(stderr, "Failed to parse input\n"); exit(-1); } noutput = 0; time = get_time_ns(); kbd_process_events(kbd, input, ninput); time = get_time_ns()-time; if (cmp_events(output, noutput, expected, nexpected)) { printf("%s \033[31;1mFAILED\033[0m\n", path); print_diff(expected, nexpected, output, noutput); exit(-1); } else { printf("%s \033[32;1mPASSED\033[0m (%zu us)\n", path, time/1000); } return time; } static void on_layer_change(const struct keyboard *kbd, const struct layer *layer, uint8_t active) { } int main(int argc, char *argv[]) { size_t i; struct config config; uint64_t total_time; struct keyboard *kbd; struct output output = { .send_key = send_key, .on_layer_change = on_layer_change, }; if (argc < 2) { printf ("usage: %s [...]\n", argv[0]); return -1; } if (config_parse(&config, argv[1])) { printf("Failed to parse config %s\n", argv[1]); return -1; } kbd = new_keyboard(&config, &output); for (i = 2; i < argc; i++) total_time += run_test(kbd, argv[i]); printf("\nTotal time spent in the main loop: %zu us\n", total_time/1000); return 0; }