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

153 lines
5.6 KiB
C
Raw Normal View History

#define _POSIX_C_SOURCE 200809L
#include "xtc/xtc_audio.h"
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static void reap(pid_t child)
{
int status;
assert(kill(child, SIGKILL) == 0);
assert(waitpid(child, &status, 0) == child && WIFSIGNALED(status) &&
WTERMSIG(status) == SIGKILL);
}
static size_t read_bytes(const char *path, unsigned char *bytes, size_t size)
{
FILE *f = fopen(path, "rb");
size_t n;
assert(f);
n = fread(bytes, 1, size, f);
assert(!ferror(f) && fclose(f) == 0);
return n;
}
static int temporaries(const char *directory, int remove_files)
{
DIR *dir = opendir(directory);
struct dirent *entry;
int count = 0;
assert(dir);
while ((entry = readdir(dir)) != NULL)
if (strstr(entry->d_name, ".tmp.")) {
char path[512];
++count;
if (remove_files) {
assert(snprintf(path, sizeof(path), "%s/%s", directory, entry->d_name) > 0);
assert(unlink(path) == 0);
}
}
assert(closedir(dir) == 0);
return count;
}
int main(void)
{
char directory[] = "/tmp/xtc-recovery-XXXXXX", song[256], wave[256];
xtc_project *old = NULL, *next = NULL, *loaded = NULL;
uint64_t track, clip;
size_t i, n, before_size;
unsigned char before[4096], after[4096];
pid_t child;
int status, pipefd[2];
char ready;
struct timespec delay = {0, 100000};
assert(mkdtemp(directory));
snprintf(song, sizeof(song), "%s/song.xtc", directory);
snprintf(wave, sizeof(wave), "%s/take.wav", directory);
assert(xtc_project_create(&old) == XTC_OK && xtc_project_create(&next) == XTC_OK &&
xtc_project_create(&loaded) == XTC_OK);
assert(xtc_project_add_track(old, "Old", &track) == XTC_OK &&
xtc_project_save(old, song) == XTC_OK);
before_size = read_bytes(song, before, sizeof(before));
assert(xtc_project_add_track(next, "New", &track) == XTC_OK);
for (i = 0; i < XTC_PROJECT_MAX_CLIPS; ++i)
assert(xtc_project_add_clip(next, track, "Recovery region", i * 960, 960, &clip) == XTC_OK);
// Deterministic per-process file-size failure; no server/system setting change.
child = fork();
assert(child >= 0);
if (!child) {
struct rlimit limit = {128, 128};
signal(SIGXFSZ, SIG_IGN);
if (setrlimit(RLIMIT_FSIZE, &limit) != 0)
_exit(2);
_exit(xtc_project_save(next, song) == XTC_ERR_IO ? 0 : 3);
}
assert(waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0);
assert(read_bytes(song, after, sizeof(after)) == before_size &&
memcmp(before, after, before_size) == 0 && temporaries(directory, 0) == 0);
// Kill a writer while its temporary is visible. Destination must be complete old/new.
for (i = 0; i < 8; ++i) {
size_t attempts;
xtc_track_info info;
assert(xtc_project_save(old, song) == XTC_OK);
assert(pipe(pipefd) == 0);
child = fork();
assert(child >= 0);
if (!child) {
close(pipefd[0]);
if (write(pipefd[1], "R", 1) != 1)
_exit(4);
close(pipefd[1]);
for (;;)
if (xtc_project_save(next, song) != XTC_OK)
_exit(5);
}
close(pipefd[1]);
assert(read(pipefd[0], &ready, 1) == 1);
close(pipefd[0]);
for (attempts = 0; attempts < 10000 && !temporaries(directory, 0); ++attempts)
nanosleep(&delay, NULL);
reap(child);
assert(attempts < 10000);
assert(xtc_project_load(loaded, song) == XTC_OK &&
xtc_project_get_track(loaded, 0, &info) == XTC_OK);
assert(strcmp(info.name, "Old") == 0 || strcmp(info.name, "New") == 0);
temporaries(directory, 1);
}
// A killed recording leaves the previously finalized destination untouched.
{
xtc_recorder *recorder = NULL;
float samples[128];
for (i = 0; i < 128; ++i)
samples[i] = 0.25f;
assert(xtc_recorder_create_replace(wave, 48000, 256, 1, &recorder) == XTC_OK);
xtc_recorder_push(recorder, samples, 128);
assert(xtc_recorder_finish(recorder) == XTC_OK);
xtc_recorder_destroy(recorder);
before_size = read_bytes(wave, before, sizeof(before));
assert(pipe(pipefd) == 0);
child = fork();
assert(child >= 0);
if (!child) {
close(pipefd[0]);
if (xtc_recorder_create_replace(wave, 48000, 256, 1, &recorder) != XTC_OK)
_exit(6);
xtc_recorder_push(recorder, samples, 128);
if (xtc_recorder_drain(recorder) != XTC_OK)
_exit(7);
if (write(pipefd[1], "R", 1) != 1)
_exit(8);
for (;;)
pause();
}
close(pipefd[1]);
assert(read(pipefd[0], &ready, 1) == 1);
close(pipefd[0]);
reap(child);
n = read_bytes(wave, after, sizeof(after));
assert(n == before_size && memcmp(before, after, n) == 0);
assert(temporaries(directory, 1) > 0);
}
xtc_project_destroy(old);
xtc_project_destroy(next);
xtc_project_destroy(loaded);
assert(unlink(song) == 0 && unlink(wave) == 0 && rmdir(directory) == 0);
puts("Save write failure, 8 killed saves and killed recording preserve complete destinations; "
"orphan temporaries remain explicit.");
return 0;
}