]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-fs: Added fs-test backend for helping with creation of fs-wrapper unit tests.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 21 Apr 2016 14:52:44 +0000 (17:52 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 26 Apr 2016 08:27:44 +0000 (11:27 +0300)
src/lib-fs/Makefile.am
src/lib-fs/fs-api-private.h
src/lib-fs/fs-api.c
src/lib-fs/fs-test.c [new file with mode: 0644]
src/lib-fs/fs-test.h [new file with mode: 0644]

index aed58c18f37e7dc91a33429f2bb7be6c7db97086..5df3c5dd458d8cbc439dca9aac199df23c9ec3c2 100644 (file)
@@ -2,6 +2,7 @@ noinst_LTLIBRARIES = libfs.la
 
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib-test \
        -I$(top_srcdir)/src/lib-dict \
        -I$(top_srcdir)/src/lib-ssl-iostream \
        -DMODULE_DIR=\""$(moduledir)"\"
@@ -12,6 +13,7 @@ libfs_la_SOURCES = \
        fs-metawrap.c \
        fs-randomfail.c \
        fs-posix.c \
+       fs-test.c \
        fs-sis.c \
        fs-sis-common.c \
        fs-sis-queue.c \
@@ -25,6 +27,7 @@ headers = \
        fs-api.h \
        fs-api-private.h \
        fs-sis-common.h \
+       fs-test.h \
        istream-fs-file.h \
        istream-fs-stats.h \
        istream-metawrap.h \
index 8e6d173f5eb1a0fdf3ad4ec2aeeaecf7f78edf8b..6f931c19c29db5518f9444bf107cccc49b4642e1 100644 (file)
@@ -145,6 +145,7 @@ extern const struct fs fs_class_randomfail;
 extern const struct fs fs_class_metawrap;
 extern const struct fs fs_class_sis;
 extern const struct fs fs_class_sis_queue;
+extern const struct fs fs_class_test;
 
 void fs_class_register(const struct fs *fs_class);
 
index 921485d47ae7fc2807828138c510a22d4cb4def8..b50fbe0f53ce6c3f69dcae686f7fe41528e2f613 100644 (file)
@@ -74,6 +74,7 @@ static void fs_classes_init(void)
        fs_class_register(&fs_class_metawrap);
        fs_class_register(&fs_class_sis);
        fs_class_register(&fs_class_sis_queue);
+       fs_class_register(&fs_class_test);
        lib_atexit(fs_classes_deinit);
 }
 
