1
0
Fork 0
forked from hanez/xtc
xtc/tests/integration/test_recording.c

108 lines
5.3 KiB
C

#define _POSIX_C_SOURCE 200809L
#include "xtc/xtc_audio.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
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.");
}