lardon3d/tests/test_import.c

310 lines
9.5 KiB
C

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <lardon3d/app_state.h>
#include <lardon3d/import.h>
#define CHECK(condition) \
do { \
if (!(condition)) { \
(void)fprintf(stderr, "Échec ligne %d : %s\n", __LINE__, #condition); \
return false; \
} \
} while (0)
static bool
join_path(char destination[PATH_MAX], const char *parent, const char *child)
{
int written = snprintf(destination, PATH_MAX, "%s/%s", parent, child);
return written >= 0 && (size_t)written < PATH_MAX;
}
static bool
create_directory(const char *path)
{
return mkdir(path, 0755) == 0;
}
static bool
create_file(const char *path, const char *content)
{
int descriptor = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (descriptor < 0) {
return false;
}
size_t length = strlen(content);
size_t written_total = 0;
while (written_total < length) {
ssize_t written = write(
descriptor,
content + written_total,
length - written_total
);
if (written < 0 && errno == EINTR) {
continue;
}
if (written <= 0) {
(void)close(descriptor);
return false;
}
written_total += (size_t)written;
}
return close(descriptor) == 0;
}
static bool
read_file(const char *path, char *content, size_t capacity)
{
int descriptor = open(path, O_RDONLY);
if (descriptor < 0 || capacity == 0) {
return false;
}
size_t total = 0;
while (total + 1 < capacity) {
ssize_t count = read(descriptor, content + total, capacity - total - 1);
if (count == 0) {
break;
}
if (count < 0 && errno == EINTR) {
continue;
}
if (count < 0) {
(void)close(descriptor);
return false;
}
total += (size_t)count;
}
content[total] = '\0';
return close(descriptor) == 0;
}
static bool
file_equals(const char *path, const char *expected)
{
char content[256];
return read_file(path, content, sizeof(content))
&& strcmp(content, expected) == 0;
}
static bool
manifest_has_unique_filenames(const char *path, size_t expected_entries)
{
FILE *file = fopen(path, "r");
if (!file) {
return false;
}
char filenames[16][NAME_MAX + 1];
size_t count = 0;
char line[PATH_MAX + 512];
bool valid = fgets(line, sizeof(line), file)
&& strcmp(line, "filename\tsize_bytes\tsource_path\n") == 0;
while (valid && fgets(line, sizeof(line), file)) {
char *tab = strchr(line, '\t');
if (!tab || count >= 16) {
valid = false;
break;
}
*tab = '\0';
for (size_t index = 0; index < count; ++index) {
if (strcmp(filenames[index], line) == 0) {
valid = false;
}
}
int written = snprintf(
filenames[count],
sizeof(filenames[count]),
"%s",
line
);
if (written < 0 || (size_t)written >= sizeof(filenames[count])) {
valid = false;
}
++count;
}
if (ferror(file) || fclose(file) != 0) {
valid = false;
}
return valid && count == expected_entries;
}
static bool
has_manifest_temporary(const char *images_path)
{
DIR *directory = opendir(images_path);
if (!directory) {
return true;
}
bool found = false;
for (struct dirent *entry = readdir(directory);
entry;
entry = readdir(directory)) {
if (strncmp(entry->d_name, ".manifest.tsv.tmp.", 18) == 0) {
found = true;
}
}
if (closedir(directory) != 0) {
found = true;
}
return found;
}
static bool
run_test(void)
{
char base[] = "/tmp/lardon3d-import-test.XXXXXX";
CHECK(mkdtemp(base));
char project[PATH_MAX];
char images[PATH_MAX];
char originals[PATH_MAX];
char valid_source[PATH_MAX];
char tab_source[PATH_MAX];
char newline_source[PATH_MAX];
CHECK(join_path(project, base, "project"));
CHECK(join_path(images, project, "images"));
CHECK(join_path(originals, images, "originals"));
CHECK(join_path(valid_source, base, "valid"));
CHECK(join_path(tab_source, base, "tab"));
CHECK(join_path(newline_source, base, "newline"));
CHECK(create_directory(project));
CHECK(create_directory(images));
CHECK(create_directory(valid_source));
CHECK(create_directory(tab_source));
CHECK(create_directory(newline_source));
char path[PATH_MAX];
CHECK(join_path(path, valid_source, "one.jpg"));
CHECK(create_file(path, "one"));
CHECK(join_path(path, valid_source, "two.PNG"));
CHECK(create_file(path, "two-two"));
CHECK(create_directory(originals));
CHECK(join_path(path, originals, "one.jpg"));
CHECK(create_file(path, "one"));
Lardon3DAppState state;
lardon3d_app_state_init(&state);
state.project_loaded = true;
CHECK(snprintf(
state.project_path,
sizeof(state.project_path),
"%s",
project
) > 0);
Lardon3DImportResult result;
bool batch_complete = false;
CHECK(lardon3d_import_directory_batch(&state, valid_source, 8, &result,
NULL, &batch_complete) == LARDON3D_IMPORT_SUCCEEDED);
CHECK(batch_complete);
CHECK(result.admissible_found == 2);
CHECK(result.newly_manifested == 2);
CHECK(result.copied == 1);
CHECK(result.already_present == 1);
char manifest[PATH_MAX];
CHECK(join_path(manifest, images, "manifest.tsv"));
char manifest_before[8192];
CHECK(read_file(manifest, manifest_before, sizeof(manifest_before)));
CHECK(join_path(path, tab_source, "would-copy.jpg"));
CHECK(create_file(path, "must-not-copy"));
CHECK(join_path(path, tab_source, "bad\tname.jpg"));
CHECK(create_file(path, "invalid"));
CHECK(!lardon3d_import_directory(&state, tab_source, &result));
CHECK(join_path(path, originals, "would-copy.jpg"));
CHECK(access(path, F_OK) != 0);
char manifest_after[8192];
CHECK(read_file(manifest, manifest_after, sizeof(manifest_after)));
CHECK(strcmp(manifest_before, manifest_after) == 0);
CHECK(!has_manifest_temporary(images));
CHECK(join_path(path, newline_source, "also-not-copied.jpg"));
CHECK(create_file(path, "must-not-copy-either"));
CHECK(join_path(path, newline_source, "bad\nname.png"));
CHECK(create_file(path, "invalid"));
CHECK(!lardon3d_import_directory(&state, newline_source, &result));
CHECK(join_path(path, originals, "also-not-copied.jpg"));
CHECK(access(path, F_OK) != 0);
CHECK(read_file(manifest, manifest_after, sizeof(manifest_after)));
CHECK(strcmp(manifest_before, manifest_after) == 0);
CHECK(!has_manifest_temporary(images));
char collision_source[PATH_MAX], collision_destination[PATH_MAX];
CHECK(join_path(collision_source, valid_source, "collision.jpg"));
CHECK(join_path(collision_destination, originals, "collision.jpg"));
CHECK(create_file(collision_source, "new-source"));
CHECK(create_file(collision_destination, "different-existing-output"));
batch_complete = false;
CHECK(lardon3d_import_directory_batch(&state, valid_source, 8, &result,
NULL, &batch_complete) == LARDON3D_IMPORT_FAILED);
CHECK(read_file(manifest, manifest_after, sizeof(manifest_after)));
CHECK(strcmp(manifest_before, manifest_after) == 0);
CHECK(unlink(collision_source) == 0 && unlink(collision_destination) == 0);
CHECK(join_path(path, valid_source, "three.tiff"));
CHECK(create_file(path, "three-three-three"));
CHECK(lardon3d_import_directory(&state, valid_source, &result));
CHECK(result.copied == 1);
CHECK(result.already_present == 2);
CHECK(lardon3d_import_directory(&state, valid_source, &result));
CHECK(result.copied == 0);
CHECK(result.already_present == 3);
CHECK(manifest_has_unique_filenames(manifest, 3));
CHECK(join_path(path, originals, "one.jpg"));
CHECK(file_equals(path, "one"));
CHECK(join_path(path, originals, "two.PNG"));
CHECK(file_equals(path, "two-two"));
CHECK(join_path(path, originals, "three.tiff"));
CHECK(file_equals(path, "three-three-three"));
CHECK(!has_manifest_temporary(images));
CHECK(unlink(path) == 0);
CHECK(join_path(path, originals, "two.PNG"));
CHECK(unlink(path) == 0);
CHECK(join_path(path, originals, "one.jpg"));
CHECK(unlink(path) == 0);
CHECK(unlink(manifest) == 0);
CHECK(rmdir(originals) == 0);
CHECK(rmdir(images) == 0);
CHECK(rmdir(project) == 0);
CHECK(join_path(path, valid_source, "three.tiff"));
CHECK(unlink(path) == 0);
CHECK(join_path(path, valid_source, "two.PNG"));
CHECK(unlink(path) == 0);
CHECK(join_path(path, valid_source, "one.jpg"));
CHECK(unlink(path) == 0);
CHECK(rmdir(valid_source) == 0);
CHECK(join_path(path, tab_source, "bad\tname.jpg"));
CHECK(unlink(path) == 0);
CHECK(join_path(path, tab_source, "would-copy.jpg"));
CHECK(unlink(path) == 0);
CHECK(rmdir(tab_source) == 0);
CHECK(join_path(path, newline_source, "bad\nname.png"));
CHECK(unlink(path) == 0);
CHECK(join_path(path, newline_source, "also-not-copied.jpg"));
CHECK(unlink(path) == 0);
CHECK(rmdir(newline_source) == 0);
CHECK(rmdir(base) == 0);
return true;
}
int
main(void)
{
return run_test() ? EXIT_SUCCESS : EXIT_FAILURE;
}