fix(test-impact): build and run on Linux GCC, git before 2.44, LeakSanitizer

PR CI on the first push of these commits found what no local leg had
built yet:

- test_impact_result_validate.c: initialize the count GCC's
  -Wmaybe-uninitialized flags (-Werror on every Linux build: smoke,
  shadow, analyze).
- tests: split one-line `if` chains GCC reports as misleading
  indentation, and drop a redundant `= {0}` it reads as missing braces
  (the GCC test runner did not compile).
- tests/test_test_impact_engine.c: realpath() into a PATH_MAX buffer;
  glibc's _FORTIFY_SOURCE aborts on anything smaller, as
  test_index_resilience.c already notes.
- test_impact_git.c: pass --no-lazy-fetch only when the git executable
  takes it (git 2.44+, probed once per session with the option on
  --version). Older git rejected it and every engine command failed;
  Ubuntu 24.04 ships git 2.43. `-c protocol.allow=never` already turns a
  lazy fetch into an error there, which is what the option asks for.
- test_impact_engine.c: the selection and publish job threads release
  their thread-local node-type bitset cache before they end, as the
  parallel workers and the MCP index thread do. LeakSanitizer reported it
  (488 bytes in 13 allocations, test-diag); the Linux arm64 ASan leg
  reproduced exactly that and is clean with the fix.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel committed 2026-10-05 23:55:25 +02:00
1 parent 867e5f883d
commit 6a29a19c23
7 files changed
+46 -9

No files matched your search

