From: Timo Sirainen Date: Tue, 14 Jul 2009 21:55:27 +0000 (-0400) Subject: mail index transactions: More code cleanups and unit tests. X-Git-Tag: 2.0.alpha1~426 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=02bb8313a711dfe50c7f01e8132e13ca93ecfb42;p=thirdparty%2Fdovecot%2Fcore.git mail index transactions: More code cleanups and unit tests. --HG-- branch : HEAD --- diff --git a/src/lib-index/Makefile.am b/src/lib-index/Makefile.am index 1db1294d9a..f96d496a09 100644 --- a/src/lib-index/Makefile.am +++ b/src/lib-index/Makefile.am @@ -61,6 +61,7 @@ headers = \ mailbox-list-index-private.h test_programs = \ + test-mail-index-transaction-finish \ test-mail-index-transaction-update \ test-mail-transaction-log-append \ test-mail-transaction-log-view @@ -72,6 +73,10 @@ test_libs = \ ../lib-test/libtest.la \ ../lib/liblib.la +test_mail_index_transaction_finish_SOURCES = test-mail-index-transaction-finish.c +test_mail_index_transaction_finish_LDADD = mail-index-transaction-finish.lo $(test_libs) +test_mail_index_transaction_finish_DEPENDENCIES = mail-index-transaction-finish.lo $(test_libs) + test_mail_index_transaction_update_SOURCES = test-mail-index-transaction-update.c test_mail_index_transaction_update_LDADD = mail-index-transaction-update.lo $(test_libs) test_mail_index_transaction_update_DEPENDENCIES = mail-index-transaction-update.lo $(test_libs) diff --git a/src/lib-index/mail-index-transaction-finish.c b/src/lib-index/mail-index-transaction-finish.c index 3f428101c5..a6d96bdb7f 100644 --- a/src/lib-index/mail-index-transaction-finish.c +++ b/src/lib-index/mail-index-transaction-finish.c @@ -140,76 +140,11 @@ mail_index_transaction_finish_flag_updates(struct mail_index_transaction *t) array_free(&t->updates); } -static bool -mail_index_update_cancel_array(ARRAY_TYPE(seq_range) *array, uint32_t seq) -{ - if (array_is_created(array)) { - if (seq_range_array_remove(array, seq)) { - if (array_count(array) == 0) - array_free(array); - return TRUE; - } - } - return FALSE; -} - -static bool -mail_index_update_cancel(struct mail_index_transaction *t, uint32_t seq) -{ - struct mail_index_transaction_keyword_update *kw; - struct mail_transaction_flag_update *updates, tmp_update; - unsigned int i, count; - bool ret, have_kw_changes = FALSE; - - ret = mail_index_update_cancel_array(&t->keyword_resets, seq); - if (array_is_created(&t->keyword_updates)) { - kw = array_get_modifiable(&t->keyword_updates, &count); - for (i = 0; i < count; i++) { - if (mail_index_update_cancel_array(&kw[i].add_seq, seq)) - ret = TRUE; - if (mail_index_update_cancel_array(&kw[i].remove_seq, - seq)) - ret = TRUE; - if (array_is_created(&kw[i].add_seq) || - array_is_created(&kw[i].remove_seq)) - have_kw_changes = TRUE; - } - if (!have_kw_changes) - array_free(&t->keyword_updates); - } - - if (!array_is_created(&t->updates)) - return ret; - - updates = array_get_modifiable(&t->updates, &count); - i = mail_index_transaction_get_flag_update_pos(t, 0, count, seq); - if (i < count && updates[i].uid1 <= seq && updates[i].uid2 >= seq) { - /* exists */ - ret = TRUE; - if (updates[i].uid1 == seq && updates[i].uid2 == seq) { - if (count > 1) - array_delete(&t->updates, i, 1); - else - array_free(&t->updates); - } else if (updates[i].uid1 == seq) - updates[i].uid1++; - else if (updates[i].uid2 == seq) - updates[i].uid2--; - else { - /* need to split it in two */ - tmp_update = updates[i]; - tmp_update.uid1 = seq+1; - updates[i].uid2 = seq-1; - array_insert(&t->updates, i + 1, &tmp_update, 1); - } - } - return ret; -} - static void mail_index_transaction_check_conflicts(struct mail_index_transaction *t) { uint32_t seq; + bool ret1, ret2; i_assert(t->max_modseq != 0); i_assert(t->conflict_seqs != NULL); @@ -225,7 +160,9 @@ mail_index_transaction_check_conflicts(struct mail_index_transaction *t) for (seq = t->min_flagupdate_seq; seq <= t->max_flagupdate_seq; seq++) { if (mail_index_modseq_lookup(t->view, seq) > t->max_modseq) { - if (mail_index_update_cancel(t, seq)) + ret1 = mail_index_cancel_flag_updates(t, seq); + ret2 = mail_index_cancel_keyword_updates(t, seq); + if (ret1 || ret2) seq_range_array_add(t->conflict_seqs, 0, seq); } } diff --git a/src/lib-index/mail-index-transaction-private.h b/src/lib-index/mail-index-transaction-private.h index a9fe061ced..93fb2c9c8f 100644 --- a/src/lib-index/mail-index-transaction-private.h +++ b/src/lib-index/mail-index-transaction-private.h @@ -116,6 +116,11 @@ mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t, unsigned int right_idx, uint32_t seq); +bool mail_index_cancel_flag_updates(struct mail_index_transaction *t, + uint32_t seq); +bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t, + uint32_t seq); + int mail_index_transaction_finish(struct mail_index_transaction *t); void mail_index_transaction_export(struct mail_index_transaction *t, struct mail_transaction_log_append_ctx *append_ctx); diff --git a/src/lib-index/mail-index-transaction-update.c b/src/lib-index/mail-index-transaction-update.c index 64dc0fe96c..a33133be19 100644 --- a/src/lib-index/mail-index-transaction-update.c +++ b/src/lib-index/mail-index-transaction-update.c @@ -1013,6 +1013,83 @@ void mail_index_update_keywords(struct mail_index_transaction *t, uint32_t seq, t->log_updates = TRUE; } +bool mail_index_cancel_flag_updates(struct mail_index_transaction *t, + uint32_t seq) +{ + struct mail_transaction_flag_update *updates, tmp_update; + unsigned int i, count; + + if (!array_is_created(&t->updates)) + return FALSE; + + updates = array_get_modifiable(&t->updates, &count); + i = mail_index_transaction_get_flag_update_pos(t, 0, count, seq); + if (i == count) + return FALSE; + else { + i_assert(seq <= updates[i].uid2); + if (seq < updates[i].uid1) + return FALSE; + } + + /* exists */ + if (updates[i].uid1 == seq) { + if (updates[i].uid2 != seq) + updates[i].uid1++; + else if (count > 1) + array_delete(&t->updates, i, 1); + else + array_free(&t->updates); + } else if (updates[i].uid2 == seq) { + updates[i].uid2--; + } else { + /* need to split it in two */ + tmp_update = updates[i]; + tmp_update.uid1 = seq+1; + updates[i].uid2 = seq-1; + array_insert(&t->updates, i + 1, &tmp_update, 1); + } + return TRUE; +} + +static bool mail_index_cancel_array(ARRAY_TYPE(seq_range) *array, uint32_t seq) +{ + if (array_is_created(array)) { + if (seq_range_array_remove(array, seq)) { + if (array_count(array) == 0) + array_free(array); + return TRUE; + } + } + return FALSE; +} + +bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t, + uint32_t seq) +{ + struct mail_index_transaction_keyword_update *kw; + unsigned int i, count; + bool ret, have_kw_changes = FALSE; + + ret = mail_index_cancel_array(&t->keyword_resets, seq); + if (!array_is_created(&t->keyword_updates)) + return ret; + + kw = array_get_modifiable(&t->keyword_updates, &count); + for (i = 0; i < count; i++) { + if (mail_index_cancel_array(&kw[i].add_seq, seq)) + ret = TRUE; + if (mail_index_cancel_array(&kw[i].remove_seq, seq)) + ret = TRUE; + if (array_is_created(&kw[i].add_seq) || + array_is_created(&kw[i].remove_seq)) + have_kw_changes = TRUE; + } + if (!have_kw_changes) + array_free(&t->keyword_updates); + return ret; +} + void mail_index_transaction_reset(struct mail_index_transaction *t) { t->v.reset(t); diff --git a/src/lib-index/test-mail-index-transaction-finish.c b/src/lib-index/test-mail-index-transaction-finish.c new file mode 100644 index 0000000000..415e5babe6 --- /dev/null +++ b/src/lib-index/test-mail-index-transaction-finish.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "test-common.h" +#include "mail-index-private.h" +#include "mail-index-modseq.h" +#include "mail-index-transaction-private.h" + +#include + +static struct mail_index_record recs[20]; +static uint64_t modseqs[N_ELEMENTS(recs)]; + +bool mail_index_map_get_ext_idx(struct mail_index_map *map ATTR_UNUSED, + uint32_t ext_id ATTR_UNUSED, + uint32_t *idx_r ATTR_UNUSED) { return FALSE; } +void mail_index_ext_set_reset_id(struct mail_index_transaction *t ATTR_UNUSED, + uint32_t ext_id ATTR_UNUSED, + uint32_t reset_id ATTR_UNUSED) { } +void mail_index_transaction_set_log_updates(struct mail_index_transaction *t ATTR_UNUSED) { } +void mail_index_update_day_headers(struct mail_index_transaction *t ATTR_UNUSED) {} +bool mail_index_cancel_flag_updates(struct mail_index_transaction *t ATTR_UNUSED, + uint32_t seq ATTR_UNUSED) { return TRUE; } +bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t ATTR_UNUSED, + uint32_t seq ATTR_UNUSED) { return TRUE; } +void mail_index_transaction_sort_appends(struct mail_index_transaction *t ATTR_UNUSED) {} +int mail_index_map(struct mail_index *index ATTR_UNUSED, + enum mail_index_sync_handler_type type ATTR_UNUSED) { return 1; } + +const struct mail_index_record * +mail_index_lookup(struct mail_index_view *view ATTR_UNUSED, uint32_t seq) +{ + i_assert(seq < N_ELEMENTS(recs)); + return &recs[seq]; +} + +struct mail_index_record * +mail_index_transaction_lookup(struct mail_index_transaction *t ATTR_UNUSED, + uint32_t seq) +{ + i_assert(seq < N_ELEMENTS(recs)); + return &recs[seq]; +} + +uint64_t mail_index_modseq_lookup(struct mail_index_view *view ATTR_UNUSED, + uint32_t seq) +{ + i_assert(seq < N_ELEMENTS(modseqs)); + return modseqs[seq]; +} + +uint64_t mail_index_modseq_get_highest(struct mail_index_view *view ATTR_UNUSED) +{ + return modseqs[0]; +} + +static void test_mail_index_transaction_finish_flag_updates(void) +{ + struct mail_index_transaction *t; + const struct mail_transaction_flag_update *updates; + struct mail_transaction_flag_update u; + unsigned int count; + + t = t_new(struct mail_index_transaction, 1); + t->drop_unnecessary_flag_updates = TRUE; + + memset(&u, 0, sizeof(u)); + u.add_flags = MAIL_SEEN; u.remove_flags = MAIL_DRAFT; + + test_begin("mail index transaction finish flag updates"); + + /* test fast path: all changed */ + t_array_init(&t->updates, 10); + u.uid1 = 1; u.uid2 = 2; + array_append(&t->updates, &u, 1); + u.uid1 = 4; u.uid2 = 5; + array_append(&t->updates, &u, 1); + mail_index_transaction_finish(t); + + updates = array_get(&t->updates, &count); + test_assert(count == 2); + test_assert(updates[0].uid1 == 1 && updates[0].uid2 == 2); + test_assert(updates[1].uid1 == 4 && updates[1].uid2 == 5); + + /* nothing changed */ + recs[1].flags = MAIL_SEEN; + recs[2].flags = MAIL_SEEN; + recs[4].flags = MAIL_SEEN; + recs[5].flags = MAIL_SEEN; + mail_index_transaction_finish(t); + test_assert(!array_is_created(&t->updates)); + + /* some changes */ + t_array_init(&t->updates, 10); + u.uid1 = 2; u.uid2 = 3; + array_append(&t->updates, &u, 1); + u.uid1 = 5; u.uid2 = 6; + array_append(&t->updates, &u, 1); + mail_index_transaction_finish(t); + + updates = array_get(&t->updates, &count); + test_assert(count == 2); + test_assert(updates[0].uid1 == 3 && updates[0].uid2 == 3); + test_assert(updates[1].uid1 == 6 && updates[1].uid2 == 6); + + test_end(); +} + +static void test_mail_index_transaction_finish_check_conflicts(void) +{ + struct mail_index_transaction *t; + const struct seq_range *conflicts; + ARRAY_TYPE(seq_range) conflict_seqs = ARRAY_INIT; + unsigned int count; + + t = t_new(struct mail_index_transaction, 1); + t->view = t_new(struct mail_index_view, 1); + t->min_flagupdate_seq = 5; + t->max_flagupdate_seq = 8; + t->conflict_seqs = &conflict_seqs; + + modseqs[0] = 1234; + modseqs[5] = 5; + modseqs[6] = 8; + modseqs[7] = 6; + modseqs[8] = 7; + + test_begin("mail index transaction finish check conflicts"); + + /* fast path: no conflicts */ + t->max_modseq = 1234; + mail_index_transaction_finish(t); + test_assert(!array_is_created(&conflict_seqs)); + + /* try some conflicts */ + t->max_modseq = 6; + mail_index_transaction_finish(t); + + conflicts = array_get(&conflict_seqs, &count); + test_assert(count == 2); + test_assert(conflicts[0].seq1 == 6 && conflicts[0].seq2 == 6); + test_assert(conflicts[1].seq1 == 8 && conflicts[1].seq2 == 8); + + test_end(); +} + +static void test_mail_index_transaction_finish_expunges(void) +{ + struct mail_index_transaction *t; + uint8_t guid1[MAIL_GUID_128_SIZE]; + uint8_t guid2[MAIL_GUID_128_SIZE]; + uint8_t guid3[MAIL_GUID_128_SIZE]; + const struct mail_transaction_expunge_guid *expunges; + struct mail_transaction_expunge_guid expunge; + unsigned int i, count; + + for (i = 0; i < sizeof(guid2); i++) { + guid1[i] = i + 1; + guid2[i] = i ^ 0xff; + guid3[i] = i + 0x80; + } + + recs[1].uid = 12; + recs[2].uid = 15; + recs[3].uid = 18; + + t = t_new(struct mail_index_transaction, 1); + t->expunges_nonsorted = TRUE; + + test_begin("mail index transaction finish expunges"); + + t_array_init(&t->expunges, 3); + expunge.uid = 2; + memcpy(expunge.guid_128, guid2, sizeof(expunge.guid_128)); + array_append(&t->expunges, &expunge, 1); + array_append(&t->expunges, &expunge, 1); + expunge.uid = 1; + memcpy(expunge.guid_128, guid1, sizeof(expunge.guid_128)); + array_append(&t->expunges, &expunge, 1); + array_append(&t->expunges, &expunge, 1); + expunge.uid = 3; + memcpy(expunge.guid_128, guid3, sizeof(expunge.guid_128)); + array_append(&t->expunges, &expunge, 1); + array_append(&t->expunges, &expunge, 1); + + mail_index_transaction_finish(t); + + expunges = array_get(&t->expunges, &count); + test_assert(count == 3); + test_assert(expunges[0].uid == 12); + test_assert(memcmp(expunges[0].guid_128, guid1, sizeof(guid1)) == 0); + test_assert(expunges[1].uid == 15); + test_assert(memcmp(expunges[1].guid_128, guid2, sizeof(guid2)) == 0); + test_assert(expunges[2].uid == 18); + test_assert(memcmp(expunges[2].guid_128, guid3, sizeof(guid3)) == 0); + test_end(); +} + +int main(void) +{ + static void (*test_functions[])(void) = { + test_mail_index_transaction_finish_flag_updates, + test_mail_index_transaction_finish_check_conflicts, + test_mail_index_transaction_finish_expunges, + NULL + }; + unsigned int i; + + for (i = 1; i < N_ELEMENTS(recs); i++) + recs[i].uid = i; + return test_run(test_functions); +} diff --git a/src/lib-index/test-mail-index-transaction-update.c b/src/lib-index/test-mail-index-transaction-update.c index 07dcf7402b..84d1baeffe 100644 --- a/src/lib-index/test-mail-index-transaction-update.c +++ b/src/lib-index/test-mail-index-transaction-update.c @@ -329,6 +329,38 @@ static void test_mail_index_flag_update_random(void) test_end(); } +static void test_mail_index_cancel_flag_updates(void) +{ + struct mail_index_transaction *t; + const struct mail_transaction_flag_update *updates; + unsigned int count; + + hdr.messages_count = 20; + t = mail_index_transaction_new(); + + test_begin("mail index cancel flag updates"); + + mail_index_update_flags_range(t, 5, 7, MODIFY_REPLACE, 0); + updates = array_get(&t->updates, &count); + test_assert(count == 1); + test_assert(updates[0].uid1 == 5 && updates[0].uid2 == 7); + mail_index_cancel_flag_updates(t, 5); + test_assert(updates[0].uid1 == 6 && updates[0].uid2 == 7); + mail_index_cancel_flag_updates(t, 7); + test_assert(updates[0].uid1 == 6 && updates[0].uid2 == 6); + mail_index_cancel_flag_updates(t, 6); + test_assert(!array_is_created(&t->updates)); + + mail_index_update_flags_range(t, 5, 7, MODIFY_REPLACE, 0); + mail_index_cancel_flag_updates(t, 6); + updates = array_get(&t->updates, &count); + test_assert(count == 2); + test_assert(updates[0].uid1 == 5 && updates[0].uid2 == 5); + test_assert(updates[1].uid1 == 7 && updates[1].uid2 == 7); + + test_end(); +} + static void test_mail_index_flag_update_appends(void) { struct mail_index_transaction *t; @@ -419,6 +451,50 @@ static void test_mail_index_transaction_get_flag_update_pos(void) test_end(); } +static void test_mail_index_expunge(void) +{ + static uint8_t empty_guid[MAIL_GUID_128_SIZE] = { 0, }; + struct mail_index_transaction *t; + const struct mail_transaction_expunge_guid *expunges; + uint8_t guid2[MAIL_GUID_128_SIZE]; + uint8_t guid3[MAIL_GUID_128_SIZE]; + uint8_t guid4[MAIL_GUID_128_SIZE]; + unsigned int i, count; + + test_begin("mail index expunge"); + + hdr.messages_count = 10; + t = mail_index_transaction_new(); + for (i = 0; i < sizeof(guid2); i++) { + guid2[i] = i + 1; + guid3[i] = i ^ 0xff; + guid4[i] = i + 0x80; + } + + mail_index_expunge_guid(t, 4, guid4); + test_assert(!t->expunges_nonsorted); + mail_index_expunge_guid(t, 2, guid2); + test_assert(t->expunges_nonsorted); + mail_index_expunge_guid(t, 3, guid3); + mail_index_expunge(t, 1); + mail_index_expunge(t, 5); + + expunges = array_get(&t->expunges, &count); + test_assert(count == 5); + test_assert(expunges[0].uid == 4); + test_assert(memcmp(expunges[0].guid_128, guid4, sizeof(guid4)) == 0); + test_assert(expunges[1].uid == 2); + test_assert(memcmp(expunges[1].guid_128, guid2, sizeof(guid2)) == 0); + test_assert(expunges[2].uid == 3); + test_assert(memcmp(expunges[2].guid_128, guid3, sizeof(guid3)) == 0); + test_assert(expunges[3].uid == 1); + test_assert(memcmp(expunges[3].guid_128, empty_guid, sizeof(empty_guid)) == 0); + test_assert(expunges[4].uid == 5); + test_assert(memcmp(expunges[4].guid_128, empty_guid, sizeof(empty_guid)) == 0); + + test_end(); +} + int main(void) { static void (*test_functions[])(void) = { @@ -428,7 +504,9 @@ int main(void) test_mail_index_flag_update_complex_merges, test_mail_index_flag_update_random, test_mail_index_flag_update_appends, + test_mail_index_cancel_flag_updates, test_mail_index_transaction_get_flag_update_pos, + test_mail_index_expunge, NULL }; return test_run(test_functions);