302 lines
8.3 KiB
C
302 lines
8.3 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/image_catalog.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
|
|
write_all(int descriptor, const char *content, size_t length)
|
|
{
|
|
size_t total = 0;
|
|
while (total < length) {
|
|
ssize_t written = write(descriptor, content + total, length - total);
|
|
if (written < 0 && errno == EINTR) {
|
|
continue;
|
|
}
|
|
if (written <= 0) {
|
|
return false;
|
|
}
|
|
total += (size_t)written;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
bool success = write_all(descriptor, content, strlen(content));
|
|
if (close(descriptor) != 0) {
|
|
success = false;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static bool
|
|
replace_manifest(const char *path, const char *content)
|
|
{
|
|
if (unlink(path) != 0 && errno != ENOENT) {
|
|
return false;
|
|
}
|
|
return create_file(path, content);
|
|
}
|
|
|
|
static bool
|
|
remove_tree(const char *path)
|
|
{
|
|
struct stat info;
|
|
if (lstat(path, &info) != 0) {
|
|
return errno == ENOENT;
|
|
}
|
|
if (!S_ISDIR(info.st_mode)) {
|
|
return unlink(path) == 0;
|
|
}
|
|
DIR *directory = opendir(path);
|
|
if (!directory) {
|
|
return false;
|
|
}
|
|
bool success = true;
|
|
for (struct dirent *entry = readdir(directory);
|
|
entry;
|
|
entry = readdir(directory)) {
|
|
if (strcmp(entry->d_name, ".") == 0
|
|
|| strcmp(entry->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
char child[PATH_MAX];
|
|
if (!join_path(child, path, entry->d_name) || !remove_tree(child)) {
|
|
success = false;
|
|
}
|
|
}
|
|
if (closedir(directory) != 0 || rmdir(path) != 0) {
|
|
success = false;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static bool
|
|
expect_invalid(
|
|
const Lardon3DAppState *state,
|
|
const char *manifest,
|
|
const char *content
|
|
)
|
|
{
|
|
CHECK(replace_manifest(manifest, content));
|
|
char error[256];
|
|
Lardon3DImageCatalog *catalog = lardon3d_image_catalog_load(
|
|
state,
|
|
error,
|
|
sizeof(error)
|
|
);
|
|
CHECK(!catalog);
|
|
CHECK(error[0]);
|
|
lardon3d_image_catalog_destroy(catalog);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
test_sizes(void)
|
|
{
|
|
char text[64];
|
|
lardon3d_image_catalog_format_size(512, text, sizeof(text));
|
|
CHECK(strcmp(text, "512 octets") == 0);
|
|
lardon3d_image_catalog_format_size(1536, text, sizeof(text));
|
|
CHECK(strcmp(text, "1,5 Kio") == 0);
|
|
lardon3d_image_catalog_format_size(2 * 1024 * 1024, text, sizeof(text));
|
|
CHECK(strcmp(text, "2,0 Mio") == 0);
|
|
lardon3d_image_catalog_format_size(
|
|
UINT64_C(3) * 1024 * 1024 * 1024,
|
|
text,
|
|
sizeof(text)
|
|
);
|
|
CHECK(strcmp(text, "3,0 Gio") == 0);
|
|
lardon3d_image_catalog_format_size(0, NULL, 0);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
run_test(void)
|
|
{
|
|
char base[] = "/tmp/lardon3d-catalog-test.XXXXXX";
|
|
CHECK(mkdtemp(base));
|
|
char project[PATH_MAX];
|
|
char images[PATH_MAX];
|
|
char originals[PATH_MAX];
|
|
char manifest[PATH_MAX];
|
|
CHECK(join_path(project, base, "project"));
|
|
CHECK(join_path(images, project, "images"));
|
|
CHECK(join_path(originals, images, "originals"));
|
|
CHECK(join_path(manifest, images, "manifest.tsv"));
|
|
CHECK(mkdir(project, 0755) == 0);
|
|
CHECK(mkdir(images, 0755) == 0);
|
|
CHECK(mkdir(originals, 0755) == 0);
|
|
|
|
Lardon3DAppState state;
|
|
lardon3d_app_state_init(&state);
|
|
state.project_loaded = true;
|
|
CHECK(snprintf(
|
|
state.project_path,
|
|
sizeof(state.project_path),
|
|
"%s",
|
|
project
|
|
) > 0);
|
|
|
|
char error[256];
|
|
Lardon3DImageCatalog *catalog = lardon3d_image_catalog_load(
|
|
&state,
|
|
error,
|
|
sizeof(error)
|
|
);
|
|
CHECK(catalog);
|
|
CHECK(lardon3d_image_catalog_count(catalog) == 0);
|
|
CHECK(strcmp(error, "Aucune image importée.") == 0);
|
|
CHECK(lardon3d_image_catalog_get(catalog, 0) == NULL);
|
|
lardon3d_image_catalog_destroy(catalog);
|
|
lardon3d_image_catalog_destroy(NULL);
|
|
|
|
char first[PATH_MAX];
|
|
char utf8[PATH_MAX];
|
|
char link_path[PATH_MAX];
|
|
CHECK(join_path(first, originals, "première image.jpg"));
|
|
CHECK(join_path(utf8, originals, "été.png"));
|
|
CHECK(join_path(link_path, originals, "lien.jpg"));
|
|
CHECK(create_file(first, "abc"));
|
|
CHECK(create_file(utf8, "12345"));
|
|
|
|
const char valid_manifest[] =
|
|
"filename\tsize_bytes\tsource_path\n"
|
|
"première image.jpg\t3\t/tmp/source avec espaces/première image.jpg\n"
|
|
"été.png\t5\t/tmp/source/été.png\n";
|
|
CHECK(replace_manifest(manifest, valid_manifest));
|
|
catalog = lardon3d_image_catalog_load(&state, error, sizeof(error));
|
|
CHECK(catalog);
|
|
CHECK(error[0] == '\0');
|
|
CHECK(lardon3d_image_catalog_count(catalog) == 2);
|
|
CHECK(lardon3d_image_catalog_total_size(catalog) == 8);
|
|
const Lardon3DImageEntry *entry = lardon3d_image_catalog_get(catalog, 0);
|
|
CHECK(entry);
|
|
CHECK(strcmp(entry->filename, "première image.jpg") == 0);
|
|
CHECK(strcmp(
|
|
entry->source_path,
|
|
"/tmp/source avec espaces/première image.jpg"
|
|
) == 0);
|
|
CHECK(entry->size_bytes == 3);
|
|
entry = lardon3d_image_catalog_get(catalog, 1);
|
|
CHECK(entry && strcmp(entry->filename, "été.png") == 0);
|
|
CHECK(lardon3d_image_catalog_get(catalog, 2) == NULL);
|
|
lardon3d_image_catalog_destroy(catalog);
|
|
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"bad\theader\npremière image.jpg\t3\t/tmp/source\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\npremière image.jpg\t3\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\npremière image.jpg\t3\t/a\textra\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\npremière image.jpg\t12x\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\n"
|
|
"première image.jpg\t18446744073709551616\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\n\t3\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\ndir/image.jpg\t3\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\ndir\\image.jpg\t3\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\npremière image.jpg\t3\t\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\n"
|
|
"première image.jpg\t3\t/a\npremière image.jpg\t3\t/b\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\nabsente.jpg\t1\t/a\n"
|
|
));
|
|
CHECK(symlink(first, link_path) == 0);
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\nlien.jpg\t3\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\npremière image.jpg\t4\t/a\n"
|
|
));
|
|
CHECK(expect_invalid(
|
|
&state,
|
|
manifest,
|
|
"filename\tsize_bytes\tsource_path\npremière image.jpg\t3\t/a"
|
|
));
|
|
|
|
CHECK(replace_manifest(manifest, valid_manifest));
|
|
catalog = lardon3d_image_catalog_load(&state, error, sizeof(error));
|
|
CHECK(catalog && lardon3d_image_catalog_count(catalog) == 2);
|
|
lardon3d_image_catalog_destroy(catalog);
|
|
CHECK(test_sizes());
|
|
CHECK(remove_tree(base));
|
|
return true;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
return run_test() ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|