/* * test_helpers.h — Cross-platform test fixture helpers. * * Replaces system("rm -rf"), system("mkdir -p && echo >"), chmod(), * and hardcoded /tmp/ paths with portable C implementations. * Uses compat.h/compat_fs.h underneath — same abstraction layer * as production code. * * All functions are static inline to avoid linker issues when * included from multiple test files. */ #ifndef TEST_HELPERS_H #define TEST_HELPERS_H #include "../src/foundation/compat.h" #include "../src/foundation/compat_fs.h" #include #include #include #include /* ── Path building ────────────────────────────────────────────── */ /* Build a path from base + relative. Uses a static buffer — not reentrant. * Usage: th_write_file(TH_PATH(base, "src/main.go"), "content"); */ #define TH_PATH(base, rel) th_path_join(base, rel) static inline const char *th_path_join(const char *base, const char *rel) { static char buf[4][1024]; static int idx = 0; int i = idx; idx = (idx + 1) & 3; snprintf(buf[i], sizeof(buf[i]), "%s/%s", base, rel); return buf[i]; } /* ── File writing ─────────────────────────────────────────────── */ /* Write content to a file, creating parent directories as needed. */ static inline int th_write_file(const char *path, const char *content) { /* Create parent directories */ char dir[1024]; snprintf(dir, sizeof(dir), "%s", path); char *last_slash = strrchr(dir, '/'); #ifdef _WIN32 char *last_bslash = strrchr(dir, '\\'); if (last_bslash && (!last_slash || last_bslash > last_slash)) { last_slash = last_bslash; } #endif if (last_slash) { *last_slash = '\0'; cbm_mkdir_p(dir, 0755); } FILE *f = fopen(path, "w"); if (!f) { return -1; } if (content && content[0]) { fputs(content, f); } fclose(f); return 0; } /* Append content to a file. */ static inline int th_append_file(const char *path, const char *content) { FILE *f = fopen(path, "a"); if (!f) { return -1; } if (content && content[0]) { fputs(content, f); } fclose(f); return 0; } /* ── Directory creation ───────────────────────────────────────── */ /* Create a directory and all parents. Returns 0 on success. */ static inline int th_mkdir_p(const char *path) { return cbm_mkdir_p(path, 0755) ? 0 : -1; } /* ── Recursive directory removal ──────────────────────────────── */ /* Remove a file or directory tree recursively. Cross-platform rm -rf. */ static inline int th_rmtree(const char *path) { struct stat st; if (stat(path, &st) != 0) { return 0; /* doesn't exist — success */ } if (!S_ISDIR(st.st_mode)) { return cbm_unlink(path); } /* Directory — recurse into children */ cbm_dir_t *d = cbm_opendir(path); if (!d) { return -1; } cbm_dirent_t *entry; int rc = 0; while ((entry = cbm_readdir(d)) != NULL) { if (strcmp(entry->name, ".") == 0 || strcmp(entry->name, "..") == 0) { continue; } char child[1024]; snprintf(child, sizeof(child), "%s/%s", path, entry->name); if (entry->is_dir) { if (th_rmtree(child) != 0) { rc = -1; } } else { if (cbm_unlink(child) != 0) { rc = -1; } } } cbm_closedir(d); cbm_rmdir(path); return rc; } /* ── Temp directory creation ──────────────────────────────────── */ /* Create a temporary directory. Returns static buffer with path. * Pattern: prefix is used as part of the dirname. */ static inline char *th_mktempdir(const char *prefix) { static char buf[256]; #ifdef _WIN32 const char *tmp = getenv("TEMP"); if (!tmp) tmp = getenv("TMP"); if (!tmp) tmp = "C:\\Temp"; snprintf(buf, sizeof(buf), "%s\\%s_XXXXXX", tmp, prefix); #else snprintf(buf, sizeof(buf), "/tmp/%s_XXXXXX", prefix); #endif if (!cbm_mkdtemp(buf)) { return NULL; } return buf; } /* ── File permissions (no-op on Windows) ──────────────────────── */ /* Make a file executable. On Windows this is a no-op (all files are * "executable" if they have the right extension). */ static inline void th_make_executable(const char *path) { #ifndef _WIN32 chmod(path, 0755); #else (void)path; #endif } /* ── Cleanup helper ───────────────────────────────────────────── */ /* Remove a temp directory tree. Safe to call with NULL. */ static inline void th_cleanup(const char *path) { if (path && path[0]) { th_rmtree(path); } } #endif /* TEST_HELPERS_H */