|
10 | 10 | #include "test_helpers.h" |
11 | 11 | #include "foundation/mem.h" // cbm_mem_init/budget (back-pressure futile-nap test) |
12 | 12 | #include "pipeline/pipeline.h" |
| 13 | +#include "pipeline/lsp_surface.h" |
13 | 14 | #include "pipeline/pipeline_internal.h" |
14 | 15 | #include "pipeline/artifact.h" |
15 | 16 | #include "store/store.h" |
@@ -14252,6 +14253,48 @@ TEST(pipeline_seq_ts_cross_uses_shared_registry) { |
14252 | 14253 | PASS(); |
14253 | 14254 | } |
14254 | 14255 |
|
| 14256 | +/* Object arrays are non-flat in yyjson. Exercise ordered decoding and arena |
| 14257 | + * ownership across a large surface, including nested string arrays. */ |
| 14258 | +TEST(pipeline_lsp_surface_large_object_array_decode) { |
| 14259 | + const int count = 8192; |
| 14260 | + const size_t capacity = (size_t)count * 192 + 64; |
| 14261 | + char *json = malloc(capacity); |
| 14262 | + ASSERT_NOT_NULL(json); |
| 14263 | + size_t used = (size_t)snprintf(json, capacity, "{\"v\":1,\"lsp\":["); |
| 14264 | + for (int i = 0; i < count; i++) { |
| 14265 | + int n = snprintf(json + used, capacity - used, |
| 14266 | + "%s{\"qn\":\"pkg.f%d\",\"sn\":\"f%d\",\"lb\":\"Function\"," |
| 14267 | + "\"spt\":[\"str\",\"int\"],\"dec\":[\"first\",\"second\"]}", |
| 14268 | + i ? "," : "", i, i); |
| 14269 | + ASSERT_TRUE(n > 0 && (size_t)n < capacity - used); |
| 14270 | + used += (size_t)n; |
| 14271 | + } |
| 14272 | + snprintf(json + used, capacity - used, "]}"); |
| 14273 | + CBMArena arena; |
| 14274 | + cbm_arena_init(&arena); |
| 14275 | + CBMLSPDef *defs = NULL; |
| 14276 | + int decoded = cbm_lsp_surface_defs_from_json(&arena, json, &defs); |
| 14277 | + free(json); |
| 14278 | + ASSERT_EQ(decoded, count); |
| 14279 | + ASSERT_NOT_NULL(defs); |
| 14280 | + for (int i = 0; i < count; i++) { |
| 14281 | + char expected[64]; |
| 14282 | + snprintf(expected, sizeof(expected), "pkg.f%d", i); |
| 14283 | + ASSERT_STR_EQ(defs[i].qualified_name, expected); |
| 14284 | + snprintf(expected, sizeof(expected), "f%d", i); |
| 14285 | + ASSERT_STR_EQ(defs[i].short_name, expected); |
| 14286 | + ASSERT_STR_EQ(defs[i].label, "Function"); |
| 14287 | + ASSERT_EQ(defs[i].signature_param_count, 2); |
| 14288 | + ASSERT_STR_EQ(defs[i].signature_param_types[0], "str"); |
| 14289 | + ASSERT_STR_EQ(defs[i].signature_param_types[1], "int"); |
| 14290 | + ASSERT_STR_EQ(defs[i].decorators[0], "first"); |
| 14291 | + ASSERT_STR_EQ(defs[i].decorators[1], "second"); |
| 14292 | + ASSERT_NULL(defs[i].decorators[2]); |
| 14293 | + } |
| 14294 | + cbm_arena_destroy(&arena); |
| 14295 | + PASS(); |
| 14296 | +} |
| 14297 | + |
14255 | 14298 | /* The closure-repair route lives and dies by two properties of the persisted |
14256 | 14299 | * per-file LSP surface: a BODY edit must leave the surface_sha unchanged (the |
14257 | 14300 | * early cutoff -- no dependent recomputation owed), while a SIGNATURE edit |
@@ -15234,6 +15277,7 @@ TEST(pipeline_objectscript_export_range_join_keeps_one_trailing_marker) { |
15234 | 15277 | #endif |
15235 | 15278 |
|
15236 | 15279 | SUITE(pipeline) { |
| 15280 | + RUN_TEST(pipeline_lsp_surface_large_object_array_decode); |
15237 | 15281 | RUN_TEST(pipeline_lsp_surface_persisted_and_body_edit_invariant); |
15238 | 15282 | /* Index lock */ |
15239 | 15283 | RUN_TEST(pipeline_lock_try_acquire); |
|
0 commit comments