testing-debugging · intermediate · ~15 min

Table-Driven Test Suite Runner

Construct table-driven unit test harnesses with strict output and return-code assertions.

Challenge

Table-driven unit testing structures test inputs, expected results, and return codes into structured data arrays. A test runner executes each test case, validates both outputs and return codes, and tallies passes.

Your Task

Given the test case definition:

typedef struct {
    const char *input;
    const char *expected;
    int expected_rc;
} TestCase;

Implement:

int run_test_suite(const TestCase *tests, size_t count,
                   int (*func)(const char *in, char *out, size_t cap),
                   size_t *passed_count);

Rules

  1. If tests == NULL, func == NULL, passed_count == NULL, or count == 0, return -1.
  2. Initialize *passed_count = 0.
  3. For each test case, allocate a local stack buffer of size 128 (char buf[128]).
  4. Execute int rc = func(tests[i].input, buf, sizeof(buf));.
  5. A test case passes if and only if rc == tests[i].expected_rc AND strcmp(buf, tests[i].expected) == 0.
  6. Increment *passed_count for each passing test case.
  7. Return 0 if all test cases passed (*passed_count == count), or 1 if at least one failed.

Example

TestCase cases[] = {{\"hello\", \"HELLO\", 0}, {\"\", \"\", 0}};
size_t passed = 0;
int rc = run_test_suite(cases, 2, to_upper, &passed); // returns 0, passed == 2

Input format

tests: array of TestCase; count: size_t; func: function under test; passed_count: output pointer for pass tally.

Output format

Returns 0 if all tests pass, 1 if any fail, -1 on NULL or zero count.

Constraints

Local stack buffer of size 128. Use strcmp for output matching.

Starter code

#include <stddef.h>
#include <string.h>

typedef struct {
    const char *input;
    const char *expected;
    int expected_rc;
} TestCase;

/* Execute test suite. Return 0 if all pass, 1 if any fail, -1 on invalid args. */
int run_test_suite(const TestCase *tests, size_t count,
                   int (*func)(const char *in, char *out, size_t cap),
                   size_t *passed_count) {
    (void)tests; (void)count; (void)func; (void)passed_count;
    return -1;
}

Common mistakes

Forgetting to initialize *passed_count = 0; buffer overflow if buffer is too small; uninitialized memory comparisons.

Edge cases to handle

Empty string outputs match correctly; return code mismatch with correct string fails; correct return code with string mismatch fails.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.