forked from WHC/Hermit
Vendored sashidown, OpenAI-compatible env (OPENAI_*), and bootstrap scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
4.1 KiB
C
157 lines
4.1 KiB
C
#include "ctl.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
int n_open;
|
|
int n_arg;
|
|
int n_ready;
|
|
int n_text;
|
|
int n_error;
|
|
char last_name[CTL_MAX_NAME];
|
|
char last_id[CTL_MAX_ID];
|
|
char last_path[512];
|
|
size_t text_bytes;
|
|
} sink_t;
|
|
|
|
static void on_ev(const ctl_event_t *ev, void *ud) {
|
|
sink_t *s = ud;
|
|
switch (ev->type) {
|
|
case CTL_EV_CALL_OPEN:
|
|
s->n_open++;
|
|
snprintf(s->last_id, sizeof(s->last_id), "%s", ev->call->id);
|
|
snprintf(s->last_name, sizeof(s->last_name), "%s", ev->call->name);
|
|
break;
|
|
case CTL_EV_ARG:
|
|
s->n_arg++;
|
|
if (ev->call->nargs > 0) {
|
|
const ctl_arg_t *a = &ev->call->args[ev->call->nargs - 1];
|
|
if (strcmp(a->key, "path") == 0 && a->value)
|
|
snprintf(s->last_path, sizeof(s->last_path), "%s", a->value);
|
|
}
|
|
break;
|
|
case CTL_EV_CALL_READY:
|
|
s->n_ready++;
|
|
break;
|
|
case CTL_EV_TEXT:
|
|
s->n_text++;
|
|
s->text_bytes += ev->text_len;
|
|
break;
|
|
case CTL_EV_ERROR:
|
|
s->n_error++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int expect(int cond, const char *msg) {
|
|
if (!cond) {
|
|
fprintf(stderr, "FAIL: %s\n", msg);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int test_chunked_confirm(void) {
|
|
sink_t s = {0};
|
|
ctl_parser_t *p = ctl_parser_create(on_ev, &s);
|
|
const char *msg =
|
|
"I'll read the file.\n"
|
|
"call t1 read_file\n"
|
|
"arg path 10\n"
|
|
"README.mdx"
|
|
"end\n";
|
|
/* feed 1 byte at a time */
|
|
for (size_t i = 0; msg[i]; i++) {
|
|
if (ctl_parser_feed(p, &msg[i], 1) != CTL_OK) {
|
|
fprintf(stderr, "feed failed at %zu\n", i);
|
|
ctl_parser_destroy(p);
|
|
return 0;
|
|
}
|
|
}
|
|
int ok = 1;
|
|
ok &= expect(ctl_parser_finish(p) == CTL_OK, "finish ok");
|
|
ok &= expect(s.n_ready == 1, "one ready");
|
|
ok &= expect(s.n_open == 1, "one open");
|
|
ok &= expect(s.n_arg == 1, "one arg");
|
|
ok &= expect(strcmp(s.last_name, "read_file") == 0, "name");
|
|
ok &= expect(strcmp(s.last_path, "README.mdx") == 0, "path");
|
|
ok &= expect(s.n_error == 0, "no error");
|
|
ok &= expect(ctl_parser_pending(p) == 0, "pending 0");
|
|
/* pending peak should be bounded by line/arg */
|
|
ok &= expect(ctl_parser_pending_peak(p) <= CTL_MAX_LINE, "peak bounded");
|
|
ctl_parser_destroy(p);
|
|
return ok;
|
|
}
|
|
|
|
static int test_truncate_no_execute(void) {
|
|
sink_t s = {0};
|
|
ctl_parser_t *p = ctl_parser_create(on_ev, &s);
|
|
const char *msg =
|
|
"call t1 read_file\n"
|
|
"arg path 20\n"
|
|
"partial"; /* incomplete: need 20 bytes, then end */
|
|
ctl_parser_feed(p, msg, strlen(msg));
|
|
int ok = 1;
|
|
ok &= expect(s.n_ready == 0, "no ready before EOF");
|
|
ok &= expect(s.n_open == 1, "open confirmed");
|
|
ok &= expect(ctl_parser_finish(p) == CTL_ERR_CONTRACT, "finish error");
|
|
ok &= expect(s.n_error >= 1, "error emitted");
|
|
ok &= expect(s.n_ready == 0, "still no ready");
|
|
ctl_parser_destroy(p);
|
|
return ok;
|
|
}
|
|
|
|
static int test_monotonic_no_retract(void) {
|
|
/* Once CALL_READY fires, further feed of garbage should error, not retract */
|
|
sink_t s = {0};
|
|
ctl_parser_t *p = ctl_parser_create(on_ev, &s);
|
|
const char *okmsg =
|
|
"call a1 run_shell\n"
|
|
"arg cmd 5\n"
|
|
"ls -l"
|
|
"end\n";
|
|
ctl_parser_feed(p, okmsg, strlen(okmsg));
|
|
int ready = s.n_ready;
|
|
int ok = expect(ready == 1, "ready once");
|
|
/* text after is fine */
|
|
ctl_parser_feed(p, "done.\n", 6);
|
|
ok &= expect(s.n_ready == 1, "ready unchanged");
|
|
ctl_parser_finish(p);
|
|
ctl_parser_destroy(p);
|
|
return ok;
|
|
}
|
|
|
|
static int test_text_before_call(void) {
|
|
sink_t s = {0};
|
|
ctl_parser_t *p = ctl_parser_create(on_ev, &s);
|
|
const char *msg = "Hello\nWorld\n";
|
|
ctl_parser_feed(p, msg, strlen(msg));
|
|
ctl_parser_finish(p);
|
|
int ok = expect(s.n_text == 2, "two text lines");
|
|
ok &= expect(s.n_ready == 0, "no calls");
|
|
ctl_parser_destroy(p);
|
|
return ok;
|
|
}
|
|
|
|
int main(void) {
|
|
int nfail = 0;
|
|
if (!test_chunked_confirm())
|
|
nfail++;
|
|
if (!test_truncate_no_execute())
|
|
nfail++;
|
|
if (!test_monotonic_no_retract())
|
|
nfail++;
|
|
if (!test_text_before_call())
|
|
nfail++;
|
|
if (nfail) {
|
|
fprintf(stderr, "%d test(s) failed\n", nfail);
|
|
return 1;
|
|
}
|
|
printf("test_ctl_frontier: OK\n");
|
|
return 0;
|
|
}
|