Files
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

168 lines
5.5 KiB
C

/*
* repro_harness.h — Shared helpers for cross-file / store-level / crash bug
* reproductions (TIER A multi-file, TIER B crashes).
*
* Ported faithfully from the proven static harness in tests/test_lang_contract.c
* so cross-file repro files don't each re-derive it. Header-only (static inline)
* — each TU gets its own copy; no link conflicts. Include AFTER test_framework.h.
*
* Single-file extraction bugs do NOT need this — use cbm_extract_file directly
* (see repro_extraction.c). Use this when the bug only appears once a fixture is
* indexed through the full production pipeline (CALLS/IMPORTS/HTTP_CALLS edges,
* cross-file/cross-package resolution, Route minting, dedup/upsert, etc.).
*/
#ifndef REPRO_HARNESS_H
#define REPRO_HARNESS_H
#include <foundation/compat.h>
#include "test_helpers.h" /* th_rmtree */
#include "cbm.h"
#include <mcp/mcp.h>
#include <store/store.h>
#include <pipeline/pipeline.h> /* cbm_project_name_from_path */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#if !defined(_WIN32)
#include <sys/wait.h> /* fork/waitpid crash isolation — POSIX only */
#endif
typedef struct {
char tmpdir[256];
char dbpath[512];
char *project;
cbm_mcp_server_t *srv;
} RProj;
typedef struct {
const char *name; /* relative filename, may include '/' for subdirs */
const char *content;
} RFile;
static inline void rh_to_fwd_slashes(char *p) {
for (; *p; p++) {
if (*p == '\\')
*p = '/';
}
}
/* Index lp->tmpdir (already populated) via the production index_repository flow
* and open the resulting graph DB (NULL on failure). */
static inline cbm_store_t *rh_open_indexed(RProj *lp) {
lp->project = cbm_project_name_from_path(lp->tmpdir);
if (!lp->project)
return NULL;
const char *home = getenv("HOME");
if (!home)
home = "/tmp";
char cache_dir[512];
snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/codebase-memory-mcp", home);
cbm_mkdir(cache_dir);
snprintf(lp->dbpath, sizeof(lp->dbpath), "%s/%s.db", cache_dir, lp->project);
unlink(lp->dbpath);
lp->srv = cbm_mcp_server_new(NULL);
if (!lp->srv)
return NULL;
char args[700];
snprintf(args, sizeof(args), "{\"repo_path\":\"%s\"}", lp->tmpdir);
char *resp = cbm_mcp_handle_tool(lp->srv, "index_repository", args);
if (resp)
free(resp);
return cbm_store_open_path(lp->dbpath);
}
/* Write each fixture file into a fresh temp project, index it via the MCP
* production flow, and open the resulting graph DB. Returns store (NULL on fail). */
static inline cbm_store_t *rh_index_files(RProj *lp, const RFile *files, int nfiles) {
memset(lp, 0, sizeof(*lp));
snprintf(lp->tmpdir, sizeof(lp->tmpdir), "/tmp/cbm_repro_XXXXXX");
if (!cbm_mkdtemp(lp->tmpdir))
return NULL;
rh_to_fwd_slashes(lp->tmpdir);
for (int i = 0; i < nfiles; i++) {
char path[700];
snprintf(path, sizeof(path), "%s/%s", lp->tmpdir, files[i].name);
char *slash = strrchr(path, '/');
if (slash && slash > path + strlen(lp->tmpdir)) {
*slash = '\0';
cbm_mkdir_p(path, 0755);
*slash = '/';
}
FILE *f = fopen(path, "wb"); /* binary: keep "\n" exact */
if (!f)
return NULL;
fputs(files[i].content, f);
fclose(f);
}
return rh_open_indexed(lp);
}
static inline cbm_store_t *rh_index(RProj *lp, const char *filename, const char *content) {
RFile f = {filename, content};
return rh_index_files(lp, &f, 1);
}
static inline void rh_cleanup(RProj *lp, cbm_store_t *store) {
if (store)
cbm_store_close(store);
if (lp->srv) {
cbm_mcp_server_free(lp->srv);
lp->srv = NULL;
}
free(lp->project);
lp->project = NULL;
th_rmtree(lp->tmpdir);
unlink(lp->dbpath);
char wal[600], shm[600];
snprintf(wal, sizeof(wal), "%s-wal", lp->dbpath);
unlink(wal);
snprintf(shm, sizeof(shm), "%s-shm", lp->dbpath);
unlink(shm);
}
/* Count edges of a given type in the project graph. Returns -1 on query error. */
static inline int rh_count_edges(cbm_store_t *store, const char *project, const char *edge) {
return store ? cbm_store_count_edges_by_type(store, project, edge) : -1;
}
/* Count nodes carrying `label`. Returns -1 on query error. */
static inline int rh_count_label(cbm_store_t *store, const char *project, const char *label) {
cbm_node_t *nodes = NULL;
int count = 0;
if (cbm_store_find_nodes_by_label(store, project, label, &nodes, &count) != CBM_STORE_OK)
return -1;
cbm_store_free_nodes(nodes, count);
return count;
}
/* TIER B: returns true if cbm_extract_file CRASHES (signal) on `content`.
* Runs in a forked child so the crash doesn't take down the repro runner. */
static inline bool rh_extract_crashes(const char *content, CBMLanguage lang, const char *relpath) {
#if defined(_WIN32)
CBMFileResult *r =
cbm_extract_file(content, (int)strlen(content), lang, "repro", relpath, 0, NULL, NULL);
if (r)
cbm_free_result(r);
return false;
#else
fflush(NULL);
pid_t pid = fork();
if (pid < 0)
return false;
if (pid == 0) {
CBMFileResult *r =
cbm_extract_file(content, (int)strlen(content), lang, "repro", relpath, 0, NULL, NULL);
if (r)
cbm_free_result(r);
_exit(0);
}
int status = 0;
(void)waitpid(pid, &status, 0);
return WIFSIGNALED(status);
#endif
}
#endif /* REPRO_HARNESS_H */