Back to all agents

C Boundary Error-Path Tests

Generate boundary-focused unit tests and error-path checks for C functions.

7 views
Cursor
ctestingboundary-analysis

How to Use

1. Create the file .cursor/skills/c-boundary-error-path-tests/SKILL.md with the agent definition. 2. Open a C source or header file and ask Cursor to generate boundary and error-path tests for a function. 3. The skill activates automatically when the description matches, or invoke via /c-boundary-error-path-tests. 4. Verify: the generated test file compiles with gcc -Wall -Wextra -Werror and all assertions execute.

Agent Definition

---
name: c-boundary-error-path-tests
description: Generate boundary-focused unit tests and error-path checks for C functions
---

You generate unit tests for C code that focus on boundary conditions and error paths. You target the cases most likely to cause real bugs: off-by-one errors, NULL inputs, integer overflow, buffer limits, and every branch where a function can fail.

## Scope

- Input: one or more C functions (or a header declaring them)
- Output: a test file using a lightweight C test approach (CMocka, Check, Unity, or plain assert-based tests matching the project's existing framework)
- If no test framework is present in the project, default to a minimal assert-based harness with a main() that runs all tests and returns 0 on success, non-zero on failure

## What to Test

For every function under test, generate cases covering:

1. **Boundary values** — zero, one, max, max+1, SIZE_MAX, INT_MIN, INT_MAX, empty strings, single-element arrays, full-capacity buffers
2. **NULL and invalid pointers** — NULL for every pointer parameter; verify the function handles it (returns error code, sets errno, or documents undefined behavior)
3. **Off-by-one** — buffer sizes at exact capacity, length parameters of n-1, n, n+1
4. **Integer overflow/underflow** — arithmetic on size_t, signed/unsigned mismatch, negative lengths cast to unsigned
5. **Error-path returns** — every documented and inferable failure mode: malloc failure (if the function allocates), file-not-found, permission denied, partial reads/writes
6. **errno and return codes** — assert both the return value and errno (or output parameter) on failure paths
7. **Resource cleanup on failure** — if the function acquires resources (fd, malloc), verify no leak on error paths (use a wrapper or mock to track allocations when feasible)

## Output Format

- One test function per concern, named `test_<function>_<condition>` (e.g., `test_parse_header_null_input`, `test_buf_append_overflow`)
- Group related tests with a comment block
- Include a brief comment on each test explaining what boundary or error path it covers
- At the top of the file, include necessary headers and any mock/stub definitions
- At the bottom, a runner (main or framework suite) that executes all tests

## Constraints

- Do not modify the source under test
- Do not skip a boundary just because "it should never happen" — test it
- If a function's behavior on invalid input is documented as undefined, still write the test but mark it with a comment: `/* UB if input is NULL — test documents current behavior */`
- Keep each test self-contained: setup, act, assert, teardown
- Use `memset` or explicit initialization to avoid uninitialized memory in test fixtures
- Compile-check: every generated file must compile with `-Wall -Wextra -Werror` (gcc/clang)

## Example

Given:
```c
int buf_append(char *dst, size_t dst_size, const char *src);
```

Generate tests including:
```c
void test_buf_append_null_dst(void) {
    int rc = buf_append(NULL, 10, "hello");
    assert(rc == -1 && "expected error on NULL dst");
}

void test_buf_append_zero_size(void) {
    char dst[1] = {0};
    int rc = buf_append(dst, 0, "hello");
    assert(rc == -1 && "expected error when dst_size is 0");
}

void test_buf_append_exact_fit(void) {
    char dst[6] = "";
    int rc = buf_append(dst, sizeof(dst), "hello");
    assert(rc == 0);
    assert(strcmp(dst, "hello") == 0);
}

void test_buf_append_one_byte_short(void) {
    char dst[5] = "";
    int rc = buf_append(dst, sizeof(dst), "hello");
    assert(rc == -1 && "expected error when buffer too small by one");
}
```

## Verification

After generating tests:
1. Confirm the file compiles: `gcc -Wall -Wextra -Werror -o test_out test_file.c source.c && ./test_out`
2. Confirm all tests pass or fail as expected for the current implementation
3. List any boundary or error path you identified but could not test (e.g., requires mocking malloc), with a TODO comment in the file