feat: add script module for note-to-command mapping with FIFO support

This commit is contained in:
Loic Coenen
2026-05-18 21:12:29 +00:00
committed by Loic Coenen (aider)
parent 16a800209f
commit f776b8a361
7 changed files with 638 additions and 5 deletions

188
client/tests/test_script.c Normal file
View File

@@ -0,0 +1,188 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* mock send_command records last command */
static char last_cmd[4096] = "";
int send_command(const char *cmd) {
strncpy(last_cmd, cmd, sizeof(last_cmd)-1);
last_cmd[sizeof(last_cmd)-1] = '\0';
return 0;
}
#include "../src/script.h"
static int tests_passed = 0;
static int tests_failed = 0;
static void test_load_valid(void) {
const char *path = "/tmp/test_script_1.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "# comment\n11 record 0\n12 stop\n\n13 add\n");
fclose(f);
int r = script_load(path);
if (r != 0) {
printf("FAIL: script_load returned %d\n", r);
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
static void test_single_command(void) {
const char *path = "/tmp/test_script_2.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "11 record 0\n");
fclose(f);
script_load(path);
last_cmd[0] = '\0';
script_handle_note(11);
if (strcmp(last_cmd, "record 0") != 0) {
printf("FAIL: expected 'record 0' got '%s'\n", last_cmd);
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
static void test_multiple_commands(void) {
const char *path = "/tmp/test_script_3.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "21 record 0 ; stop\n");
fclose(f);
script_load(path);
last_cmd[0] = '\0';
script_handle_note(21);
if (strcmp(last_cmd, "stop") != 0) {
printf("FAIL: expected 'stop' got '%s'\n", last_cmd);
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
static void test_unmapped_note(void) {
const char *path = "/tmp/test_script_4.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "30 add\n");
fclose(f);
script_load(path);
last_cmd[0] = '\0';
script_handle_note(31);
if (last_cmd[0] != '\0') {
printf("FAIL: expected empty last_cmd\n");
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
static void test_ignores_comments_and_blanks(void) {
const char *path = "/tmp/test_script_5.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "\n# this is a comment\n \n40 bind 2\n\n");
fclose(f);
int r = script_load(path);
if (r != 0) {
printf("FAIL: script_load returned %d\n", r);
tests_failed++;
unlink(path);
return;
}
last_cmd[0] = '\0';
script_handle_note(40);
if (strcmp(last_cmd, "bind 2") != 0) {
printf("FAIL: expected 'bind 2' got '%s'\n", last_cmd);
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
static void test_note_out_of_range(void) {
const char *path = "/tmp/test_script_6.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "11 load\n");
fclose(f);
script_load(path);
last_cmd[0] = '\0';
script_handle_note(200);
if (last_cmd[0] != '\0') {
printf("FAIL: expected empty last_cmd\n");
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
static void test_empty_macro(void) {
const char *path = "/tmp/test_script_7.rc";
FILE *f = fopen(path, "w");
if (!f) { tests_failed++; return; }
fprintf(f, "11 \n12 record 0\n");
fclose(f);
int r = script_load(path);
if (r != 0) {
printf("FAIL: script_load returned %d\n", r);
tests_failed++;
unlink(path);
return;
}
last_cmd[0] = '\0';
script_handle_note(11);
if (last_cmd[0] != '\0') {
printf("FAIL: empty macro produced command\n");
tests_failed++;
unlink(path);
return;
}
script_handle_note(12);
if (strcmp(last_cmd, "record 0") != 0) {
printf("FAIL: expected 'record 0' got '%s'\n", last_cmd);
tests_failed++;
unlink(path);
return;
}
unlink(path);
tests_passed++;
printf("PASS\n");
}
int main(void) {
printf("Script module tests:\n");
test_load_valid();
test_single_command();
test_multiple_commands();
test_unmapped_note();
test_ignores_comments_and_blanks();
test_note_out_of_range();
test_empty_macro();
printf("\nResults: %d passed, %d failed\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}