diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-index/test-mail-transaction-log-view.c | 268 |
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); +} |