summaryrefslogtreecommitdiffstats
path: root/src/lib-index/test-mail-transaction-log-view.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-index/test-mail-transaction-log-view.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/lib-index/test-mail-transaction-log-view.c b/src/lib-index/test-mail-transaction-log-view.c
new file mode 100644
index 0000000..17a7628
--- /dev/null
+++ b/src/lib-index/test-mail-transaction-log-view.c
@@ -0,0 +1,268 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "test-common.h"
+#include "mail-index-private.h"
+#include "mail-transaction-log-view-private.h"
+
+static struct mail_transaction_log *log;
+static struct mail_transaction_log_view *view;
+static bool clean_refcount0_files = FALSE;
+
+static void
+test_transaction_log_file_add(uint32_t file_seq)
+{
+ struct mail_transaction_log_file **p, *file;
+
+ file = i_new(struct mail_transaction_log_file, 1);
+ file->hdr.file_seq = file_seq;
+ file->hdr.hdr_size = file->sync_offset = sizeof(file->hdr);
+ file->hdr.prev_file_seq = file_seq - 1;
+ file->hdr.prev_file_offset = (uint32_t)-1;
+ file->log = log;
+ file->fd = -1;
+ file->buffer = buffer_create_dynamic(default_pool, 256);
+ file->buffer_offset = file->hdr.hdr_size;
+
+ /* files must be sorted by file_seq */
+ for (p = &log->files; *p != NULL; p = &(*p)->next) {
+ if ((*p)->hdr.file_seq > file->hdr.file_seq) {
+ file->next = *p;
+ break;
+ }
+ }
+ *p = file;
+ log->head = file;
+}
+
+void mail_index_set_error(struct mail_index *index ATTR_UNUSED,
+ const char *fmt ATTR_UNUSED, ...)
+{
+}
+
+void mail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file ATTR_UNUSED,
+ const char *fmt ATTR_UNUSED, ...)
+{
+}
+
+void mail_transaction_logs_clean(struct mail_transaction_log *log ATTR_UNUSED)
+{
+}
+
+int mail_transaction_log_find_file(struct mail_transaction_log *log,
+ uint32_t file_seq, bool nfs_flush ATTR_UNUSED,
+ struct mail_transaction_log_file **file_r,
+ const char **reason_r)
+{
+ struct mail_transaction_log_file *file, *next;
+
+ for (file = log->files; file != NULL; file = next) {
+ next = file->next;
+ if (file->hdr.file_seq == file_seq) {
+ *file_r = file;
+ return 1;
+ }
+ /* refcount=0 files at the beginning of the list may be freed */
+ if (file->refcount == 0 && file == log->files &&
+ clean_refcount0_files)
+ log->files = next;
+ }
+ if (clean_refcount0_files && file_seq == 4) {
+ /* "clean refcount=0 files" test autocreates this file */
+ test_transaction_log_file_add(4);
+ *file_r = log->head;
+ return 1;
+ }
+ *reason_r = "not found";
+ return 0;
+}
+
+int mail_transaction_log_file_map(struct mail_transaction_log_file *file ATTR_UNUSED,
+ uoff_t start_offset ATTR_UNUSED, uoff_t end_offset ATTR_UNUSED,
+ const char **reason_r ATTR_UNUSED)
+{
+ return 1;
+}
+
+int mail_transaction_log_file_get_highest_modseq_at(
+ struct mail_transaction_log_file *file ATTR_UNUSED,
+ uoff_t offset ATTR_UNUSED, uint64_t *highest_modseq_r,
+ const char **error_r ATTR_UNUSED)
+{
+ *highest_modseq_r = 0;
+ return 1;
+}
+
+void mail_transaction_update_modseq(const struct mail_transaction_header *hdr ATTR_UNUSED,
+ const void *data ATTR_UNUSED,
+ uint64_t *cur_modseq,
+ unsigned int version ATTR_UNUSED)
+{
+ *cur_modseq += 1;
+}
+
+static bool view_is_file_refed(uint32_t file_seq)
+{
+ struct mail_transaction_log_file *const *files;
+ unsigned int i, count;
+ bool ret = FALSE;
+
+ files = array_get(&view->file_refs, &count);
+ for (i = 0; i < count; i++) {
+ if (files[i]->hdr.file_seq == file_seq) {
+ i_assert(!ret); /* could be a test too.. */
+ ret = TRUE;
+ }
+ }
+ return ret;
+}
+
+static size_t
+add_append_record(struct mail_transaction_log_file *file,
+ const struct mail_index_record *rec)
+{
+ struct mail_transaction_header hdr;
+ size_t size;
+
+ i_zero(&hdr);
+ hdr.type = MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL;
+ hdr.size = mail_index_uint32_to_offset(sizeof(hdr) + sizeof(*rec));
+
+ buffer_append(file->buffer, &hdr, sizeof(hdr));
+ buffer_append(file->buffer, rec, sizeof(*rec));
+
+ size = sizeof(hdr) + sizeof(*rec);
+ file->sync_offset += size;
+ return size;
+}
+
+static void test_mail_transaction_log_view(void)
+{
+ const struct mail_transaction_header *hdr;
+ const struct mail_index_record *rec;
+ struct mail_index_record append_rec;
+ const void *data;
+ void *oldfile;
+ uint32_t seq;
+ uoff_t offset, last_log_size;
+ const char *reason;
+ bool reset;
+
+ test_begin("init");
+ log = i_new(struct mail_transaction_log, 1);
+ log->index = i_new(struct mail_index, 1);
+ log->index->log = log;
+ log->index->log_sync_locked = TRUE;
+ test_transaction_log_file_add(1);
+ test_transaction_log_file_add(2);
+ test_transaction_log_file_add(3);
+
+ /* add an append record to the 3rd log file */
+ i_zero(&append_rec);
+ append_rec.uid = 1;
+
+ last_log_size = sizeof(struct mail_transaction_log_header) +
+ add_append_record(log->head, &append_rec);
+
+ view = mail_transaction_log_view_open(log);
+ i_assert(view != NULL);
+ test_assert(log->views == view &&
+ !view_is_file_refed(1) && !view_is_file_refed(2) &&
+ view_is_file_refed(3));
+ test_end();
+
+ /* we have files 1-3 opened */
+ test_begin("set all");
+ test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, UOFF_T_MAX, &reset, &reason) == 1 &&
+ reset && view_is_file_refed(1) && view_is_file_refed(2) &&
+ view_is_file_refed(3) &&
+ !mail_transaction_log_view_is_corrupted(view));
+ mail_transaction_log_view_get_prev_pos(view, &seq, &offset);
+ test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header));
+ test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 1);
+ test_assert(hdr->type == (MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL));
+ rec = data;
+ test_assert(memcmp(rec, &append_rec, sizeof(*rec)) == 0);
+ test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0);
+ test_assert(mail_transaction_log_view_is_last(view));
+ mail_transaction_log_view_get_prev_pos(view, &seq, &offset);
+ test_assert(seq == 3 && offset == last_log_size);
+ test_end();
+
+ test_begin("set first");
+ test_assert(mail_transaction_log_view_set(view, 0, 0, 0, 0, &reset, &reason) == 1);
+ mail_transaction_log_view_get_prev_pos(view, &seq, &offset);
+ test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header));
+ test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0);
+ mail_transaction_log_view_get_prev_pos(view, &seq, &offset);
+ test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header));
+ test_end();
+
+ test_begin("set end");
+ test_assert(mail_transaction_log_view_set(view, 3, last_log_size, (uint32_t)-1, UOFF_T_MAX, &reset, &reason) == 1);
+ mail_transaction_log_view_get_prev_pos(view, &seq, &offset);
+ test_assert(seq == 3 && offset == last_log_size);
+ test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0);
+ mail_transaction_log_view_get_prev_pos(view, &seq, &offset);
+ test_assert(seq == 3 && offset == last_log_size);
+ test_end();
+
+ test_begin("log clear");
+ mail_transaction_log_view_clear(view, 2);
+ test_assert(!view_is_file_refed(1) && view_is_file_refed(2) &&
+ view_is_file_refed(3));
+ oldfile = log->files;
+ buffer_free(&log->files->buffer);
+ log->files = log->files->next;
+ i_free(oldfile);
+ test_assert(log->files->hdr.file_seq == 2);
+ test_end();
+
+ /* --- first file has been removed --- */
+
+ test_begin("set 2-3");
+ test_assert(mail_transaction_log_view_set(view, 2, 0, (uint32_t)-1, UOFF_T_MAX, &reset, &reason) == 1);
+ test_end();
+
+ test_begin("missing log handing");
+ test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, UOFF_T_MAX, &reset, &reason) == 0);
+ test_end();
+
+ test_begin("closed log handling");
+ view->log = NULL;
+ test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, UOFF_T_MAX, &reset, &reason) == 0);
+ view->log = log;
+ test_end();
+
+ test_begin("clean refcount=0 files");
+ oldfile = log->files;
+ /* clear all references */
+ mail_transaction_log_view_clear(view, 0);
+ clean_refcount0_files = TRUE;
+ /* create a new file during mail_transaction_log_view_set(), which
+ triggers freeing any unreferenced files. */
+ test_assert(mail_transaction_log_view_set(view, 2, 0, 4, UOFF_T_MAX, &reset, &reason) == 1);
+ clean_refcount0_files = FALSE;
+ log->files = oldfile;
+ test_end();
+
+ mail_transaction_log_view_close(&view);
+ i_free(log->index);
+ while (log->files != NULL) {
+ oldfile = log->files;
+ buffer_free(&log->files->buffer);
+ log->files = log->files->next;
+ i_free(oldfile);
+ }
+ i_free(log);
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_mail_transaction_log_view,
+ NULL
+ };
+ return test_run(test_functions);
+}