Skip to content

Commit 809950e

Browse files
committed
Enhance test runner functionality and add new test modules
- Updated the test runner front door to support a new `--case <case-name>` option for more granular test execution. - Added new test modules for code generation: `codegen-render`, `codegen-build`, and `codegen-reject`, including their respective test source files. - Refactored existing test suite functions to improve workspace management and error reporting. - Introduced case filtering capabilities to allow selective test execution based on environment variables. - Enhanced failure message reporting in the test assertion framework to include file and line information. - Created main entry points for the new code generation test modules to facilitate execution.
1 parent 4df8fd3 commit 809950e

29 files changed

Lines changed: 2046 additions & 527 deletions

docs/codegen/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ variants.
2020
## Normative Contract
2121

2222
- [Runtime contract](./codegen_runtime_contract.md)
23+
- [Supported subset](./generated_backend_supported_subset.md)
2324

2425
## Dependencies
2526

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Generated Backend Supported Subset
2+
3+
## Status
4+
5+
This document is the canonical `C4` product claim for the generated backend.
6+
7+
It is evidence-backed by:
8+
9+
- `./build/nob test evaluator-codegen-diff`
10+
- `./build/nob test artifact-parity`
11+
- `./build/nob test artifact-parity-corpus`
12+
13+
Anything not listed here remains outside the supported claim even if the
14+
evaluator implements it.
15+
16+
## Proven Phases And Families
17+
18+
The current supported generated-backend subset is limited to the proven
19+
downstream path:
20+
21+
- configure replay for the deterministic filesystem and local host-effect
22+
subset landed in `C2`
23+
- build graph execution for the supported target/build-step subset
24+
- local-only `test` / baseline `ctest_*` / local `FetchContent_*` ownership
25+
landed in `C3`
26+
- install, export, and archive package execution already proven by the
27+
explicit parity harness
28+
29+
This claim is intentionally phase-and-family based. It is not a blanket claim
30+
for the full CMake 3.28 universe.
31+
32+
## Supported Real Projects
33+
34+
The pinned real-project corpus currently supports these projects through
35+
build/install/consumer proof:
36+
37+
- `fmt`
38+
Imported target expectation: `fmt::fmt`
39+
- `pugixml`
40+
Imported target expectation: `pugixml::pugixml`
41+
- `nlohmann_json`
42+
Imported target expectation: `nlohmann_json::nlohmann_json`
43+
- `cjson`
44+
Imported target expectation: `cjson`
45+
46+
These projects are the current real-project scope of the supported claim.
47+
Unlisted projects are outside the `C4` product boundary by default.
48+
49+
## Explicit Boundaries
50+
51+
The supported subset does not currently claim:
52+
53+
- generic process replay such as `execute_process()` and `exec_program()`
54+
- positive generated-backend probe execution for `try_compile()` and `try_run()`
55+
- remote/provider/VCS/custom-command `FetchContent_*`
56+
- dashboard/script/network `ctest_*` variants outside the local-only subset
57+
- installer generators and unsupported component packaging variants
58+
- the full CMake 3.28 command universe beyond the explicit closure inventory
59+
60+
Those surfaces must stay explicit in the closure harness as `backend-reject`
61+
or explicit product boundaries until new proof lands.