diff --git a/src/lib-fs/fs-test.c b/src/lib-fs/fs-test.c
new file mode 100644 (file)
index 0000000..211b61f
--- /dev/null
@@ -0,0 +1,355 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "ostream.h"
+#include "test-common.h"
+#include "fs-test.h"
+
+static struct fs *fs_test_alloc(void)
+{
+       struct test_fs *fs;
+
+       fs = i_new(struct test_fs, 1);
+       fs->fs = fs_class_test;
+       i_array_init(&fs->iter_files, 32);
+       return &fs->fs;
+}
+
+static int
+fs_test_init(struct fs *_fs ATTR_UNUSED, const char *args ATTR_UNUSED,
+            const struct fs_settings *set ATTR_UNUSED)
+{
+       return 0;
+}
+
+static void fs_test_deinit(struct fs *_fs)
+{
+       struct test_fs *fs = (struct test_fs *)_fs;
+
+       array_free(&fs->iter_files);
+       i_free(fs);
+}
+
+static enum fs_properties fs_test_get_properties(struct fs *_fs)
+{
+       struct test_fs *fs = (struct test_fs *)_fs;
+
+       return fs->properties;
+}
+
+static struct fs_file *
+fs_test_file_init(struct fs *_fs, const char *path,
+                 enum fs_open_mode mode, enum fs_open_flags flags ATTR_UNUSED)
+{
+       struct test_fs_file *file;
+
+       file = i_new(struct test_fs_file, 1);
+       file->file.fs = _fs;
+       file->file.path = i_strdup(path);
+       file->mode = mode;
+       file->contents = buffer_create_dynamic(default_pool, 1024);
+       file->exists = TRUE;
+       file->seekable = TRUE;
+       return &file->file;
+}
+
+static void fs_test_file_deinit(struct fs_file *_file)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       buffer_free(&file->contents);
+       i_free(file->file.path);
+       i_free(file);
+}
+
+static void fs_test_file_close(struct fs_file *_file)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       file->closed = TRUE;
+}
+
+static const char *fs_test_file_get_path(struct fs_file *_file)
+{
+       return _file->path;
+}
+
+static void
+fs_test_set_async_callback(struct fs_file *_file,
+                          fs_file_async_callback_t *callback,
+                          void *context)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       file->async_callback = callback;
+       file->async_context = context;
+}
+
+static int fs_test_wait_async(struct fs *_fs ATTR_UNUSED)
+{
+       return 0;
+}
+
+static void
+fs_test_set_metadata(struct fs_file *_file, const char *key,
+                    const char *value)
+{
+       fs_default_set_metadata(_file, key, value);
+}
+
+static int
+fs_test_get_metadata(struct fs_file *_file,
+                    const ARRAY_TYPE(fs_metadata) **metadata_r)
+{
+       fs_metadata_init(_file);
+       *metadata_r = &_file->metadata;
+       return 0;
+}
+
+static bool fs_test_prefetch(struct fs_file *_file ATTR_UNUSED,
+                            uoff_t length ATTR_UNUSED)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       file->prefetched = TRUE;
+       return TRUE;
+}
+
+static void fs_test_stream_destroyed(struct test_fs_file *file)
+{
+       i_assert(file->input != NULL);
+       file->input = NULL;
+}
+
+static struct istream *
+fs_test_read_stream(struct fs_file *_file, size_t max_buffer_size ATTR_UNUSED)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+       struct istream *input;
+
+       i_assert(file->input == NULL);
+
+       if (!file->exists)
+               return i_stream_create_error(ENOENT);
+       input = test_istream_create_data(file->contents->data,
+                                        file->contents->used);
+       i_stream_add_destroy_callback(input, fs_test_stream_destroyed, file);
+       if (!file->seekable)
+               input->seekable = FALSE;
+       file->input = input;
+       return input;
+}
+
+static void fs_test_write_stream(struct fs_file *_file)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       i_assert(_file->output == NULL);
+
+       buffer_set_used_size(file->contents, 0);
+       _file->output = o_stream_create_buffer(file->contents);
+}
+
+static int fs_test_write_stream_finish(struct fs_file *_file, bool success)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       if (!success)
+               buffer_set_used_size(file->contents, 0);
+       return success ? 1 : -1;
+}
+
+static int
+fs_test_lock(struct fs_file *_file, unsigned int secs ATTR_UNUSED,
+            struct fs_lock **lock_r)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       if (file->locked)
+               return 0;
+       file->locked = TRUE;
+       *lock_r = i_new(struct fs_lock, 1);
+       (*lock_r)->file = _file;
+       return 1;
+}
+
+static void fs_test_unlock(struct fs_lock *lock)
+{
+       struct test_fs_file *file = (struct test_fs_file *)lock->file;
+
+       file->locked = FALSE;
+       i_free(lock);
+}
+
+static int fs_test_exists(struct fs_file *_file)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       return file->exists ? 1 : 0;
+}
+
+static int fs_test_stat(struct fs_file *_file, struct stat *st_r)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       if (!file->exists) {
+               errno = ENOENT;
+               return -1;
+       }
+       memset(st_r, 0, sizeof(*st_r));
+       st_r->st_size = file->contents->used;
+       return 0;
+}
+
+static int fs_test_copy(struct fs_file *_src, struct fs_file *_dest)
+{
+       struct test_fs_file *src = (struct test_fs_file *)_src;
+       struct test_fs_file *dest = (struct test_fs_file *)_dest;
+
+       if (!src->exists) {
+               errno = ENOENT;
+               return -1;
+       }
+       buffer_set_used_size(dest->contents, 0);
+       buffer_append_buf(dest->contents, src->contents, 0, (size_t)-1);
+       dest->exists = TRUE;
+       return 0;
+}
+
+static int fs_test_rename(struct fs_file *_src, struct fs_file *_dest)
+{
+       struct test_fs_file *src = (struct test_fs_file *)_src;
+
+       if (fs_test_copy(_src, _dest) < 0)
+               return -1;
+       src->exists = FALSE;
+       return 0;
+}
+
+static int fs_test_delete(struct fs_file *_file)
+{
+       struct test_fs_file *file = (struct test_fs_file *)_file;
+
+       if (!file->exists) {
+               errno = ENOENT;
+               return -1;
+       }
+       return 0;
+}
+
+static struct fs_iter *
+fs_test_iter_init(struct fs *_fs, const char *path,
+                 enum fs_iter_flags flags)
+{
+       struct test_fs *fs = (struct test_fs *)_fs;
+       struct test_fs_iter *iter;
+
+       iter = i_new(struct test_fs_iter, 1);
+       iter->iter.fs = _fs;
+       iter->iter.flags = flags;
+       iter->prefix = i_strdup(path);
+       iter->prefix_len = strlen(iter->prefix);
+       iter->prev_dir = i_strdup("");
+       array_sort(&fs->iter_files, i_strcmp_p);
+       return &iter->iter;
+}
+
+static const char *fs_test_iter_next(struct fs_iter *_iter)
+{
+       struct test_fs_iter *iter = (struct test_fs_iter *)_iter;
+       struct test_fs *fs = (struct test_fs *)_iter->fs;
+       const char *const *files, *p;
+       unsigned int count, len, prev_dir_len = strlen(iter->prev_dir);
+
+       files = array_get(&fs->iter_files, &count);
+       for (; iter->idx < count; iter->idx++) {
+               const char *fname = files[iter->idx];
+
+               if (strncmp(fname, iter->prefix, iter->prefix_len) != 0)
+                       continue;
+               p = strrchr(fname, '/');
+               if ((_iter->flags & FS_ITER_FLAG_DIRS) == 0) {
+                       if (p == NULL)
+                               return fname;
+                       if (p[1] == '\0')
+                               continue; /* dir/ */
+                       return p+1;
+               }
+
+               if (p == NULL)
+                       continue;
+               len = p - fname;
+               if (len == 0)
+                       continue;
+               if (len == prev_dir_len &&
+                   strncmp(fname, iter->prev_dir, len) == 0)
+                       continue;
+               i_free(iter->prev_dir);
+               iter->prev_dir = i_strndup(fname, len);
+               return iter->prev_dir;
+       }
+       return NULL;
+}
+
+static int fs_test_iter_deinit(struct fs_iter *_iter)
+{
+       struct test_fs_iter *iter = (struct test_fs_iter *)_iter;
+       int ret = iter->failed ? -1 : 0;
+
+       i_free(iter->prefix);
+       i_free(iter);
+       return ret;
+}
+
+struct test_fs_file *test_fs_file_get(struct fs *fs, unsigned int n)
+{
+       struct fs_file *file;
+
+       while (strcmp(fs->name, "test") != 0) {
+               i_assert(fs->parent != NULL);
+               fs = fs->parent;
+       }
+
+       file = fs->files;
+       for (; n > 0; n--) {
+               i_assert(file != NULL);
+               file = file->next;
+       }
+       i_assert(file != NULL);
+       return (struct test_fs_file *)file;
+}
+
+const struct fs fs_class_test = {
+       .name = "test",
+       .v = {
+               fs_test_alloc,
+               fs_test_init,
+               fs_test_deinit,
+               fs_test_get_properties,
+               fs_test_file_init,
+               fs_test_file_deinit,
+               fs_test_file_close,
+               fs_test_file_get_path,
+               fs_test_set_async_callback,
+               fs_test_wait_async,
+               fs_test_set_metadata,
+               fs_test_get_metadata,
+               fs_test_prefetch,
+               NULL,
+               fs_test_read_stream,
+               NULL,
+               fs_test_write_stream,
+               fs_test_write_stream_finish,
+               fs_test_lock,
+               fs_test_unlock,
+               fs_test_exists,
+               fs_test_stat,
+               fs_test_copy,
+               fs_test_rename,
+               fs_test_delete,
+               fs_test_iter_init,
+               fs_test_iter_next,
+               fs_test_iter_deinit
+       }
+};
diff --git a/src/lib-fs/fs-test.h b/src/lib-fs/fs-test.h
new file mode 100644 (file)
index 0000000..c5f94cc
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef FS_TEST_H
+#define FS_TEST_H
+
+#include "fs-api-private.h"
+
+struct test_fs {
+       struct fs fs;
+       enum fs_properties properties;
+       ARRAY_TYPE(const_string) iter_files;
+};
+
+struct test_fs_file {
+       struct fs_file file;
+       enum fs_open_mode mode;
+
+       fs_file_async_callback_t *async_callback;
+       void *async_context;
+
+       buffer_t *contents;
+       struct istream *input;
+
+       bool prefetched;
+       bool locked;
+       bool exists;
+       bool seekable;
+       bool closed;
+};
+
+struct test_fs_iter {
+       struct fs_iter iter;
+       char *prefix, *prev_dir;
+       unsigned int prefix_len, idx;
+       bool failed;
+};
+
+struct test_fs_file *test_fs_file_get(struct fs *fs, unsigned int n);
+
+#endif