Minimalist agentic harness in C
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504/** Tests for the lib/lib.h macros. Each test aborts on the first failed assert.*/#undef NDEBUG // The asserts are the tests, so they must never be compiled out.#include "../lib/lib.h" // First, so the test fails if lib.h stops including what its macros use.#include <assert.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/wait.h>#include <unistd.h>
typedef RESULT(int) int_result;
typedef struct { u64 a; u64 b; } pair;typedef RESULT(pair) pair_result;
static int_resultok(int value) { return (int_result){ .value = value };}
static int_resultfail(error_t error) { return (int_result){ .error = error };}
// Counts how many times a macro evaluates its argument.static int calls;
static int_resultcounted_ok(int value) { calls++; return ok(value);}
static int_resultcounted_fail(error_t error) { calls++; return fail(error);}
static intcounted_fallback(int value) { calls++; return value;}
#define STRINGIFY_(x) #x#define STRINGIFY(x) STRINGIFY_(x)
// CONCAT expands its arguments before pasting, so each __COUNTER__ is a fresh name.static voidtest_concat_expands_counter(void) { const char* first = STRINGIFY(CONCAT(name_, __COUNTER__)); const char* second = STRINGIFY(CONCAT(name_, __COUNTER__)); assert(strncmp(first, "name_", 5) == 0); assert(strcmp(first, "name___COUNTER__") != 0); assert(strcmp(first, second) != 0);}
// Every RESULT(T) expansion is the same type, so they can be assigned to each other.static voidtest_result_expansions_are_compatible(void) { RESULT(int) a = ok(1); int_result b = a; RESULT(int) c = b; assert(c.value == 1 && c.error == OK);}
// Zeroed results are OK, and unnamed members of a compound literal are zeroed.static voidtest_result_zero_value(void) { int_result zero = {0}; assert(is_ok(zero)); assert(fail(ERROR_ALLOCATION_FAILED).value == 0); assert(ok(5).error == OK);}
static voidtest_result_ok(void) { const int_result result = RESULT_OK(int, 1 + 2); assert(is_ok(result)); assert(result.value == 3);}
// Commas inside the value must not split the macro's arguments.static voidtest_result_ok_struct_value(void) { const pair_result result = RESULT_OK(pair, (pair){ .a = 1, .b = 2 }); assert(is_ok(result)); assert(result.value.a == 1 && result.value.b == 2);}
// The value is zeroed whatever T is, including structs.static voidtest_result_err(void) { const int_result scalar = RESULT_ERR(int, ERROR_ALLOCATION_FAILED); assert(!is_ok(scalar)); assert(scalar.error == ERROR_ALLOCATION_FAILED); assert(scalar.value == 0);
const pair_result aggregate = RESULT_ERR(pair, ERROR_REALLOCATION_FAILED); assert(aggregate.error == ERROR_REALLOCATION_FAILED); assert(aggregate.value.a == 0 && aggregate.value.b == 0);}
static int_resulttry_forwards_error(void) { const int value = TRY_OR_ELSE(fail(ERROR_REALLOCATION_FAILED), err, return RESULT_ERR(int, err)); return RESULT_OK(int, value);}
// RESULT_OK and RESULT_ERR return as the typedef'd result type, and fit TRY_OR_ELSE's branch.static voidtest_result_constructors_return(void) { const int_result result = try_forwards_error(); assert(result.error == ERROR_REALLOCATION_FAILED);}
static voidtest_is_ok(void) { assert(is_ok(ok(1))); assert(!is_ok(fail(ERROR_ALLOCATION_FAILED))); assert(!is_ok(fail(ERROR_REALLOCATION_FAILED)));
calls = 0; assert(is_ok(counted_ok(1))); assert(calls == 1);}
static voidtest_unwrap_or_value(void) { assert(UNWRAP_OR(ok(42), -1) == 42); assert(UNWRAP_OR(fail(ERROR_ALLOCATION_FAILED), -1) == -1);}
static voidtest_unwrap_or_evaluates_once(void) { calls = 0; assert(UNWRAP_OR(counted_ok(7), -1) == 7); assert(calls == 1);
calls = 0; assert(UNWRAP_OR(counted_fail(ERROR_ALLOCATION_FAILED), -1) == -1); assert(calls == 1);}
// The fallback only runs on failure.static voidtest_unwrap_or_lazy_fallback(void) { calls = 0; assert(UNWRAP_OR(ok(1), counted_fallback(2)) == 1); assert(calls == 0); assert(UNWRAP_OR(fail(ERROR_ALLOCATION_FAILED), counted_fallback(2)) == 2); assert(calls == 1);}
static voidtest_unwrap_or_struct_value(void) { const pair fallback = { 0, 0 }; const pair got = UNWRAP_OR(((pair_result){ .value = { 1, 2 } }), fallback); assert(got.a == 1 && got.b == 2);
const pair missed = UNWRAP_OR(((pair_result){ .error = ERROR_ALLOCATION_FAILED }), fallback); assert(missed.a == 0 && missed.b == 0);}
static inttry_success(void) { const int value = TRY_OR_ELSE(ok(42), err, return -1); return value;}
static inttry_failure(void) { const int value = TRY_OR_ELSE(fail(ERROR_ALLOCATION_FAILED), err, return -err); return value;}
static voidtest_try_or_else_returns(void) { assert(try_success() == 42); assert(try_failure() == -ERROR_ALLOCATION_FAILED);}
// Commas in the failure branch must not split the macro's arguments.static int_resulttry_commas(void) { const int value = TRY_OR_ELSE(fail(ERROR_REALLOCATION_FAILED), err, return (int_result){ .value = -1, .error = err }); return ok(value);}
static voidtest_try_or_else_commas_in_branch(void) { const int_result result = try_commas(); assert(result.error == ERROR_REALLOCATION_FAILED); assert(result.value == -1);}
// A multi-statement failure branch runs in order before leaving.static inttry_multi_statement(int* seen) { const int value = TRY_OR_ELSE(fail(ERROR_ALLOCATION_FAILED), err, *seen = err; *seen *= 10; return -1); return value;}
static voidtest_try_or_else_multi_statement_branch(void) { int seen = 0; assert(try_multi_statement(&seen) == -1); assert(seen == ERROR_ALLOCATION_FAILED * 10);}
static inttry_goto(void) { const int value = TRY_OR_ELSE(fail(ERROR_ALLOCATION_FAILED), err, goto fail); return value;fail: return 99;}
static voidtest_try_or_else_goto(void) { assert(try_goto() == 99);}
static voidtest_try_or_else_continue_and_break(void) { int sum = 0; for (int i = 0; i < 4; i++) { sum += TRY_OR_ELSE(i == 1 ? fail(ERROR_ALLOCATION_FAILED) : ok(i), err, continue); } assert(sum == 0 + 2 + 3);
int last = -1; for (int i = 0; i < 4; i++) { last = TRY_OR_ELSE(i == 2 ? fail(ERROR_ALLOCATION_FAILED) : ok(i), err, break); } assert(last == 1);}
static voidtest_try_or_else_evaluates_once(void) { calls = 0; const int value = TRY_OR_ELSE(counted_ok(5), err, assert(!"unreachable")); assert(value == 5); assert(calls == 1);}
static inttry_nested(int_result inner) { const int value = TRY_OR_ELSE(ok(TRY_OR_ELSE(inner, inner_err, return -inner_err) + 1), outer_err, return -100); return value;}
static voidtest_try_or_else_nested(void) { assert(try_nested(ok(4)) == 5); assert(try_nested(fail(ERROR_REALLOCATION_FAILED)) == -ERROR_REALLOCATION_FAILED);}
// The macro's hidden local must not capture the caller's variables, whatever they're called.static voidtest_try_or_else_no_capture(void) { const int r = 10, r_try_or_else = 20, _r_try_or_else0 = 30; const int value = TRY_OR_ELSE(ok(r + r_try_or_else + _r_try_or_else0), err, assert(!"unreachable")); assert(value == 60);}
static voidtest_try_or_else_struct_value(void) { const pair got = TRY_OR_ELSE(((pair_result){ .value = { 3, 4 } }), err, assert(!"unreachable")); assert(got.a == 3 && got.b == 4);}
static inttry_falls_through(void) { return TRY_OR_ELSE(fail(ERROR_ALLOCATION_FAILED), err, (void)err);}
// A failure branch that doesn't leave the scope must abort. Run in a child so the abort// doesn't take down the test run.static voidtest_try_or_else_fallthrough_aborts(void) { fflush(stdout); const pid_t pid = fork(); assert(pid >= 0); if (pid == 0) { freopen("/dev/null", "w", stderr); // Silence the expected assertion message. try_falls_through(); _exit(0); }
int status; assert(waitpid(pid, &status, 0) == pid); assert(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT);}
// Returns 1 if the macro jumped, 0 if it fell through. `ran` records whether outexpr ran.static intfail_if_goto(int cond, int* ran) { *ran = 0; FAIL_IF_GOTO(cond, *ran = 1, jumped); return 0;jumped: return 1;}
static voidtest_fail_if_goto(void) { int ran; assert(fail_if_goto(1, &ran) == 1 && ran == 1); assert(fail_if_goto(0, &ran) == 0 && ran == 0);}
static intensure_or_goto(int cond, int* ran) { *ran = 0; ENSURE_OR_GOTO(cond, *ran = 1, jumped); return 0;jumped: return 1;}
static voidtest_ensure_or_goto(void) { int ran; assert(ensure_or_goto(0, &ran) == 1 && ran == 1); assert(ensure_or_goto(1, &ran) == 0 && ran == 0);}
// ENSURE negates the whole condition, not just its first operand.static voidtest_ensure_or_goto_negates_whole_condition(void) { int ran; const int x = 1; assert(ensure_or_goto(0 || x, &ran) == 0 && ran == 0); assert(ensure_or_goto(1 && !x, &ran) == 1 && ran == 1);}
static int_resultfail_if_goto_r(int cond) { int_result out = ok(7); FAIL_IF_GOTO_R(cond, out, ERROR_ALLOCATION_FAILED, cleanup); out = ok(1);cleanup: return out;}
static voidtest_fail_if_goto_r(void) { const int_result failed = fail_if_goto_r(1); assert(failed.error == ERROR_ALLOCATION_FAILED); assert(failed.value == 0); // The whole result is replaced, not just .error. assert(fail_if_goto_r(0).error == OK && fail_if_goto_r(0).value == 1);}
static int_resultensure_or_goto_r(int cond) { int_result out = ok(7); ENSURE_OR_GOTO_R(cond, out, ERROR_ALLOCATION_FAILED, cleanup); out = ok(1);cleanup: return out;}
static voidtest_ensure_or_goto_r(void) { const int_result failed = ensure_or_goto_r(0); assert(failed.error == ERROR_ALLOCATION_FAILED && failed.value == 0); assert(ensure_or_goto_r(1).error == OK && ensure_or_goto_r(1).value == 1);}
static intfail_if_goto_counted(int_result r) { FAIL_IF_GOTO(!is_ok(counted_fail(r.error)), (void)0, jumped); return 0;jumped: return 1;}
static voidtest_fail_if_goto_evaluates_once(void) { calls = 0; assert(fail_if_goto_counted(fail(ERROR_ALLOCATION_FAILED)) == 1); assert(calls == 1); calls = 0; assert(fail_if_goto_counted(ok(0)) == 0); assert(calls == 1);}
// The do/while(0) wrapper makes the macro a single statement, so it can be an unbraced if body.// Unbraced on purpose: formatting or tidy fix-its adding braces would defeat the test.static intfail_if_goto_in_if_else(int outer, int cond) { // clang-format off // NOLINTBEGIN(readability-braces-around-statements) if (outer) FAIL_IF_GOTO(cond, (void)0, jumped); else return 2; // NOLINTEND(readability-braces-around-statements) // clang-format on return 0;jumped: return 1;}
static voidtest_fail_if_goto_in_if_else(void) { assert(fail_if_goto_in_if_else(1, 1) == 1); assert(fail_if_goto_in_if_else(1, 0) == 0); assert(fail_if_goto_in_if_else(0, 1) == 2);}
// `out` can be a different result type from the tried expression.static pair_resulttry_or_goto(int_result r) { pair_result out = { .value = { 7, 7 } }; const int value = TRY_OR_GOTO(r, err, out, cleanup); out = (pair_result){ .value = { (u64)value, (u64)value } };cleanup: return out;}
static voidtest_try_or_goto(void) { const pair_result failed = try_or_goto(fail(ERROR_REALLOCATION_FAILED)); assert(failed.error == ERROR_REALLOCATION_FAILED); assert(failed.value.a == 0 && failed.value.b == 0);
const pair_result got = try_or_goto(ok(3)); assert(got.error == OK && got.value.a == 3 && got.value.b == 3);}
static int_resulttry_or_goto_counted(int_result (*make)(int)) { int_result out = { 0 }; const int value = TRY_OR_GOTO(make(5), err, out, cleanup); out = ok(value);cleanup: return out;}
static int_resultcounted_fail_any(int value) { (void)value; return counted_fail(ERROR_ALLOCATION_FAILED);}
static voidtest_try_or_goto_evaluates_once(void) { calls = 0; assert(try_or_goto_counted(counted_ok).value == 5); assert(calls == 1); calls = 0; assert(try_or_goto_counted(counted_fail_any).error == ERROR_ALLOCATION_FAILED); assert(calls == 1);}
intmain(void) { test_concat_expands_counter(); test_result_expansions_are_compatible(); test_result_zero_value(); test_result_ok(); test_result_ok_struct_value(); test_result_err(); test_result_constructors_return(); test_is_ok(); test_unwrap_or_value(); test_unwrap_or_evaluates_once(); test_unwrap_or_lazy_fallback(); test_unwrap_or_struct_value(); test_try_or_else_returns(); test_try_or_else_commas_in_branch(); test_try_or_else_multi_statement_branch(); test_try_or_else_goto(); test_try_or_else_continue_and_break(); test_try_or_else_evaluates_once(); test_try_or_else_nested(); test_try_or_else_no_capture(); test_try_or_else_struct_value(); test_try_or_else_fallthrough_aborts(); test_fail_if_goto(); test_ensure_or_goto(); test_ensure_or_goto_negates_whole_condition(); test_fail_if_goto_r(); test_ensure_or_goto_r(); test_fail_if_goto_evaluates_once(); test_fail_if_goto_in_if_else(); test_try_or_goto(); test_try_or_goto_evaluates_once(); puts("lib: all tests passed"); return 0;}