Files
pretranscode/tests/test_core.c
T

160 lines
7.5 KiB
C

#define PRETRANSCODE_TEST
#include "../pretranscode.c"
#include <assert.h>
static void write_file(const char *path, const char *value)
{
char *parent = xstrdup(path), *slash = strrchr(parent, '/');
assert(slash); *slash = 0; assert(mkdirs(parent) == 0); free(parent);
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
assert(fd >= 0); assert(write(fd, value, strlen(value)) == (ssize_t)strlen(value));
assert(close(fd) == 0);
}
static void remove_tree(const char *root)
{
char *roots[] = {(char *)root, NULL};
FTS *tree = fts_open(roots, FTS_PHYSICAL | FTS_NOCHDIR, NULL); assert(tree);
FTSENT *entry;
while ((entry = fts_read(tree))) {
if (entry->fts_info == FTS_DP) assert(rmdir(entry->fts_path) == 0);
else if (entry->fts_info != FTS_D) assert(unlink(entry->fts_path) == 0);
}
fts_close(tree);
}
static void test_hashes(void)
{
char a[33], b[33];
path_hash("tv/Test/one.mkv", a); path_hash("tv/Test/one.mkv", b);
assert(strcmp(a, b) == 0); path_hash("tv/Test/two.mkv", b); assert(strcmp(a, b));
struct stat st = {.st_dev = 1, .st_ino = 2, .st_size = 3};
st.st_mtim.tv_sec = 4; st.st_mtim.tv_nsec = 5;
generation_hash(&st, a); st.st_ino++; generation_hash(&st, b); assert(strcmp(a, b));
char path[] = "/tmp/pretranscode-hash-XXXXXX";
int fd = mkstemp(path); assert(fd >= 0);
const char value[] = "immutable output";
assert(write(fd, value, sizeof(value) - 1) == (ssize_t)(sizeof(value) - 1)); close(fd);
assert(file_hash(path, a) == 0); hash_bytes(value, sizeof(value) - 1, b);
assert(strcmp(a, b) == 0); assert(unlink(path) == 0);
}
static void test_atomic_link_keeps_open_inode(void)
{
char root[] = "/tmp/pretranscode-link-XXXXXX"; assert(mkdtemp(root));
char *view = xprintf("%s/view/file.mkv", root);
char *original = xprintf("%s/original", root), *cached = xprintf("%s/cached", root);
write_file(original, "original"); write_file(cached, "cached");
assert(atomic_link(view, original) == 1);
int old = open(view, O_RDONLY | O_CLOEXEC); assert(old >= 0);
assert(atomic_link(view, cached) == 1);
char data[16] = {0}; assert(read(old, data, sizeof(data)) == 8); assert(!strcmp(data, "original")); close(old);
memset(data, 0, sizeof(data)); int current = open(view, O_RDONLY | O_CLOEXEC); assert(current >= 0);
assert(read(current, data, sizeof(data)) == 6); assert(!strcmp(data, "cached")); close(current);
free(view); free(original); free(cached); remove_tree(root);
}
static void test_cache_name(void)
{
const char *hash = "00112233445566778899aabbccddeeff";
char *name = xprintf("%s.%s.%s.%s.mkv", hash, hash, hash, hash);
struct cache cache = {0}; assert(parse_cache_name(name, &cache)); cache.named = true;
assert(!strcmp(cache.path_hash, hash)); assert(!strcmp(cache.extension, ".mkv"));
struct item item = {.extension = ".mkv"};
memcpy(item.path_hash, hash, HASH_HEX + 1); memcpy(item.generation_hash, hash, HASH_HEX + 1);
assert(cache_matches(&cache, &item));
name[2] = 'X'; assert(!parse_cache_name(name, &cache)); free(name);
}
static void test_lru_does_not_change_media_mtime(void)
{
char root[] = "/tmp/pretranscode-atime-XXXXXX"; assert(mkdtemp(root));
char *view_root = xprintf("%s/view", root), *media = xprintf("%s/media.mkv", root);
char *view = xprintf("%s/movie.mkv", view_root); write_file(media, "cached");
assert(atomic_link(view, "/cache/files/media.mkv") == 1);
struct timespec old[2] = {{.tv_sec = 100}, {.tv_sec = 100}};
assert(utimensat(AT_FDCWD, media, old, 0) == 0);
struct manager manager = {.cfg.view_root = view_root};
struct cache cache = {.path = media, .target = "/cache/files/media.mkv"};
manager.caches.v = &cache; manager.caches.n = 1;
touch_target(&manager, "movie.mkv");
struct stat st; assert(stat(media, &st) == 0);
assert(st.st_mtim.tv_sec == 100); assert(st.st_atim.tv_sec > 100);
manager.caches.v = NULL; manager.caches.n = 0;
free(view_root); free(media); free(view); remove_tree(root);
}
static void test_old_profile_is_replacement_candidate(void)
{
const char *hash = "00112233445566778899aabbccddeeff";
struct cache old = {.named = true, .target = "/cache/files/old.mkv"};
memcpy(old.path_hash, hash, HASH_HEX + 1); memcpy(old.generation_hash, hash, HASH_HEX + 1);
memcpy(old.extension, ".mkv", 5); memset(old.profile_hash, '1', HASH_HEX); old.profile_hash[HASH_HEX] = 0;
struct item item = {.relative = "movie.mkv", .extension = ".mkv", .fallback = &old, .mtime = 1};
memcpy(item.path_hash, hash, HASH_HEX + 1); memcpy(item.generation_hash, hash, HASH_HEX + 1);
struct manager manager = {0}; manager.items.v = &item; manager.items.n = 1;
memset(manager.cfg.profile_hash, '2', HASH_HEX); manager.cfg.profile_hash[HASH_HEX] = 0;
assert(valid_cache(&manager, &item) == NULL);
assert(candidate(&manager) == &item);
}
static void test_single_writer_lock(void)
{
char root[] = "/tmp/pretranscode-lock-XXXXXX";
assert(mkdtemp(root));
int first = open(root, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
int second = open(root, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
assert(first >= 0 && second >= 0);
assert(flock(first, LOCK_EX | LOCK_NB) == 0);
assert(flock(second, LOCK_EX | LOCK_NB) < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));
close(second); close(first); assert(rmdir(root) == 0);
}
static void test_scheduling(void)
{
int pipes[2]; assert(pipe(pipes) == 0); pid_t pid = fork(); assert(pid >= 0);
if (!pid) {
close(pipes[0]); background_or_exit();
int values[2] = {sched_getscheduler(0),
IOPRIO_PRIO_CLASS((int)syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, 0))};
if (write(pipes[1], values, sizeof(values)) != (ssize_t)sizeof(values))
_exit(1);
_exit(0);
}
close(pipes[1]); int values[2]; assert(read(pipes[0], values, sizeof(values)) == (ssize_t)sizeof(values));
close(pipes[0]); assert(waitpid(pid, NULL, 0) == pid);
assert(values[0] == SCHED_IDLE); assert(values[1] == IOPRIO_CLASS_IDLE);
}
static void test_output_validation(void)
{
struct media source = {.codec = "hevc", .depth = 10, .width = 1920, .height = 1080,
.audio = 2, .subtitles = 3, .attachments = 1, .chapters = 12, .duration = 1200};
struct media output = {.codec = "h264", .depth = 8, .width = 1920, .height = 1080,
.audio = 1, .audio_compatible = true, .duration = 1200};
assert(valid_output(&source, &output));
output.dolby_vision = true; assert(!valid_output(&source, &output)); output.dolby_vision = false;
output.subtitles = 1; assert(!valid_output(&source, &output)); output.subtitles = 0;
output.audio_compatible = false; assert(!valid_output(&source, &output));
}
static void test_canonical_policy(void)
{
struct media media = {.codec = "h264", .depth = 8, .audio = 1, .audio_compatible = true};
assert(!incompatible(&media));
snprintf(media.codec, sizeof(media.codec), "av1"); assert(incompatible(&media));
snprintf(media.codec, sizeof(media.codec), "h264"); media.audio_compatible = false; assert(incompatible(&media));
media.audio_compatible = true; media.subtitles = 1; assert(incompatible(&media));
}
int main(void)
{
test_hashes(); test_atomic_link_keeps_open_inode(); test_cache_name();
test_lru_does_not_change_media_mtime(); test_old_profile_is_replacement_candidate();
test_single_writer_lock(); test_scheduling();
test_output_validation(); test_canonical_policy();
puts("all core tests passed"); return 0;
}