docs/tests/README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,17 @@ Current supported human-facing baseline commands now run through `./build/nob te
4747
`./build/nob test daemon status`
4848
- explicit artifact parity suite:
4949
`./build/nob test artifact-parity`
50+
- explicit real-project corpus suite:
51+
`./build/nob test artifact-parity-corpus`
5052
- explicit closure harness suite:
5153
`./build/nob test evaluator-codegen-diff`
5254

55+
The current docs-only release gate for the generated-backend claim is:
56+
57+
- `./build/nob test evaluator-codegen-diff`
58+
- `./build/nob test artifact-parity`
59+
- `./build/nob test artifact-parity-corpus`
60+
5361
The active daemon roadmap now lands its T6 surface on:
5462

5563
- `./build/nob test`
@@ -61,6 +69,7 @@ The active daemon roadmap now lands its T6 surface on:
6169
- `./build/nob test watch auto`
6270
- `./build/nob test daemon start|stop|status`
6371
- `./build/nob test artifact-parity`
72+
- `./build/nob test artifact-parity-corpus`
6473
- `./build/nob test evaluator-codegen-diff`
6574
- daemon front-door profile flags:
6675
`--verbose`, `--asan`, `--ubsan`, `--msan`, `--san`, `--cov`
@@ -75,8 +84,8 @@ Current T6 limitation:
7584
- default watch mode is compact and failure-first; use `--verbose` to print
7685
roots, full routed path/module sets, and per-rerun fast-path detail.
7786

78-
`artifact-parity` and `evaluator-codegen-diff` remain outside default smoke
79-
while they stay heavier and host-sensitive.
87+
`artifact-parity`, `artifact-parity-corpus`, and `evaluator-codegen-diff`
88+
remain outside default smoke while they stay heavier and host-sensitive.
8089

8190
## Relationship To Product Contracts
8291

@@ -86,5 +95,7 @@ while they stay heavier and host-sensitive.
8695
[`./test_daemon_roadmap.md`](./test_daemon_roadmap.md)
8796
- Generated runtime contract:
8897
[`../codegen/codegen_runtime_contract.md`](../codegen/codegen_runtime_contract.md)
98+
- Generated backend supported subset:
99+
[`../codegen/generated_backend_supported_subset.md`](../codegen/generated_backend_supported_subset.md)
89100
- Build-model replay contract:
90101
[`../build_model/build_model_replay.md`](../build_model/build_model_replay.md)

docs/tests/evaluator_codegen_diff.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ The canonical corpus is:
151151
- `test_v2/evaluator_diff/cases/*.cmake`
152152
- focused seed case-packs under `test_v2/evaluator_codegen_diff/cases/` when a
153153
backend-owned surface has no suitable existing case
154+
- the real-project corpus catalog under
155+
`test_v2/artifact_parity/real_projects/manifest.json` for `C4` project
156+
disposition and supported-subset cross-checks
154157

155158
The closure harness adopts the full case-pack DSL from `evaluator_diff` rather
156159
than a suite-local subset. Each closure case declares the metadata required to
@@ -171,6 +174,14 @@ When existing evaluator case-packs are not enough, the suite adds local seeds
171174
under `test_v2/evaluator_codegen_diff/cases/` instead of synthesizing ad hoc C
172175
tests.
173176

177+
For `C4`, the suite also carries explicit real-project metadata outside the
178+
case-pack DSL:
179+
180+
- one explicit corpus disposition per manifest project
181+
- severity/frequency/disposition metadata for any remaining corpus-driven gap
182+
- supported-subset document cross-checks for the projects and release-gate
183+
commands claimed by the product docs
184+
174185
## Promotion Rules
175186

176187
A case or variant may change state only when the suite and its documentation
@@ -209,6 +220,12 @@ Reasons:
209220
It therefore does not participate in the current baseline
210221
`./build/nob test` / `./build/nob test smoke` smoke aggregate.
211222

223+
The docs-only release gate for the generated-backend claim is the trio:
224+
225+
- `./build/nob test evaluator-codegen-diff`
226+
- `./build/nob test artifact-parity`
227+
- `./build/nob test artifact-parity-corpus`
228+
212229
`./build/nob test evaluator-codegen-diff`
213230

214231
Like the other explicit-only host suites, it inherits runner-owned workspace,

docs/tests/tests_architecture.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,18 @@ The active daemon roadmap now owns the full human-facing test command surface.
191191
stay out of scope in this wave.
192192
Current aggregate status: excluded from the default aggregate path.
193193

194+
- `artifact-parity-corpus`
195+
Focus: manifest-driven real-project hardening for the pinned corpus under
196+
`test_v2/artifact_parity/real_projects/manifest.json`. This suite is the
197+
execution owner for the product-facing corpus claim: it runs the pinned
198+
projects through build/install/consumer parity, keeps project names as the
199+
stable `--case` surface, and emits actionable project+stage failures when a
200+
real-project run regresses. The current docs-only release gate explicitly
201+
includes `./build/nob test artifact-parity-corpus` alongside
202+
`./build/nob test artifact-parity` and
203+
`./build/nob test evaluator-codegen-diff`.
204+
Current aggregate status: excluded from the default aggregate path.
205+
194206
- `evaluator-codegen-diff`
195207
Focus: evaluator-corpus-backed classification of implemented command and
196208
subcommand surfaces into explicit closure-program states, with phase-aware
@@ -201,9 +213,12 @@ The active daemon roadmap now owns the full human-facing test command surface.
201213
to ensure that every `FULL` evaluator command is inventoried and that
202214
curated subcommand families such as `file()`, `string()`, `list()`,
203215
`math()`, `cmake_language()`, `cmake_path()`, and `ctest_*` do not silently
204-
drift out of downstream/backend ownership. This suite is explicit-only on
205-
purpose: it is a heavier host-sensitive closure harness, not a default smoke
206-
signal.
216+
drift out of downstream/backend ownership. In `C4` it also cross-checks the
217+
real-project corpus catalog, corpus dispositions, and the generated-backend
218+
supported-subset docs without rerunning the corpus itself. This suite is
219+
explicit-only on purpose: it is a heavier host-sensitive closure harness,
220+
not a default smoke signal, and it participates in the docs-only release
221+
gate through `./build/nob test evaluator-codegen-diff`.
207222
Current aggregate status: excluded from the default aggregate path.
208223

209224
- `evaluator-integration`

src_v2/build/nob.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ static void format_duration_usec(uint64_t duration_usec, char *buffer, size_t bu
7575
}
7676
}
7777

78+
static void log_multiline_status_block(const char *label, const char *text) {
79+
char buffer[TEST_RUNNER_FAILURE_SUMMARY_CAPACITY + 1] = {0};
80+
char *line = NULL;
81+
char *state = NULL;
82+
83+
if (!label || !text || text[0] == '\0') return;
84+
if (snprintf(buffer, sizeof(buffer), "%s", text) >= (int)sizeof(buffer)) return;
85+
86+
nob_log(NOB_INFO, "%s", label);
87+
for (line = strtok_r(buffer, "\n", &state); line; line = strtok_r(NULL, "\n", &state)) {
88+
nob_log(NOB_INFO, " %s", line);
89+
}
90+
}
91+
7892
static void log_test_daemon_status_details(const Test_Daemon_Status *status) {
7993
char duration[64] = {0};
8094
const Test_Daemon_Request_Metadata *active = NULL;
@@ -100,11 +114,12 @@ static void log_test_daemon_status_details(const Test_Daemon_Status *status) {
100114
if (active->kind != TEST_DAEMON_REQUEST_NONE) {
101115
format_duration_usec(active->active_duration_usec, duration, sizeof(duration));
102116
nob_log(NOB_INFO,
103-
"active request: kind=%s policy=%s module=%s profile=%s duration=%s client=%s",
117+
"active request: kind=%s policy=%s module=%s profile=%s case=%s duration=%s client=%s",
104118
test_daemon_request_kind_name((Test_Daemon_Request_Kind)active->kind),
105119
test_daemon_admission_policy_name((Test_Daemon_Admission_Policy)active->admission_policy),
106120
active->module_name[0] != '\0' ? active->module_name : "<none>",
107121
active->profile_name[0] != '\0' ? active->profile_name : "<none>",
122+
active->case_name[0] != '\0' ? active->case_name : "<all>",
108123
duration[0] != '\0' ? duration : "0ms",
109124
active->client_attached ? "attached" : "detached");
110125
nob_log(NOB_INFO,
@@ -133,6 +148,9 @@ static void log_test_daemon_status_details(const Test_Daemon_Status *status) {
133148
if (status->stderr_log_path[0] != '\0') {
134149
nob_log(NOB_INFO, "last stderr log: %s", status->stderr_log_path);
135150
}
151+
if (status->failure_summary[0] != '\0') {
152+
log_multiline_status_block("last failure summary:", status->failure_summary);
153+
}
136154
}
137155

138156
static bool wait_for_test_daemon_shutdown(unsigned timeout_ms) {
@@ -549,7 +567,11 @@ static bool build_snapshot_tool(void) {
549567

550568
nob_cc(&cmd);
551569
nob_cmd_append(&cmd, "-Wall", "-Wextra", "-std=c11", "-O2", "-ggdb", "-Ivendor");
552-
nob_cmd_append(&cmd, "-o", SNAPSHOT_TOOL_BIN, SNAPSHOT_TOOL_SRC);
570+
nob_cmd_append(&cmd,
571+
"-o",
572+
SNAPSHOT_TOOL_BIN,
573+
SNAPSHOT_TOOL_SRC,
574+
"test_v2/artifact_parity/test_artifact_parity_corpus_manifest.c");
553575
ok = nob_cmd_run(&cmd);
554576
nob_cmd_free(cmd);
555577
return ok;

src_v2/build/test_daemon_client.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,18 @@ static bool test_daemon_client_copy_status(Test_Daemon_Status *out_status,
5050
test_daemon_client_copy_cstr(out_status->stderr_log_path,
5151
sizeof(out_status->stderr_log_path),
5252
payload->stderr_log_path) &&
53+
test_daemon_client_copy_cstr(out_status->case_name,
54+
sizeof(out_status->case_name),
55+
payload->case_name) &&
5356
test_daemon_client_copy_cstr(out_status->detail,
5457
sizeof(out_status->detail),
5558
payload->detail) &&
5659
test_daemon_client_copy_cstr(out_status->summary,
5760
sizeof(out_status->summary),
58-
payload->summary);
61+
payload->summary) &&
62+
test_daemon_client_copy_cstr(out_status->failure_summary,
63+
sizeof(out_status->failure_summary),
64+
payload->failure_summary);
5965
}
6066

6167
static bool test_daemon_client_copy_result(Test_Runner_Result *out_result,
@@ -64,7 +70,10 @@ static bool test_daemon_client_copy_result(Test_Runner_Result *out_result,
6470
*out_result = (Test_Runner_Result){0};
6571
out_result->ok = payload->runner_ok != 0;
6672
out_result->exit_code = payload->exit_code;
67-
return test_daemon_client_copy_cstr(out_result->preserved_workspace_path,
73+
return test_daemon_client_copy_cstr(out_result->case_name,
74+
sizeof(out_result->case_name),
75+
payload->case_name) &&
76+
test_daemon_client_copy_cstr(out_result->preserved_workspace_path,
6877
sizeof(out_result->preserved_workspace_path),
6978
payload->preserved_workspace_path) &&
7079
test_daemon_client_copy_cstr(out_result->stdout_log_path,
@@ -75,7 +84,10 @@ static bool test_daemon_client_copy_result(Test_Runner_Result *out_result,
7584
payload->stderr_log_path) &&
7685
test_daemon_client_copy_cstr(out_result->summary,
7786
sizeof(out_result->summary),
78-
payload->summary);
87+
payload->summary) &&
88+
test_daemon_client_copy_cstr(out_result->failure_summary,
89+
sizeof(out_result->failure_summary),
90+
payload->failure_summary);
7991
}
8092

8193
static int test_daemon_client_connect(void) {
@@ -199,12 +211,13 @@ static void test_daemon_client_log_error_payload(const Test_Daemon_Error_Payload
199211
if ((Test_Daemon_Request_Kind)active->kind == TEST_DAEMON_REQUEST_NONE) return;
200212

201213
nob_log(NOB_ERROR,
202-
"daemon state=%s active=%s policy=%s module=%s profile=%s duration=%lluus",
214+
"daemon state=%s active=%s policy=%s module=%s profile=%s case=%s duration=%lluus",
203215
test_daemon_state_name((Test_Daemon_State)payload->daemon_state),
204216
test_daemon_request_kind_name((Test_Daemon_Request_Kind)active->kind),
205217
test_daemon_admission_policy_name((Test_Daemon_Admission_Policy)active->admission_policy),
206218
active->module_name[0] != '\0' ? active->module_name : "<none>",
207219
active->profile_name[0] != '\0' ? active->profile_name : "<none>",
220+
active->case_name[0] != '\0' ? active->case_name : "<all>",
208221
(unsigned long long)active->active_duration_usec);
209222
}
210223

@@ -291,7 +304,7 @@ bool test_daemon_client_cleanup_stale_artifacts(void) {
291304

292305
static bool test_daemon_client_build_run_argv(const Test_Runner_Request *request,
293306
int *out_argc,
294-
const char *argv_buf[3]) {
307+
const char *argv_buf[5]) {
295308
const Test_Runner_Module_Def *module = NULL;
296309
const Test_Runner_Profile_Def *profile = NULL;
297310
int argc = 0;
@@ -318,6 +331,11 @@ static bool test_daemon_client_build_run_argv(const Test_Runner_Request *request
318331
argv_buf[argc++] = profile->front_door_flag;
319332
}
320333

334+
if (request->case_name[0] != '\0') {
335+
argv_buf[argc++] = "--case";
336+
argv_buf[argc++] = request->case_name;
337+
}
338+
321339
*out_argc = argc;
322340
return true;
323341
}
@@ -372,14 +390,20 @@ static void test_daemon_client_log_watch_result(const Test_Daemon_Result_Payload
372390
if (payload->stderr_log_path[0] != '\0') {
373391
fprintf(stderr, "[watch] stderr log: %s\n", payload->stderr_log_path);
374392
}
393+
if (payload->failure_summary[0] != '\0') {
394+
fprintf(stderr, "%s", payload->failure_summary);
395+
if (payload->failure_summary[strlen(payload->failure_summary) - 1] != '\n') {
396+
fputc('\n', stderr);
397+
}
398+
}
375399
fflush(stderr);
376400
}
377401

378402
bool test_daemon_client_run_request(const Test_Runner_Request *request,
379403
Test_Runner_Result *out_result) {
380404
Test_Daemon_Message message = {0};
381405
Test_Daemon_Result_Payload payload = {0};
382-
const char *argv_buf[3] = {0};
406+
const char *argv_buf[5] = {0};
383407
int argc = 0;
384408
int fd = -1;
385409
bool ok = false;
@@ -437,6 +461,13 @@ bool test_daemon_client_run_request(const Test_Runner_Request *request,
437461
}
438462
memcpy(&payload, message.payload, sizeof(payload));
439463
if (out_result && !test_daemon_client_copy_result(out_result, &payload)) goto defer;
464+
if (payload.runner_ok == 0 && payload.failure_summary[0] != '\0') {
465+
fprintf(stderr, "%s", payload.failure_summary);
466+
if (payload.failure_summary[strlen(payload.failure_summary) - 1] != '\n') {
467+
fputc('\n', stderr);
468+
}
469+
fflush(stderr);
470+
}
440471
ok = payload.runner_ok != 0;
441472
goto defer;
442473

src_v2/build/test_daemon_client.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ typedef struct {
2323
char preserved_workspace_path[TEST_RUNNER_PATH_CAPACITY];
2424
char stdout_log_path[TEST_RUNNER_PATH_CAPACITY];
2525
char stderr_log_path[TEST_RUNNER_PATH_CAPACITY];
26+
char case_name[TEST_RUNNER_CASE_NAME_CAPACITY];
2627
char detail[TEST_RUNNER_SUMMARY_CAPACITY];
2728
char summary[TEST_RUNNER_SUMMARY_CAPACITY];
29+
char failure_summary[TEST_RUNNER_FAILURE_SUMMARY_CAPACITY];
2830
} Test_Daemon_Status;
2931

3032
bool test_daemon_client_run_request(const Test_Runner_Request *request,

src_v2/build/test_daemon_protocol.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,22 @@ bool test_daemon_result_from_runner(Test_Daemon_State daemon_state,
329329
runner_result->stderr_log_path)) {
330330
return false;
331331
}
332+
if (!test_daemon_copy_string(out_payload->case_name,
333+
sizeof(out_payload->case_name),
334+
runner_result->case_name)) {
335+
return false;
336+
}
332337
out_payload->detail[0] = '\0';
333338
if (!test_daemon_copy_string(out_payload->summary,
334339
sizeof(out_payload->summary),
335340
runner_result->summary)) {
336341
return false;
337342
}
343+
if (!test_daemon_copy_string(out_payload->failure_summary,
344+
sizeof(out_payload->failure_summary),
345+
runner_result->failure_summary)) {
346+
return false;
347+
}
338348
return true;
339349
}
340350

0 commit comments

Comments
 (0)