#include "goal.h" #include #include #include static int failures = 0; static int checks = 0; #define CHECK(cond, msg) \ do { \ checks++; \ if (!(cond)) { \ failures++; \ fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, (msg)); \ } \ } while (0) #define CHECK_CONT(hay, needle, msg) \ do { \ checks++; \ if (!hay || !strstr(hay, needle)) { \ failures++; \ fprintf(stderr, \ "FAIL %s:%d: %s (got `%s`, want substring `%s`)\n", \ __FILE__, __LINE__, (msg), hay ? hay : "(null)", \ (needle)); \ } \ } while (0) #define CHECK_NOT_CONT(hay, needle, msg) \ do { \ checks++; \ if (hay && strstr(hay, needle)) { \ failures++; \ fprintf(stderr, "FAIL %s:%d: %s (found `%s` in `%s`)\n", \ __FILE__, __LINE__, (msg), (needle), hay); \ } \ } while (0) static void test_parse_defaults(void) { goal_spec spec; char *err = NULL; CHECK(goal_parse("all tests pass", &spec, &err) == 1, "parse"); CHECK(strcmp(spec.criteria, "all tests pass") == 0, "criteria"); CHECK(spec.max_tries == GOAL_DEFAULT_MAX_TRIES, "default cap"); goal_spec_free(&spec); } static void test_parse_reads_cap(void) { goal_spec spec; char *err = NULL; CHECK(goal_parse("all tests pass, stop after 3 tries", &spec, &err) == 1, "parse"); CHECK(strcmp(spec.criteria, "all tests pass") == 0, "criteria"); CHECK(spec.max_tries == 3, "cap"); goal_spec_free(&spec); } static void test_parse_cap_without_tries_word(void) { goal_spec spec; char *err = NULL; CHECK(goal_parse("make it green, stop after 10", &spec, &err) == 1, "parse"); CHECK(strcmp(spec.criteria, "make it green") == 0, "criteria"); CHECK(spec.max_tries == 10, "cap"); goal_spec_free(&spec); } static void test_parse_tries_word_case_insensitive(void) { goal_spec spec; char *err = NULL; CHECK(goal_parse("ship it, stop after 4 TRIES", &spec, &err) == 1, "uppercase TRIES accepted"); CHECK(spec.max_tries == 4, "cap"); goal_spec_free(&spec); } static void test_parse_rejections(void) { goal_spec spec; char *err = NULL; err = NULL; CHECK(goal_parse(" ", &spec, &err) == 0, "empty input rejected"); CHECK(err && strstr(err, "success criteria") != NULL, "empty err msg"); free(err); err = NULL; CHECK(goal_parse("stop after 5 tries", &spec, &err) == 0, "missing criteria rejected"); CHECK(err && strstr(err, "before `stop after`") != NULL, "missing err msg"); free(err); err = NULL; CHECK(goal_parse("ship it, stop after", &spec, &err) == 0, "missing cap rejected"); CHECK(err && strstr(err, "number of tries") != NULL, "missing cap err"); free(err); err = NULL; CHECK(goal_parse("ship it, stop after 0 tries", &spec, &err) == 0, "zero cap rejected"); CHECK(err && strstr(err, "at least 1 try") != NULL, "zero cap err"); free(err); err = NULL; CHECK(goal_parse("ship it, stop after 5xyz", &spec, &err) == 0, "junk after the number rejected"); CHECK(err && strstr(err, "number of tries") != NULL, "junk err msg"); free(err); } static void test_first_attempt_prompt(void) { goal_spec spec; spec.criteria = xstrdup("all tests pass"); spec.max_tries = 5; char *prompt = goal_prompt(&spec, 1, NULL); CHECK_CONT(prompt, "all tests pass", "states the goal"); CHECK_NOT_CONT(prompt, "NOT yet met", "no feedback on first attempt"); CHECK_CONT(prompt, "fully met", "asks to keep going"); free(prompt); goal_spec_free(&spec); } static void test_later_attempt_prompt(void) { goal_spec spec; spec.criteria = xstrdup("all tests pass"); spec.max_tries = 3; char *prompt = goal_prompt(&spec, 2, "the build still fails on x86"); CHECK_CONT(prompt, "attempt 2 of 3", "attempt framing"); CHECK_CONT(prompt, "NOT yet met", "verdict phrasing"); CHECK_CONT(prompt, "the build still fails on x86", "includes feedback"); free(prompt); char *no_fb = goal_prompt(&spec, 4, NULL); CHECK_CONT(no_fb, "attempt 4 of 3", "attempt number"); CHECK_CONT(no_fb, "no specific feedback was given", "fallback feedback"); free(no_fb); goal_spec_free(&spec); } static void test_verdict_met(void) { goal_verdict v; goal_parse_verdict("MET\nAll tests pass and the suite is green.", &v); CHECK(v.met, "met detected"); CHECK_CONT(v.reason, "green", "reason captured"); free(v.reason); } static void test_verdict_not_met(void) { goal_verdict v; goal_parse_verdict("NOT MET\nTwo tests still fail in the linux job.", &v); CHECK(!v.met, "not met detected"); CHECK_CONT(v.reason, "still fail", "reason captured"); free(v.reason); } static void test_verdict_garbage(void) { goal_verdict v; goal_parse_verdict("I think we are close! Let me keep going.", &v); CHECK(!v.met, "conservative on garbage"); CHECK_CONT(v.reason, "close", "first line is the reason"); free(v.reason); } static void test_verdict_unmet_is_not_met(void) { /* "UNMET" contains the substring "MET"; it must not read as met. */ goal_verdict v; goal_parse_verdict("UNMET\nStill failing.", &v); CHECK(!v.met, "UNMET is not met"); free(v.reason); goal_parse_verdict("NOT UNMET", &v); CHECK(!v.met, "NOT UNMET is not met"); free(v.reason); goal_parse_verdict("unmet", &v); CHECK(!v.met, "lowercase unmet is not met"); free(v.reason); goal_parse_verdict("MET", &v); CHECK(v.met, "plain MET is met"); free(v.reason); } static void test_verdict_no_reason(void) { goal_verdict v; goal_parse_verdict("MET", &v); CHECK(v.met, "bare MET is met"); CHECK_CONT(v.reason, "did not give a reason", "fallback reason"); free(v.reason); } static void test_preamble(void) { const char *p = goal_evaluator_preamble(); CHECK_CONT(p, "MET", "mentions MET token"); CHECK_CONT(p, "NOT MET", "mentions NOT MET token"); CHECK_CONT(p, "strict", "strict reviewer"); } int main(void) { test_parse_defaults(); test_parse_reads_cap(); test_parse_cap_without_tries_word(); test_parse_tries_word_case_insensitive(); test_parse_rejections(); test_first_attempt_prompt(); test_later_attempt_prompt(); test_verdict_met(); test_verdict_not_met(); test_verdict_garbage(); test_verdict_unmet_is_not_met(); test_verdict_no_reason(); test_preamble(); printf("%d checks, %d failures\n", checks, failures); return failures ? 1 : 0; }