test_ingest: portable temp paths for Windows CI
The test hardcoded /tmp, which does not exist on the Windows runner. With NDEBUG compiling the asserts out, the NULL stream from the failed fopen reached fclose() and tripped the UCRT invalid-parameter fail-fast (0xc0000409). Temp files now go to %TEMP% on Windows; rm -rf and unlink are replaced with ISO C remove(); file-handle acquisition failures now exit loudly instead of relying on assert.
This commit is contained in:
@@ -18,11 +18,41 @@ static int tests_run = 0, tests_passed = 0;
|
||||
|
||||
static char tmp_archive[256];
|
||||
|
||||
static void rmrf(const char *path)
|
||||
/* Temp-file base: %TEMP% on Windows, /tmp elsewhere. */
|
||||
static const char *tmpdir(void)
|
||||
{
|
||||
char cmd[768];
|
||||
snprintf(cmd, sizeof cmd, "rm -rf '%s' '%s.blocks'", path, path);
|
||||
system(cmd);
|
||||
#ifdef _WIN32
|
||||
const char *t = getenv("TEMP");
|
||||
if (!t) t = getenv("TMP");
|
||||
return t ? t : ".";
|
||||
#else
|
||||
return "/tmp";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Remove the archive and its derived files. v2 archives never create
|
||||
* the .blocks sidecar, so plain remove() covers everything. */
|
||||
static void cleanup(const char *path)
|
||||
{
|
||||
char buf[320];
|
||||
remove(path);
|
||||
snprintf(buf, sizeof buf, "%s.out", path);
|
||||
remove(buf);
|
||||
snprintf(buf, sizeof buf, "%s.blocks", path);
|
||||
remove(buf);
|
||||
}
|
||||
|
||||
/* fopen that fails the test loudly: assert() is compiled out in
|
||||
* Release builds, and continuing with a NULL stream trips the MSVC
|
||||
* CRT invalid-parameter fail-fast instead of a test failure. */
|
||||
static FILE *xfopen(const char *path, const char *mode)
|
||||
{
|
||||
FILE *f = fopen(path, mode);
|
||||
if (!f) {
|
||||
fprintf(stderr, "FATAL: cannot open %s (mode %s)\n", path, mode);
|
||||
exit(1);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static void fill_random(uint8_t *buf, size_t len, uint32_t seed)
|
||||
@@ -37,7 +67,10 @@ static void fill_random(uint8_t *buf, size_t len, uint32_t seed)
|
||||
static uint8_t *slurp(const char *path, size_t *out_len)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
if (!f) {
|
||||
fprintf(stderr, "FATAL: cannot slurp %s\n", path);
|
||||
exit(1);
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
@@ -50,7 +83,7 @@ static uint8_t *slurp(const char *path, size_t *out_len)
|
||||
|
||||
static void test_roundtrip_small(void)
|
||||
{
|
||||
rmrf(tmp_archive);
|
||||
cleanup(tmp_archive);
|
||||
const char *msg = "hello world";
|
||||
struct uc2_ingest_stats st;
|
||||
int rc = uc2_ingest_write(tmp_archive,
|
||||
@@ -64,8 +97,7 @@ static void test_roundtrip_small(void)
|
||||
|
||||
char restored[320];
|
||||
snprintf(restored, sizeof restored, "%s.out", tmp_archive);
|
||||
FILE *out = fopen(restored, "wb");
|
||||
assert(out);
|
||||
FILE *out = xfopen(restored, "wb");
|
||||
rc = uc2_ingest_restore(tmp_archive, out);
|
||||
fclose(out);
|
||||
assert(rc == 0);
|
||||
@@ -75,13 +107,13 @@ static void test_roundtrip_small(void)
|
||||
assert(got_len == strlen(msg));
|
||||
assert(memcmp(got, msg, got_len) == 0);
|
||||
free(got);
|
||||
unlink(restored);
|
||||
rmrf(tmp_archive);
|
||||
remove(restored);
|
||||
cleanup(tmp_archive);
|
||||
}
|
||||
|
||||
static void test_roundtrip_multichunk(void)
|
||||
{
|
||||
rmrf(tmp_archive);
|
||||
cleanup(tmp_archive);
|
||||
const size_t N = 200000;
|
||||
uint8_t *data = malloc(N);
|
||||
fill_random(data, N, 0x12345678);
|
||||
@@ -95,8 +127,7 @@ static void test_roundtrip_multichunk(void)
|
||||
|
||||
char restored[320];
|
||||
snprintf(restored, sizeof restored, "%s.out", tmp_archive);
|
||||
FILE *out = fopen(restored, "wb");
|
||||
assert(out);
|
||||
FILE *out = xfopen(restored, "wb");
|
||||
rc = uc2_ingest_restore(tmp_archive, out);
|
||||
fclose(out);
|
||||
assert(rc == 0);
|
||||
@@ -108,13 +139,13 @@ static void test_roundtrip_multichunk(void)
|
||||
|
||||
free(got);
|
||||
free(data);
|
||||
unlink(restored);
|
||||
rmrf(tmp_archive);
|
||||
remove(restored);
|
||||
cleanup(tmp_archive);
|
||||
}
|
||||
|
||||
static void test_intra_call_dedup(void)
|
||||
{
|
||||
rmrf(tmp_archive);
|
||||
cleanup(tmp_archive);
|
||||
/* Concatenate the same random buffer twice -- CDC produces the
|
||||
* same chunk hashes for both halves, so half the chunks should
|
||||
* dedup within a single ingest call. */
|
||||
@@ -137,8 +168,7 @@ static void test_intra_call_dedup(void)
|
||||
* structurally transparent. */
|
||||
char restored[320];
|
||||
snprintf(restored, sizeof restored, "%s.out", tmp_archive);
|
||||
FILE *out = fopen(restored, "wb");
|
||||
assert(out);
|
||||
FILE *out = xfopen(restored, "wb");
|
||||
rc = uc2_ingest_restore(tmp_archive, out);
|
||||
fclose(out);
|
||||
assert(rc == 0);
|
||||
@@ -150,13 +180,13 @@ static void test_intra_call_dedup(void)
|
||||
|
||||
free(got);
|
||||
free(data);
|
||||
unlink(restored);
|
||||
rmrf(tmp_archive);
|
||||
remove(restored);
|
||||
cleanup(tmp_archive);
|
||||
}
|
||||
|
||||
static void test_v2_self_contained(void)
|
||||
{
|
||||
rmrf(tmp_archive);
|
||||
cleanup(tmp_archive);
|
||||
/* A v2 archive must restore correctly even if the legacy sidecar
|
||||
* blockstore directory is absent. The chunk pool lives inside
|
||||
* the archive file itself. */
|
||||
@@ -179,8 +209,7 @@ static void test_v2_self_contained(void)
|
||||
|
||||
char restored[320];
|
||||
snprintf(restored, sizeof restored, "%s.out", tmp_archive);
|
||||
FILE *out = fopen(restored, "wb");
|
||||
assert(out);
|
||||
FILE *out = xfopen(restored, "wb");
|
||||
rc = uc2_ingest_restore(tmp_archive, out);
|
||||
fclose(out);
|
||||
assert(rc == 0);
|
||||
@@ -192,13 +221,13 @@ static void test_v2_self_contained(void)
|
||||
|
||||
free(got);
|
||||
free(data);
|
||||
unlink(restored);
|
||||
rmrf(tmp_archive);
|
||||
remove(restored);
|
||||
cleanup(tmp_archive);
|
||||
}
|
||||
|
||||
static void test_empty_stream(void)
|
||||
{
|
||||
rmrf(tmp_archive);
|
||||
cleanup(tmp_archive);
|
||||
struct uc2_ingest_stats st;
|
||||
int rc = uc2_ingest_write(tmp_archive, NULL, 0, 0, &st);
|
||||
assert(rc == 0);
|
||||
@@ -208,8 +237,7 @@ static void test_empty_stream(void)
|
||||
|
||||
char restored[320];
|
||||
snprintf(restored, sizeof restored, "%s.out", tmp_archive);
|
||||
FILE *out = fopen(restored, "wb");
|
||||
assert(out);
|
||||
FILE *out = xfopen(restored, "wb");
|
||||
rc = uc2_ingest_restore(tmp_archive, out);
|
||||
fclose(out);
|
||||
assert(rc == 0);
|
||||
@@ -218,35 +246,35 @@ static void test_empty_stream(void)
|
||||
uint8_t *got = slurp(restored, &got_len);
|
||||
assert(got_len == 0);
|
||||
free(got);
|
||||
unlink(restored);
|
||||
rmrf(tmp_archive);
|
||||
remove(restored);
|
||||
cleanup(tmp_archive);
|
||||
}
|
||||
|
||||
static void test_bad_magic_rejected(void)
|
||||
{
|
||||
rmrf(tmp_archive);
|
||||
FILE *f = fopen(tmp_archive, "wb");
|
||||
assert(f);
|
||||
cleanup(tmp_archive);
|
||||
FILE *f = xfopen(tmp_archive, "wb");
|
||||
const char garbage[16] = "not-a-uc2-ingest";
|
||||
fwrite(garbage, 1, sizeof garbage, f);
|
||||
fclose(f);
|
||||
|
||||
FILE *out = fopen("/dev/null", "wb");
|
||||
#ifdef _MSC_VER
|
||||
if (!out) out = fopen("NUL", "wb");
|
||||
#endif
|
||||
assert(out);
|
||||
if (!out) {
|
||||
fprintf(stderr, "FATAL: no null device\n");
|
||||
exit(1);
|
||||
}
|
||||
int rc = uc2_ingest_restore(tmp_archive, out);
|
||||
fclose(out);
|
||||
assert(rc != 0);
|
||||
(void)rc;
|
||||
rmrf(tmp_archive);
|
||||
cleanup(tmp_archive);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
snprintf(tmp_archive, sizeof tmp_archive,
|
||||
"/tmp/uc2_ingest_test_%d.uc2", (int)getpid());
|
||||
"%s/uc2_ingest_test_%d.uc2", tmpdir(), (int)getpid());
|
||||
|
||||
printf("Running uc2_ingest tests...\n");
|
||||
TEST(test_roundtrip_small);
|
||||
|
||||
Reference in New Issue
Block a user