mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
test(coverage): pin the truncation marker, the partial ceiling and the grammar limit (#963)
Phase 5. Four test groups, each checked RED before it was kept. - The Studio Export range join puts ONE ",+<N>" marker at the end with the summed drop count. A marker left mid-string makes every reader stop there and silently lose the ranges after it. Reaching the join through the pipeline needs an export file with 256+ error regions across two <Class> elements, so it goes through a test seam, following the pattern already in this repo (CBM_COVERAGE_MARKER_TEST_API). - check_index_coverage emits every range in front of a marker, never turns the marker's digits into a range, and reports "truncated" from BOTH caps — the producer's and its own 256 limit. - test_index_resilience now has a ceiling beside its floor: exactly one of the two fixture files is flagged, the clean neighbour is absent, and the range does not cover the whole file. - The three _Thread_local forms are pinned as measured. Only the array form fails today; the plan's Phase 0 also listed the pointer form, and that is wrong on the grammar shipped now. Also fixes 13 clang-format violations the earlier commits on this branch left in cbm.c, mcp.c and pass_definitions.c. `make -f Makefile.cbm lint-format` would have failed CI. The changes are whitespace only — the two reflowed tool descriptions concatenate byte-identically, so no output moved. Full suite: 7735 passed, 28 failed, 7 skipped. The 28 are the pre-existing cli install/uninstall failures, identical at clean HEAD. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joshua Richter <jrichter5781@gmail.com>
This commit is contained in:
1 parent
a953509275
commit
219b348263
8 files changed
+254
-15
No files matched your search
+3
-1
@@ -98,9 +98,10 @@ endif
|
||||
KOTLIN_DEDUP_TEST_DEFINE = -DCBM_KOTLIN_DEDUP_TEST_API=1
|
||||
CALL_REFERENCE_LOOKUP_TEST_DEFINE = -DCBM_CALL_REFERENCE_LOOKUP_TEST_API=1
|
||||
INCREMENTAL_TEST_DEFINE = -DCBM_INCREMENTAL_TEST_API=1
|
||||
COVERAGE_MARKER_TEST_DEFINE = -DCBM_COVERAGE_MARKER_TEST_API=1
|
||||
CFLAGS_TEST = $(CFLAGS_COMMON) $(EDITOR_TEST_DEFINES) $(SANITIZED_DEFINE) \
|
||||
$(KOTLIN_DEDUP_TEST_DEFINE) $(CALL_REFERENCE_LOOKUP_TEST_DEFINE) \
|
||||
$(INCREMENTAL_TEST_DEFINE) -g -O1 $(SANITIZE)
|
||||
$(INCREMENTAL_TEST_DEFINE) $(COVERAGE_MARKER_TEST_DEFINE) -g -O1 $(SANITIZE)
|
||||
CXXFLAGS_TEST = $(CXXFLAGS_COMMON) $(SANITIZED_DEFINE) -g -O1 $(SANITIZE) $(CXX_STDLIB_FLAGS)
|
||||
|
||||
# TSan (can't combine with ASan)
|
||||
@@ -118,6 +119,7 @@ TSAN_SANITIZE = -fsanitize=thread -fno-omit-frame-pointer
|
||||
# macro of ours.
|
||||
CFLAGS_TSAN = $(CFLAGS_COMMON) $(EDITOR_TEST_DEFINES) $(KOTLIN_DEDUP_TEST_DEFINE) \
|
||||
$(CALL_REFERENCE_LOOKUP_TEST_DEFINE) $(INCREMENTAL_TEST_DEFINE) \
|
||||
$(COVERAGE_MARKER_TEST_DEFINE) \
|
||||
-DCBM_SANITIZED_BUILD=1 -g -O1 $(TSAN_SANITIZE)
|
||||
CXXFLAGS_TSAN = $(CXXFLAGS_COMMON) -DCBM_SANITIZED_BUILD=1 -g -O1 \
|
||||
$(TSAN_SANITIZE)
|
||||
|
||||
+3
-4
@@ -1801,16 +1801,15 @@ static CBMFileResult *extract_file_ex_body(const char *source, int source_len, C
|
||||
orig_lines++;
|
||||
}
|
||||
}
|
||||
uint8_t *map =
|
||||
(uint8_t *)cbm_arena_alloc(a, (size_t)orig_lines + 2);
|
||||
uint8_t *map = (uint8_t *)cbm_arena_alloc(a, (size_t)orig_lines + 2);
|
||||
int exp_lines = preprocessed->expanded_line_count;
|
||||
uint8_t *bad_rows =
|
||||
exp_lines > 0 ? (uint8_t *)calloc((size_t)exp_lines + 2, 1) : NULL;
|
||||
if (map && bad_rows) {
|
||||
memset(map, 0, (size_t)orig_lines + 2);
|
||||
cbm_mark_no_code_lines(source, source_len, map, orig_lines);
|
||||
cbm_mark_pp_error_rows(pp_root, bad_rows, (uint32_t)exp_lines,
|
||||
expanded, expanded_len);
|
||||
cbm_mark_pp_error_rows(pp_root, bad_rows, (uint32_t)exp_lines, expanded,
|
||||
expanded_len);
|
||||
/* Walk the expanded text once. An expanded line
|
||||
* only vouches for its original line when it
|
||||
* actually HAS content: the preprocessor emits a
|
||||
|
||||
@@ -561,9 +561,9 @@ static int objectscript_export_split_range_marker(const char *ranges, size_t *bo
|
||||
if (i == len || i == 0 || ranges[i - 1] != '+') {
|
||||
return 0;
|
||||
}
|
||||
size_t marker = i - 1; /* index of '+' */
|
||||
size_t marker = i - 1; /* index of '+' */
|
||||
if (marker > 0 && ranges[marker - 1] == ',') {
|
||||
marker--; /* drop the separator too */
|
||||
marker--; /* drop the separator too */
|
||||
}
|
||||
*body_len = marker;
|
||||
return atoi(ranges + i);
|
||||
@@ -612,8 +612,8 @@ static bool objectscript_export_append_error_ranges(CBMFileResult *aggregate,
|
||||
return false;
|
||||
}
|
||||
if (dropped > 0) {
|
||||
combined = cbm_arena_sprintf(&aggregate->arena, "%s%s+%d", combined,
|
||||
combined[0] ? "," : "", dropped);
|
||||
combined = cbm_arena_sprintf(&aggregate->arena, "%s%s+%d", combined, combined[0] ? "," : "",
|
||||
dropped);
|
||||
if (!combined) {
|
||||
return false;
|
||||
}
|
||||
@@ -622,6 +622,16 @@ static bool objectscript_export_append_error_ranges(CBMFileResult *aggregate,
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(CBM_COVERAGE_MARKER_TEST_API) && CBM_COVERAGE_MARKER_TEST_API
|
||||
/* Test seam. This join only fires for a Studio Export file holding several
|
||||
* <Class> elements where a class overruns the 256-region cap — hard to reach
|
||||
* through the pipeline, easy to get wrong, and a wrong result hides ranges
|
||||
* without saying so. Expose the join so the marker rules can be pinned. */
|
||||
bool cbm_pipeline_coverage_marker_test_join(CBMFileResult *aggregate, const CBMFileResult *part) {
|
||||
return objectscript_export_append_error_ranges(aggregate, part);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Studio Export files may contain multiple <Class> elements, while the
|
||||
* pipeline cache has one slot per physical file. Extract each generated UDL
|
||||
* class independently (preserving the upstream parser behavior), then compose
|
||||
@@ -842,10 +852,9 @@ int cbm_pipeline_pass_definitions(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t
|
||||
} else if (result->parse_incomplete) {
|
||||
/* Best-effort parse-coverage signal (#963): indexed, but with
|
||||
* ERROR/MISSING regions — see pass_parallel.c (keep in sync). */
|
||||
cbm_pipeline_add_file_error(ctx->pipeline, rel,
|
||||
result->error_ranges ? result->error_ranges : "unknown",
|
||||
result->parse_unusable ? "parse_unusable"
|
||||
: "parse_partial");
|
||||
cbm_pipeline_add_file_error(
|
||||
ctx->pipeline, rel, result->error_ranges ? result->error_ranges : "unknown",
|
||||
result->parse_unusable ? "parse_unusable" : "parse_partial");
|
||||
}
|
||||
|
||||
/* Create nodes for each definition */
|
||||
|
||||
@@ -864,6 +864,12 @@ void cbm_pp_bp_nap_cycles_reset(void);
|
||||
uint64_t cbm_pp_lsp_linear_fallback_rows(void);
|
||||
void cbm_pp_lsp_linear_fallback_rows_reset(void);
|
||||
|
||||
#if defined(CBM_COVERAGE_MARKER_TEST_API) && CBM_COVERAGE_MARKER_TEST_API
|
||||
/* Test-only view of the Studio Export range join, so the ",+<N>" truncation
|
||||
* marker rules can be checked without building a 256-region export file. */
|
||||
bool cbm_pipeline_coverage_marker_test_join(CBMFileResult *aggregate, const CBMFileResult *part);
|
||||
#endif
|
||||
|
||||
#if defined(CBM_CALL_REFERENCE_LOOKUP_TEST_API) && CBM_CALL_REFERENCE_LOOKUP_TEST_API
|
||||
/* Deterministic test-only operation count for the shared semantic-reference
|
||||
* matcher used by both sequential and fused-parallel usage materialization. */
|
||||
|
||||
@@ -337,8 +337,16 @@ TEST(index_parse_partial_reported) {
|
||||
ASSERT_STR_EQ("indexed", status);
|
||||
ASSERT_EQ(yyjson_get_int(yyjson_obj_get(sc, "skipped_count")), 0);
|
||||
|
||||
/* The coverage signal is surfaced with ranges + the best-effort note. */
|
||||
/* The coverage signal is surfaced with ranges + the best-effort note.
|
||||
* Both bounds matter. The floor catches the signal going missing. The
|
||||
* ceiling catches the opposite failure: exactly one of the two files has
|
||||
* a gap, so a count above 1 means the clean Python neighbour got flagged
|
||||
* as well, which is how over-flagging looks from the outside. */
|
||||
ASSERT_GTE(yyjson_get_int(yyjson_obj_get(sc, "parse_partial_count")), 1);
|
||||
ASSERT_EQ(yyjson_get_int(yyjson_obj_get(sc, "parse_partial_count")), 1);
|
||||
/* A local gap is not a whole-file failure, so the other coverage kind
|
||||
* must stay empty here. */
|
||||
ASSERT_EQ(yyjson_get_int(yyjson_obj_get(sc, "parse_unusable_count")), 0);
|
||||
yyjson_val *pp = yyjson_obj_get(sc, "parse_partial");
|
||||
ASSERT_NOT_NULL(pp);
|
||||
yyjson_val *files = yyjson_obj_get(pp, "files");
|
||||
@@ -354,7 +362,13 @@ TEST(index_parse_partial_reported) {
|
||||
found_split = 1;
|
||||
ASSERT_NOT_NULL(ranges);
|
||||
ASSERT_GT((int)strlen(ranges), 0);
|
||||
/* The gap is the two-header block, not the whole file. An
|
||||
* 8-line file reported as 1-8 would be the old whole-file
|
||||
* blame coming back. */
|
||||
ASSERT_NULL(strstr(ranges, "1-8"));
|
||||
}
|
||||
/* The clean file must not appear in the list at all. */
|
||||
ASSERT_NULL(fp ? strstr(fp, "good.py") : NULL);
|
||||
}
|
||||
ASSERT_TRUE(found_split);
|
||||
const char *note = yyjson_get_str(yyjson_obj_get(pp, "note"));
|
||||
|
||||
@@ -5425,6 +5425,86 @@ TEST(tool_check_index_coverage_pages_exact_paths_and_restores_raw_diagnostics) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* The range string can carry a trailing ",+<N>" marker saying the producer hit
|
||||
* its own cap and threw ranges away. The reader must emit every range in front
|
||||
* of the marker, must not turn the marker itself into a range, and must say
|
||||
* "ranges_truncated" so nobody reads a short list as a complete one. The reader has a
|
||||
* second cap of its own, and that one must report itself the same way. */
|
||||
TEST(tool_check_index_coverage_reports_truncation_marker_issue963) {
|
||||
enum { WIDE_RANGE_COUNT = 300 };
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
ASSERT_NOT_NULL(srv);
|
||||
cbm_store_t *st = cbm_mcp_server_store(srv);
|
||||
ASSERT_NOT_NULL(st);
|
||||
|
||||
const char *project = "coverage-marker";
|
||||
ASSERT_EQ(cbm_store_upsert_project(st, project, "/tmp/coverage-marker"), CBM_STORE_OK);
|
||||
cbm_mcp_server_set_project(srv, project);
|
||||
|
||||
/* 300 one-line ranges — past the reader's own 256 limit. */
|
||||
char *wide = calloc(1, WIDE_RANGE_COUNT * 12 + 1);
|
||||
ASSERT_NOT_NULL(wide);
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < WIDE_RANGE_COUNT; i++) {
|
||||
off += (size_t)snprintf(wide + off, WIDE_RANGE_COUNT * 12 + 1 - off, "%s%d-%d",
|
||||
i ? "," : "", i * 3 + 1, i * 3 + 1);
|
||||
}
|
||||
|
||||
cbm_coverage_row_t rows[3] = {
|
||||
{.rel_path = "src/marked.c", .kind = "parse_partial", .detail = "3-4,9-9,+12"},
|
||||
{.rel_path = "src/plain.c", .kind = "parse_partial", .detail = "3-4,9-9"},
|
||||
{.rel_path = "src/wide.c", .kind = "parse_partial", .detail = wide},
|
||||
};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ASSERT_EQ(cbm_store_upsert_file_hash(st, project, rows[i].rel_path, "fixture", i + 1, 10),
|
||||
CBM_STORE_OK);
|
||||
}
|
||||
ASSERT_EQ(cbm_store_coverage_replace(st, project, rows, 3), CBM_STORE_OK);
|
||||
|
||||
/* The marked file: both real ranges survive, the marker is flagged, and the
|
||||
* "12" from the marker never becomes a range of its own. */
|
||||
char *marked =
|
||||
cbm_mcp_handle_tool(srv, "check_index_coverage",
|
||||
"{\"project\":\"coverage-marker\",\"paths\":[\"src/marked.c\"],\"format\":\"json\"}");
|
||||
ASSERT_NOT_NULL(marked);
|
||||
char *marked_inner = extract_text_content(marked);
|
||||
ASSERT_NOT_NULL(marked_inner);
|
||||
ASSERT_NOT_NULL(strstr(marked_inner, "\"start\":3"));
|
||||
ASSERT_NOT_NULL(strstr(marked_inner, "\"start\":9"));
|
||||
ASSERT_NULL(strstr(marked_inner, "\"start\":12"));
|
||||
ASSERT_NOT_NULL(strstr(marked_inner, "\"ranges_truncated\":true"));
|
||||
free(marked_inner);
|
||||
free(marked);
|
||||
|
||||
/* The same ranges without a marker must NOT be reported as truncated. */
|
||||
char *plain =
|
||||
cbm_mcp_handle_tool(srv, "check_index_coverage",
|
||||
"{\"project\":\"coverage-marker\",\"paths\":[\"src/plain.c\"],\"format\":\"json\"}");
|
||||
ASSERT_NOT_NULL(plain);
|
||||
char *plain_inner = extract_text_content(plain);
|
||||
ASSERT_NOT_NULL(plain_inner);
|
||||
ASSERT_NOT_NULL(strstr(plain_inner, "\"start\":3"));
|
||||
ASSERT_NULL(strstr(plain_inner, "\"ranges_truncated\":true"));
|
||||
free(plain_inner);
|
||||
free(plain);
|
||||
|
||||
/* The reader's own limit stops the list early, so it must say so even
|
||||
* though the producer sent no marker. */
|
||||
char *widest =
|
||||
cbm_mcp_handle_tool(srv, "check_index_coverage",
|
||||
"{\"project\":\"coverage-marker\",\"paths\":[\"src/wide.c\"],\"format\":\"json\"}");
|
||||
ASSERT_NOT_NULL(widest);
|
||||
char *wide_inner = extract_text_content(widest);
|
||||
ASSERT_NOT_NULL(wide_inner);
|
||||
ASSERT_NOT_NULL(strstr(wide_inner, "\"ranges_truncated\":true"));
|
||||
free(wide_inner);
|
||||
free(widest);
|
||||
|
||||
free(wide);
|
||||
cbm_mcp_server_free(srv);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(tool_check_index_coverage_reports_paths_scopes_and_ranges) {
|
||||
char tmp[256];
|
||||
cbm_mcp_server_t *srv = setup_snippet_server(tmp, sizeof(tmp));
|
||||
@@ -19846,6 +19926,7 @@ SUITE(mcp) {
|
||||
RUN_TEST(tool_list_projects_preserves_root_beyond_one_kib);
|
||||
RUN_TEST(tool_index_status_no_project);
|
||||
RUN_TEST(tool_check_index_coverage_finds_path_beyond_status_cap);
|
||||
RUN_TEST(tool_check_index_coverage_reports_truncation_marker_issue963);
|
||||
RUN_TEST(tool_check_index_coverage_reports_paths_scopes_and_ranges);
|
||||
RUN_TEST(tool_check_index_coverage_pages_exact_paths_and_restores_raw_diagnostics);
|
||||
RUN_TEST(tool_check_index_coverage_preserves_multiple_scope_labels);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* GREEN (fixed): cbm_extract_file sets parse_incomplete=true iff the tree
|
||||
* contains ERROR/MISSING nodes, records the 1-based line
|
||||
* ranges of the TOP-MOST error regions ("start-end,..."),
|
||||
* bounded by the 64-region cap, and clean files stay
|
||||
* bounded by the 256-region cap, and clean files stay
|
||||
* completely unflagged (no false positives).
|
||||
*
|
||||
* BEST-EFFORT framing (must never be weakened the other way): a flag means
|
||||
@@ -791,6 +791,49 @@ TEST(c_ifdef_split_is_partial_never_unusable) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Phase 0 finding 2, pinned so a tree-sitter bump cannot change it quietly.
|
||||
*
|
||||
* The C grammar handles `_Thread_local` unevenly, and these are the three
|
||||
* forms measured on the grammar shipped today:
|
||||
*
|
||||
* static _Thread_local int x = 0; parses clean
|
||||
* static _Thread_local int *p; parses clean
|
||||
* static _Thread_local char b[8]; fails — flagged as range 1-1
|
||||
*
|
||||
* The array line really is missing from the graph, so flagging it is the
|
||||
* honest answer, not a false positive. This test exists to make a grammar
|
||||
* bump visible: if a newer grammar fixes the array form, this goes red and
|
||||
* says so, instead of leaving a wrong note in the plan. (The plan's Phase 0
|
||||
* also listed the pointer form as failing. It does not fail today.) */
|
||||
TEST(c_thread_local_grammar_limit_is_pinned_issue963) {
|
||||
CBMFileResult *ok = do_extract("static _Thread_local int x = 0;\n"
|
||||
"void f(void) { x = 1; }\n",
|
||||
CBM_LANG_C, "tls_init.c");
|
||||
ASSERT_NOT_NULL(ok);
|
||||
ASSERT_FALSE(ok->parse_incomplete);
|
||||
cbm_free_result(ok);
|
||||
|
||||
CBMFileResult *ptr = do_extract("static _Thread_local int *p;\n"
|
||||
"void f(void) { p = 0; }\n",
|
||||
CBM_LANG_C, "tls_ptr.c");
|
||||
ASSERT_NOT_NULL(ptr);
|
||||
ASSERT_FALSE(ptr->parse_incomplete);
|
||||
cbm_free_result(ptr);
|
||||
|
||||
CBMFileResult *arr = do_extract("static _Thread_local char b[8];\n"
|
||||
"void f(void) { b[0] = 0; }\n",
|
||||
CBM_LANG_C, "tls_arr.c");
|
||||
ASSERT_NOT_NULL(arr);
|
||||
ASSERT_TRUE(arr->parse_incomplete);
|
||||
ASSERT_NOT_NULL(arr->error_ranges);
|
||||
/* The range names the one broken line, not the whole file. */
|
||||
ASSERT_STR_EQ("1-1", arr->error_ranges);
|
||||
/* The clean function below it still reaches the graph. */
|
||||
ASSERT_TRUE(has_def(arr, "f"));
|
||||
cbm_free_result(arr);
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(parse_coverage) {
|
||||
RUN_TEST(c_ifdef_split_brace_sets_parse_incomplete);
|
||||
RUN_TEST(c_ifdef_split_brace_neighbors_still_extracted);
|
||||
@@ -826,4 +869,5 @@ SUITE(parse_coverage) {
|
||||
RUN_TEST(dockerfile_trailing_blank_at_eof_not_flagged_issue1746);
|
||||
RUN_TEST(real_error_before_eof_still_flagged_with_trailing_blank_issue1746);
|
||||
RUN_TEST(width_bearing_error_at_eof_still_flagged_with_trailing_blank_issue1746);
|
||||
RUN_TEST(c_thread_local_grammar_limit_is_pinned_issue963);
|
||||
}
|
||||
@@ -13409,6 +13409,87 @@ TEST(pipeline_markdown_and_config_prose_reaches_fts_body) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
#if defined(CBM_COVERAGE_MARKER_TEST_API) && CBM_COVERAGE_MARKER_TEST_API
|
||||
/* Join two Studio Export range strings and hand back the result. The caller
|
||||
* owns nothing: the string lives in the aggregate's arena, so copy it out
|
||||
* before the arena goes away. */
|
||||
static void join_export_ranges(const char *agg_ranges, int agg_regions, const char *part_ranges,
|
||||
int part_regions, char *out, size_t out_size, int *out_regions) {
|
||||
CBMFileResult aggregate;
|
||||
CBMFileResult part;
|
||||
memset(&aggregate, 0, sizeof(aggregate));
|
||||
memset(&part, 0, sizeof(part));
|
||||
cbm_arena_init(&aggregate.arena);
|
||||
cbm_arena_init(&part.arena);
|
||||
aggregate.error_ranges = agg_ranges;
|
||||
aggregate.error_region_count = agg_regions;
|
||||
aggregate.parse_incomplete = true;
|
||||
part.error_ranges = part_ranges;
|
||||
part.error_region_count = part_regions;
|
||||
part.parse_incomplete = true;
|
||||
|
||||
out[0] = '\0';
|
||||
*out_regions = 0;
|
||||
if (cbm_pipeline_coverage_marker_test_join(&aggregate, &part)) {
|
||||
snprintf(out, out_size, "%s", aggregate.error_ranges ? aggregate.error_ranges : "");
|
||||
*out_regions = aggregate.error_region_count;
|
||||
}
|
||||
cbm_arena_destroy(&aggregate.arena);
|
||||
cbm_arena_destroy(&part.arena);
|
||||
}
|
||||
|
||||
/* Count the "+" characters in a range string. A truncation marker must appear
|
||||
* once and only at the end: every reader stops at the first token that is not
|
||||
* a range, so a marker in the middle silently hides every range after it. */
|
||||
static int count_plus(const char *s) {
|
||||
int n = 0;
|
||||
for (const char *p = s; *p; p++) {
|
||||
if (*p == '+') {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
TEST(pipeline_objectscript_export_range_join_keeps_one_trailing_marker) {
|
||||
char joined[256];
|
||||
int regions = 0;
|
||||
|
||||
/* Neither side dropped anything, so nothing invents a marker. */
|
||||
join_export_ranges("1-2,5-9", 2, "20-24", 1, joined, sizeof(joined), ®ions);
|
||||
ASSERT_STR_EQ("1-2,5-9,20-24", joined);
|
||||
ASSERT_EQ(0, count_plus(joined));
|
||||
ASSERT_EQ(3, regions);
|
||||
|
||||
/* The first class overran the cap. Its marker must move to the end, so the
|
||||
* second class's ranges stay visible in front of it. */
|
||||
join_export_ranges("1-2,5-9,+7", 2, "20-24", 1, joined, sizeof(joined), ®ions);
|
||||
ASSERT_STR_EQ("1-2,5-9,20-24,+7", joined);
|
||||
ASSERT_EQ(1, count_plus(joined));
|
||||
|
||||
/* The second class overran the cap. Same single trailing marker. */
|
||||
join_export_ranges("1-2", 1, "20-24,+3", 1, joined, sizeof(joined), ®ions);
|
||||
ASSERT_STR_EQ("1-2,20-24,+3", joined);
|
||||
ASSERT_EQ(1, count_plus(joined));
|
||||
|
||||
/* Both overran. One marker, carrying the sum, or the report would
|
||||
* under-count what it threw away. */
|
||||
join_export_ranges("1-2,+7", 1, "20-24,+3", 1, joined, sizeof(joined), ®ions);
|
||||
ASSERT_STR_EQ("1-2,20-24,+10", joined);
|
||||
ASSERT_EQ(1, count_plus(joined));
|
||||
|
||||
/* An empty aggregate is the first class in the file. No leading comma. */
|
||||
join_export_ranges(NULL, 0, "20-24,+3", 1, joined, sizeof(joined), ®ions);
|
||||
ASSERT_STR_EQ("20-24,+3", joined);
|
||||
ASSERT_EQ(1, regions);
|
||||
|
||||
/* A part with nothing to say leaves the aggregate exactly as it was. */
|
||||
join_export_ranges("1-2,+7", 1, "", 0, joined, sizeof(joined), ®ions);
|
||||
ASSERT_STR_EQ("1-2,+7", joined);
|
||||
PASS();
|
||||
}
|
||||
#endif
|
||||
|
||||
SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_lsp_surface_persisted_and_body_edit_invariant);
|
||||
/* Index lock */
|
||||
@@ -13453,6 +13534,9 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_objectscript_export_preserves_calls_sequential_parallel);
|
||||
RUN_TEST(pipeline_objectscript_export_incremental_matches_full_relationships);
|
||||
RUN_TEST(pipeline_objectscript_export_aggregate_exceeds_arena_block_table);
|
||||
#if defined(CBM_COVERAGE_MARKER_TEST_API) && CBM_COVERAGE_MARKER_TEST_API
|
||||
RUN_TEST(pipeline_objectscript_export_range_join_keeps_one_trailing_marker);
|
||||
#endif
|
||||
RUN_TEST(pipeline_env_access_configures_sequential_parallel_parity);
|
||||
RUN_TEST(pipeline_call_reference_sequential_parallel_edge_set_parity);
|
||||
RUN_TEST(pipeline_incremental_cross_file_call_reference_matches_fresh_full);
|
||||
|
||||
Reference in new issue
Block a user