+6
View File
@@ -13,6 +13,7 @@
#include "foundation/sha256.h"
#include "store/store_graph_digest.h"
#include "store/store_impact.h"
#include "helpers.h" /* cbm_kind_in_set_free_cache — the job thread's cache teardown */
#include <stdio.h>
#include <stdlib.h>
@@ -621,6 +622,10 @@ static void *te_job_main(void *arg) {
status = CBM_TEST_IMPACT_CANCELLED;
}
te_free(&c);
/* The snapshot build extracted on THIS thread, which ends here: its
* thread-local node-type bitset cache goes with it (as the MCP index
* thread and the parallel workers release theirs). */
cbm_kind_in_set_free_cache();
job->status = status;
return NULL;
}
@@ -678,6 +683,7 @@ static void *te_publish_main(void *arg) {
bool ok = te_publish(&c, p);
job->status = c.oom ? CBM_TEST_IMPACT_OOM : ok ? CBM_TEST_IMPACT_OK : CBM_TEST_IMPACT_INVALID;
te_free(&c);
cbm_kind_in_set_free_cache(); /* this thread's extraction cache, as in te_job_main */
return NULL;
}
+21 -2
View File
@@ -60,6 +60,7 @@ struct cbm_git_facts {
cbm_git_facts_error_t stopped;
gf_batch_budget_t *batch_budget; /* borrowed stack state during one serialized call */
gf_inventory_cache_t inventory_cache[2];
bool no_lazy_fetch; /* the executable takes --no-lazy-fetch (gf_probe_no_lazy_fetch) */
};
typedef struct {
@@ -486,7 +487,6 @@ static bool gf_run_input(cbm_git_facts_t *facts, const char *const *arguments, i
"--no-replace-objects",
"--no-pager",
"--literal-pathspecs",
"--no-lazy-fetch",
"-C",
facts->options.root,
"-c",
@@ -496,11 +496,13 @@ static bool gf_run_input(cbm_git_facts_t *facts, const char *const *arguments, i
size_t prefix_count = sizeof(prefix) / sizeof(prefix[0]);
size_t directory_count = facts->command_git_dir ? 4 : 0;
if (count < 1 || count >= GF_ARG_CAP ||
(size_t)count + prefix_count + directory_count + (size_t)facts->git_pin_count + 1 >
(size_t)count + prefix_count + directory_count + (size_t)facts->git_pin_count + 2 >
GF_ARG_CAP)
return gf_error(error, CBM_GIT_FACTS_INVALID, "Invalid internal Git command");
for (size_t i = 0; i < prefix_count; i++)
argv[argc++] = prefix[i];
if (facts->no_lazy_fetch)
argv[argc++] = "--no-lazy-fetch";
if (facts->command_git_dir) {
argv[argc++] = "--git-dir";
argv[argc++] = facts->command_git_dir;
@@ -907,6 +909,22 @@ static bool gf_open_identity(cbm_git_facts_t *facts, cbm_git_facts_error_t *erro
return gf_gate(facts, error);
}
/* --no-lazy-fetch (git 2.44) keeps an object missing from a partial clone
* from being fetched. Older git rejects the option, which failed every engine
* command (Ubuntu 24.04 ships git 2.43). There `-c protocol.allow=never`
* already turns such a fetch into an error, which is what the option asks
* for, so it is passed only when the executable accepts it: one probe per
* facts session, outside the command budget, output discarded. */
static bool gf_probe_no_lazy_fetch(const char *git) {
const char *argv[] = {git, "--no-lazy-fetch", "--version", NULL};
cbm_proc_opts_t options = {.bin = git,
.argv = argv,
.cancel_grace_ms = CBM_SUBPROCESS_DEFAULT_CANCEL_GRACE_MS,
.strip_git_repo_env = true};
cbm_proc_result_t result = {0};
return cbm_subprocess_run(&options, &result) == 0 && result.outcome == CBM_PROC_CLEAN;
}
cbm_git_facts_t *cbm_git_facts_open(const cbm_git_facts_options_t *options,
cbm_git_facts_error_t *error) {
cbm_git_facts_error_t local;
@@ -959,6 +977,7 @@ cbm_git_facts_t *cbm_git_facts_open(const cbm_git_facts_options_t *options,
cbm_git_facts_free(facts);
return NULL;
}
facts->no_lazy_fetch = gf_probe_no_lazy_fetch(facts->options.git_executable);
if (!gf_open_identity(facts, error)) {
cbm_git_facts_free(facts);
return NULL;
+1 -1
View File
@@ -27,7 +27,7 @@ static bool tir_label(tir_context *c, const char *s, tir_bytes *out) {
static bool tir_string_array(tir_context *c, const char *const *strings, int count,
tir_bytes **saved) {
size_t n;
size_t n = 0;
if (!tir_count(c, strings, count, &n))
return false;
tir_bytes *copy = saved ? tir_alloc(c, n, sizeof(*copy)) : NULL;
-1
View File
@@ -258,7 +258,6 @@ static cbm_pipeline_frozen_inputs_t pf_inputs(pf_fixture_t *f, const char *confi
.config_bytes = config,
.config_len = config ? strlen(config) : 0,
.pinned_git = git,
.resource_policy = {0},
.cancelled = cancelled};
}
static cbm_pipeline_frozen_status_t pf_create(pf_fixture_t *f, const char *config,
+5 -2
View File
@@ -420,9 +420,12 @@ TEST(store_graph_digest_invalid_arguments_missing_project_and_initial_latches) {
int opened=s && collation?cbm_store_read_scope_open(s,NULL,NULL,&scope):CBM_STORE_ERR;
cbm_store_graph_digest_limits_t lim=gd_room;cbm_store_graph_digest_t out;memset(&out,0xa5,sizeof(out));
const unsigned char *p=(const unsigned char *)"p";size_t len=1;
if(which==2)p=NULL;if(which==3)len=0;if(which==4)len=(size_t)INT_MAX+1;
if(which==2)p=NULL;
if(which==3)len=0;
if(which==4)len=(size_t)INT_MAX+1;
if(which==5){p=(const unsigned char *)"p\0q";len=3;}
if(which==7)lim.max_rows=0;if(which==8)lim.max_framed_bytes=0;
if(which==7)lim.max_rows=0;
if(which==8)lim.max_framed_bytes=0;
if(which==9)lim.max_framed_bytes=UINT64_MAX/8+1;
if(which==10)p=(const unsigned char *)"P";
int rc=opened==CBM_STORE_OK?cbm_store_graph_digest(which==0?NULL:scope,p,len,which==6?NULL:&lim,which==1?NULL:&out):-1;
+8 -1
View File
@@ -16,11 +16,18 @@
#include "foundation/subprocess.h"
#include "mcp/test_impact_engine.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* glibc's _FORTIFY_SOURCE realpath() aborts unless the output buffer is at
* least PATH_MAX bytes, independent of the actual path length. */
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
typedef struct {
char git[1024];
char home[1024];
@@ -126,7 +133,7 @@ static bool tie_open_with(tie_fixture_t *fx, const char *extra_path, const char
memset(fx, 0, sizeof(*fx));
const char *git = cbm_find_cli("git", cbm_get_home_dir());
const char *home = th_mktempdir("cbm-ti-engine");
char real[1024];
char real[PATH_MAX];
if (!git || !home || !realpath(home, real) || strlen(git) >= sizeof(fx->git)) {
return false;
}
+5 -2
View File
@@ -2002,7 +2002,9 @@ TEST(test_git_blob_batch_validates_full_selection_and_empty_requests) {
gfb_error(facts,&request,NULL,CBM_GIT_FACTS_INVALID) && ok;
for(int i=0;control && i<3;i++) {
cbm_git_blob_batch_limits_t zero=limits;
if(i==0)zero.max_entries=0;if(i==1)zero.max_input_bytes=0;if(i==2)zero.max_arena_bytes=0;
if(i==0)zero.max_entries=0;
if(i==1)zero.max_input_bytes=0;
if(i==2)zero.max_arena_bytes=0;
ok=gfb_error(facts,&request,&zero,CBM_GIT_FACTS_INVALID) && ok;
}
bool no_out=control && !cbm_git_facts_read_blob_batch(facts,&request,&limits,NULL,&error) &&
@@ -2265,7 +2267,8 @@ static cbm_git_bytes_t gfb_concat(CBMArena *arena,cbm_git_bytes_t a,cbm_git_byte
if(a.length>SIZE_MAX-b.length)return (cbm_git_bytes_t){NULL,0};
size_t size=a.length+b.length;unsigned char *bytes=cbm_arena_alloc(arena,size?size:1);
if(!bytes)return (cbm_git_bytes_t){NULL,0};
if(a.length)memcpy(bytes,a.data,a.length);if(b.length)memcpy(bytes+a.length,b.data,b.length);
if(a.length)memcpy(bytes,a.data,a.length);
if(b.length)memcpy(bytes+a.length,b.data,b.length);
return (cbm_git_bytes_t){bytes,size};
}