#define _POSIX_C_SOURCE 200809L #include "xtc/xtc_audio.h" #include #include #include #include #include #include #include #include #include static void file(const char *path, const char *text) { FILE *f = fopen(path, "wb"); assert(f); assert(fputs(text, f) >= 0 && fclose(f) == 0); } int main(void) { char dir[] = "/tmp/xtc-recording-XXXXXX", take[256], recovered[256], retained[4096], song[256]; xtc_recorder *r = NULL; float samples[4096]; struct stat info; uint64_t position; size_t i; assert(mkdtemp(dir)); snprintf(take, sizeof(take), "%s/take.wav", dir); snprintf(recovered, sizeof(recovered), "%s/recovered.wav", dir); snprintf(song, sizeof(song), "%s/song.xtc", dir); for (i = 0; i < 4096; ++i) samples[i] = 0.25f; file(take, "existing"); assert(xtc_recorder_create(take, 8000, 4096, &r) == XTC_ERR_IO && !r); assert(unlink(take) == 0 && symlink("missing", take) == 0); assert(xtc_recorder_create(take, 8000, 4096, &r) == XTC_ERR_IO && !r); assert(unlink(take) == 0); assert(xtc_recorder_create("/missing/xtc/take.wav", 8000, 4096, &r) == XTC_ERR_IO && !r); /* Destination appears after creation: no-clobber publication must preserve it. */ assert(xtc_recorder_create(take, 8000, 4096, &r) == XTC_OK); xtc_recorder_push(r, samples, 4096); file(take, "racing writer"); assert(xtc_recorder_finish(r) == XTC_ERR_IO); snprintf(retained, sizeof(retained), "%s", xtc_recorder_recovery_path(r)); xtc_recorder_destroy(r); assert(stat(take, &info) == 0 && info.st_size == 13); assert(xtc_recorder_recover(retained, take) == XTC_ERR_IO); assert(xtc_recorder_recover(retained, recovered) == XTC_OK); assert(stat(recovered, &info) == 0 && info.st_size == 44 + 4096 * 4); assert(unlink(retained) == 0 && unlink(recovered) == 0 && unlink(take) == 0); /* Last-resort destruction drains accepted samples after producer quiescence. */ assert(xtc_recorder_create(take, 8000, 4096, &r) == XTC_OK); xtc_recorder_push(r, samples, 4096); xtc_recorder_destroy(r); assert(stat(take, &info) == 0 && info.st_size == 44 + 4096 * 4); assert(unlink(take) == 0); /* Thirty minutes of virtual 8 kHz audio, repeated ring wrap/drain, no sleep. */ assert(xtc_recorder_create(take, 8000, 4096, &r) == XTC_OK); for (position = 0; position < 14400000;) { size_t count = 14400000 - position > 4096 ? 4096 : (size_t)(14400000 - position); xtc_recorder_push(r, samples, count); assert(xtc_recorder_drain(r) == XTC_OK); position += count; } assert(xtc_recorder_finish(r) == XTC_OK); xtc_recorder_destroy(r); assert(stat(take, &info) == 0 && info.st_size == 57600044); assert(unlink(take) == 0); /* Child-local disk-size failure preserves a recoverable on-disk prefix. */ { int pipes[2], status; pid_t child; assert(pipe(pipes) == 0); child = fork(); assert(child >= 0); if (!child) { struct rlimit limit = {128, 128}; close(pipes[0]); signal(SIGXFSZ, SIG_IGN); assert(setrlimit(RLIMIT_FSIZE, &limit) == 0); assert(xtc_recorder_create(take, 8000, 4096, &r) == XTC_OK); xtc_recorder_push(r, samples, 4096); (void)xtc_recorder_drain(r); assert(xtc_recorder_finish(r) == XTC_ERR_IO); snprintf(retained, sizeof(retained), "%s", xtc_recorder_recovery_path(r)); xtc_recorder_destroy(r); assert(write(pipes[1], retained, sizeof(retained)) == (ssize_t)sizeof(retained)); _exit(0); } close(pipes[1]); assert(read(pipes[0], retained, sizeof(retained)) == (ssize_t)sizeof(retained)); close(pipes[0]); assert(waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0); assert(access(take, F_OK) != 0 && stat(retained, &info) == 0 && info.st_size == 128); assert(xtc_recorder_recover(retained, recovered) == XTC_OK); assert(stat(recovered, &info) == 0 && info.st_size == 128); assert(unlink(retained) == 0 && unlink(recovered) == 0); } /* Incomplete and missing references survive save/reopen and are reported. */ { xtc_project *p = NULL; xtc_audio_plan *plan = NULL; uint64_t track, clip; const char *issue; assert(xtc_project_create(&p) == XTC_OK && xtc_project_add_track(p, "Incomplete", &track) == XTC_OK); assert(xtc_project_add_clip(p, track, "Broken take", 0, 960, &clip) == XTC_OK); assert(xtc_project_set_clip_asset(p, clip, take) == XTC_OK); file(take, "RIFF incomplete"); assert(xtc_project_save(p, song) == XTC_OK && xtc_project_load(p, song) == XTC_OK); assert(xtc_audio_plan_create(p, 8000, &plan) == XTC_OK && xtc_audio_plan_issues(plan, &issue) == 1); assert(!strcmp(issue, take)); xtc_audio_plan_destroy(plan); assert(unlink(take) == 0); assert(xtc_audio_plan_create(p, 8000, &plan) == XTC_OK && xtc_audio_plan_issues(plan, &issue) == 1); xtc_audio_plan_destroy(plan); xtc_project_destroy(p); assert(unlink(song) == 0); } assert(rmdir(dir) == 0); puts("30-minute virtual recording, no-clobber races, disk failure recovery and incomplete/missing media passed."); }