Mercurial > dovecot > core-2.2
view src/dsync/test-dsync-brain.c @ 9644:16849c0931be HEAD
dsync: Handle missing mail GUIDs.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 17 Jul 2009 20:11:05 -0400 |
parents | ffda7bd92ebc |
children | 4d5cc6ce68aa |
line wrap: on
line source
/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "sha1.h" #include "master-service.h" #include "dsync-brain-private.h" #include "test-common.h" #include "test-dsync-worker.h" #include "test-dsync-common.h" #include <stdlib.h> enum { FLAG_EXISTS = 0x01, FLAG_CREATED = 0x02, FLAG_UNCHANGED = 0x04 }; struct test_dsync_mailbox { struct dsync_mailbox box; struct dsync_message *src_msgs, *dest_msgs; unsigned int dest_flags; }; static const char *kw12[] = { "kw1", "kw2", NULL }; static struct dsync_message box1_src_msgs[] = { { "guid1", 3, MAIL_SEEN, kw12, 123, 987 }, { "guid2", 5, MAIL_DRAFT, NULL, 125, 989 }, { "guid3", 8, 0, NULL, 128, 990 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct dsync_message box1_dest_msgs[] = { { "guid1", 3, MAIL_FLAGGED, NULL, 123, 987 }, { "guid2", 5, MAIL_DRAFT, kw12, 125, 989 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct dsync_message box2_src_msgs[] = { { "guid2", 6, MAIL_ANSWERED | MAIL_FLAGGED, NULL, 3434, 6552354 }, { "guid4", 10, 0, NULL, 3426, 43643 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct dsync_message box3_src_msgs[] = { { "guid1", 1, MAIL_FLAGGED, NULL, 5454, 273850 }, { "guid5", 5, 0, NULL, 331, 38701233 }, { "b75c81f2b3a4c9f84f24851c37acedee", 6, DSYNC_MAIL_FLAG_EXPUNGED, NULL, 331, 38701233 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct dsync_message box3_dest_msgs[] = { { "guid1", 1, MAIL_FLAGGED, NULL, 5454, 273850 }, { "guid8", 3, 0, NULL, 330, 2424 }, { "guid5", 5, 0, NULL, 1, 38701233 }, { "guid6", 6, 0, NULL, 333, 6482 }, { "guid7", 7, 0, NULL, 333, 6482 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct test_dsync_mailbox basic_mailboxes[] = { { { "box1", { { 0x12, 0x34, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x21, 0x43, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe } }, 1234567890, 4321, 605040302010ULL }, box1_src_msgs, box1_dest_msgs, FLAG_EXISTS }, { { "box2", { { 0xa3, 0xbd, 0x78, 0x24, 0xde, 0xfe, 0x08, 0xf7, 0xac, 0xc7, 0xca, 0x8c, 0xe7, 0x39, 0xdb, 0xca } }, 554321023, 6767, 79 }, box2_src_msgs, NULL, 0 }, { { "box3", { { 0x46, 0x25, 0xb3, 0x24, 0xde, 0xfe, 0x08, 0xf7, 0xac, 0xc7, 0xca, 0x1a, 0xe7, 0x39, 0xdb, 0x54 } }, 4545454, 656, 2366 }, box3_src_msgs, box3_dest_msgs, FLAG_EXISTS }, { { "dir1", { { 0, } }, 0, 0, 0 }, NULL, NULL, FLAG_EXISTS }, { { "dir2", { { 0, } }, 0, 0, 0 }, NULL, NULL, 0 }, { { "box4", { { 0x46, 0x2d, 0xa3, 0x24, 0x2e, 0x5e, 0x28, 0x67, 0xa6, 0xc7, 0xca, 0x8a, 0xe7, 0x36, 0xd4, 0xa4 } }, 2142, 445, 53535 }, box3_src_msgs, box3_dest_msgs, FLAG_EXISTS | FLAG_UNCHANGED }, { { NULL, { { 0, } }, 0, 0, 0 }, NULL, NULL, 0 } }; static struct test_dsync_mailbox *mailboxes; struct master_service *master_service; void master_service_stop(struct master_service *master_service ATTR_UNUSED) { } void mail_generate_guid_128_hash(const char *guid, uint8_t guid_128[MAIL_GUID_128_SIZE]) { unsigned char sha1_sum[SHA1_RESULTLEN]; sha1_get_digest(guid, strlen(guid), sha1_sum); memcpy(guid_128, sha1_sum, MAIL_GUID_128_SIZE); } bool mail_guid_128_is_empty(const uint8_t guid_128[MAIL_GUID_128_SIZE] ATTR_UNUSED) { return FALSE; } static bool mailbox_find(const char *name, unsigned int *idx_r) { unsigned int i; for (i = 0; mailboxes[i].box.name != NULL; i++) { if (strcmp(mailboxes[i].box.name, name) == 0) { *idx_r = i; return TRUE; } } return FALSE; } static int test_dsync_mailbox_cmp(const void *p1, const void *p2) { const struct test_dsync_mailbox *t1 = p1, *t2 = p2; return dsync_mailbox_guid_cmp(&t1->box, &t2->box); } static void test_dsync_sync_msgs(struct test_dsync_worker *worker, bool dest) { struct test_dsync_worker_msg test_msg; struct dsync_message *msgs; unsigned int i, j; for (i = 0; mailboxes[i].box.name != NULL; i++) { msgs = dest ? mailboxes[i].dest_msgs : mailboxes[i].src_msgs; if (msgs == NULL) continue; if ((mailboxes[i].dest_flags & FLAG_UNCHANGED) != 0) continue; for (j = 0; msgs[j].guid != NULL; j++) { test_msg.msg = msgs[j]; test_msg.mailbox_idx = i; array_append(&worker->msg_iter.msgs, &test_msg, 1); worker->worker.input_callback(worker->worker.input_context); } } worker->msg_iter.last = TRUE; worker->worker.input_callback(worker->worker.input_context); } static int test_dsync_msg_event_cmp(const struct test_dsync_msg_event *e1, const struct test_dsync_msg_event *e2) { int ret; ret = memcmp(e1->mailbox.guid, e2->mailbox.guid, sizeof(e1->mailbox.guid)); if (ret != 0) return ret; return (int)e1->msg.uid - (int)e2->msg.uid; } static bool test_dsync_msg_find_guid(const struct test_dsync_mailbox *box, const struct dsync_message *msg, const struct test_dsync_mailbox **box2_r, const struct dsync_message **msg2_r) { unsigned int i, j; for (i = 0; mailboxes[i].box.name != NULL; i++) { if (mailboxes[i].src_msgs == NULL) continue; for (j = 0; mailboxes[i].src_msgs[j].guid != NULL; j++) { if (strcmp(mailboxes[i].src_msgs[j].guid, msg->guid) != 0) continue; if (memcmp(mailboxes[i].box.guid.guid, box->box.guid.guid, MAILBOX_GUID_SIZE) != 0 || mailboxes[i].src_msgs[j].uid != msg->uid) { *box2_r = &mailboxes[i]; *msg2_r = &mailboxes[i].src_msgs[j]; return TRUE; } } } return FALSE; } static void test_dsync_brain_verify_existing_one(const struct test_dsync_mailbox *box, const struct test_dsync_msg_event *event, const struct dsync_message *src) { test_assert(event->msg.guid != NULL); test_assert(event->type == LAST_MSG_TYPE_UPDATE); test_assert(memcmp(event->mailbox.guid, box->box.guid.guid, MAILBOX_GUID_SIZE) == 0); test_assert(event->msg.flags == src->flags); test_assert(dsync_keyword_list_equals(event->msg.keywords, src->keywords)); test_assert(event->msg.modseq == src->modseq); } static void test_dsync_brain_verify_existing(const struct test_dsync_mailbox *box, const struct test_dsync_msg_event **eventsp, unsigned int *idx_r) { const struct test_dsync_msg_event *event = *eventsp; unsigned int i, j; /* we don't try to handle uid conflicts here */ i = j = 0; while (box->src_msgs[i].guid != NULL && box->dest_msgs[j].guid != NULL) { if (box->src_msgs[i].uid < box->dest_msgs[j].uid) { /* need to add message to dest */ i++; } else if (box->src_msgs[i].uid > box->dest_msgs[j].uid) { /* message expunged from src */ test_assert(event->type == LAST_MSG_TYPE_EXPUNGE); test_assert(memcmp(event->mailbox.guid, box->box.guid.guid, MAILBOX_GUID_SIZE) == 0); test_assert(event->msg.uid == box->dest_msgs[j].uid); j++; event++; } else if (box->src_msgs[i].flags == DSYNC_MAIL_FLAG_EXPUNGED) { /* message expunged from end of mailbox */ test_assert(event->type == LAST_MSG_TYPE_EXPUNGE); test_assert(event->msg.uid == box->dest_msgs[j].uid); i++; j++; event++; } else if (box->src_msgs[i].modseq > box->dest_msgs[j].modseq || box->src_msgs[i].flags != box->dest_msgs[j].flags || !dsync_keyword_list_equals(box->src_msgs[i].keywords, box->dest_msgs[j].keywords)) { /* message changed */ i_assert(strcmp(box->src_msgs[i].guid, box->dest_msgs[j].guid) == 0); test_dsync_brain_verify_existing_one(box, event, &box->src_msgs[i]); i++; j++; event++; } else { /* message unchanged */ i_assert(strcmp(box->src_msgs[i].guid, box->dest_msgs[j].guid) == 0); i++; j++; } } while (box->dest_msgs[j].guid != NULL) { /* message expunged from src */ test_assert(event->type == LAST_MSG_TYPE_EXPUNGE); test_assert(memcmp(event->mailbox.guid, box->box.guid.guid, MAILBOX_GUID_SIZE) == 0); test_assert(event->msg.uid == box->dest_msgs[j].uid); j++; event++; } *idx_r = i; *eventsp = event; } static void test_dsync_brain_verify_mailbox(const struct test_dsync_mailbox *box, const struct test_dsync_msg_event **eventsp) { const struct test_dsync_msg_event *event = *eventsp; const struct test_dsync_mailbox *box2; const struct dsync_message *msgs, *msg2; unsigned int i = 0; if (box->src_msgs == NULL) return; msgs = box->src_msgs; if (box->dest_msgs != NULL) { /* sync existing messages */ test_dsync_brain_verify_existing(box, &event, &i); } /* sync new messages */ for (; msgs[i].guid != NULL; i++) { test_assert(event->msg.guid != NULL); test_assert(memcmp(event->mailbox.guid, box->box.guid.guid, MAILBOX_GUID_SIZE) == 0); if (test_dsync_msg_find_guid(box, &msgs[i], &box2, &msg2)) { test_assert(event->type == LAST_MSG_TYPE_COPY); test_assert(memcmp(event->copy_src_mailbox.guid, box2->box.guid.guid, MAILBOX_GUID_SIZE) == 0); test_assert(event->copy_src_uid == msg2->uid); } else { test_assert(event->type == LAST_MSG_TYPE_SAVE); test_assert(strcmp(event->save_body, "hdr\n\nbody") == 0); } test_assert(strcmp(event->msg.guid, msgs[i].guid) == 0); test_assert(event->msg.uid == msgs[i].uid); test_assert(event->msg.flags == msgs[i].flags); test_assert(dsync_keyword_list_equals(event->msg.keywords, msgs[i].keywords)); test_assert(event->msg.modseq == msgs[i].modseq); test_assert(event->msg.save_date == msgs[i].save_date); event++; } *eventsp = event; } static void test_dsync_brain_verify_msg_events(struct test_dsync_worker *dest_test_worker) { ARRAY_DEFINE(msg_events, struct test_dsync_msg_event); const struct test_dsync_msg_event *events, *events_end; struct test_dsync_msg_event msg_event; unsigned int i, event_count; /* get events and sort them so we can easily check if they're correct */ t_array_init(&msg_events, 64); while (test_dsync_worker_next_msg_event(dest_test_worker, &msg_event)) array_append(&msg_events, &msg_event, 1); array_sort(&msg_events, test_dsync_msg_event_cmp); events = array_get(&msg_events, &event_count); events_end = events + event_count; for (i = 0; mailboxes[i].box.name != NULL; i++) { if ((mailboxes[i].dest_flags & FLAG_UNCHANGED) == 0) test_dsync_brain_verify_mailbox(&mailboxes[i], &events); } test_assert(events == events_end); } static void test_dsync_brain_run(const struct test_dsync_mailbox *test_mailboxes, void (*verify_func)(struct test_dsync_worker *)) { struct dsync_brain *brain; struct dsync_worker *src_worker, *dest_worker; struct test_dsync_worker *src_test_worker, *dest_test_worker; struct dsync_mailbox new_box; struct test_dsync_box_event box_event; unsigned int i, j, box_count; box_count = 0; while (test_mailboxes[box_count].box.name != NULL) box_count++; mailboxes = t_new(struct test_dsync_mailbox, box_count + 1); memcpy(mailboxes, test_mailboxes, sizeof(*mailboxes) * box_count); src_worker = dsync_worker_init_test(); dest_worker = dsync_worker_init_test(); src_test_worker = (struct test_dsync_worker *)src_worker; dest_test_worker = (struct test_dsync_worker *)dest_worker; brain = dsync_brain_init(src_worker, dest_worker); dsync_brain_sync(brain); /* have brain read the mailboxes */ for (i = 0; mailboxes[i].box.name != NULL; i++) { src_test_worker->box_iter.next_box = &mailboxes[i].box; src_worker->input_callback(src_worker->input_context); if ((mailboxes[i].dest_flags & FLAG_EXISTS) != 0) { if ((mailboxes[i].dest_flags & FLAG_UNCHANGED) == 0) mailboxes[i].box.highest_modseq++; dest_test_worker->box_iter.next_box = &mailboxes[i].box; dest_worker->input_callback(dest_worker->input_context); } } src_test_worker->box_iter.last = TRUE; src_worker->input_callback(src_worker->input_context); dest_test_worker->box_iter.last = TRUE; dest_worker->input_callback(dest_worker->input_context); /* check that it created missing mailboxes */ while (test_dsync_worker_next_box_event(dest_test_worker, &box_event)) { test_assert(box_event.type == LAST_BOX_TYPE_CREATE); test_assert(mailbox_find(box_event.box.name, &i)); test_assert(mailboxes[i].dest_flags == 0); mailboxes[i].dest_flags |= FLAG_CREATED; new_box = mailboxes[i].box; new_box.uid_next = 0; new_box.highest_modseq = 0; test_assert(dsync_mailboxes_equal(&box_event.box, &new_box)); } /* brain wants mailboxes in guid order. make things easier for us by sorting them now. */ qsort(mailboxes, box_count, sizeof(*mailboxes), test_dsync_mailbox_cmp); /* start syncing messages */ for (i = j = 0; mailboxes[i].box.name != NULL; i++) { if ((mailboxes[i].dest_flags & FLAG_UNCHANGED) != 0) continue; test_assert(memcmp(&dest_test_worker->msg_iter_mailboxes[j], mailboxes[i].box.guid.guid, MAILBOX_GUID_SIZE) == 0); j++; } test_assert(dest_test_worker->msg_iter_mailbox_count == j); test_dsync_sync_msgs(src_test_worker, FALSE); test_dsync_sync_msgs(dest_test_worker, TRUE); verify_func(dest_test_worker); dsync_worker_deinit(&src_worker); dsync_worker_deinit(&dest_worker); dsync_brain_deinit(&brain); } static void test_dsync_brain(void) { test_begin("dsync brain basics"); test_dsync_brain_run(basic_mailboxes, test_dsync_brain_verify_msg_events); test_end(); } static struct dsync_message conflict_src_msgs[] = { { "guid1", 1, 0, NULL, 1, 1 }, { "guid3", 3, 0, NULL, 1, 1 }, { "guid5", 5, 0, NULL, 1, 1 }, { "guidy", 6, 0, NULL, 1, 1 }, { "67ddfe2125de633c56e033b57c897018", 7, DSYNC_MAIL_FLAG_EXPUNGED, NULL, 1, 1 }, { "guidz", 8, DSYNC_MAIL_FLAG_EXPUNGED, NULL, 1, 1 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct dsync_message conflict_dest_msgs[] = { { "guid1", 1, 0, NULL, 1, 1 }, { "guid2", 2, 0, NULL, 1, 1 }, { "guidx", 3, 0, NULL, 1, 1 }, { "guid4", 4, 0, NULL, 1, 1 }, { "guid5", 5, 0, NULL, 1, 1 }, { "guid7", 7, 0, NULL, 1, 1 }, { "guid8", 8, 0, NULL, 1, 1 }, { NULL, 0, 0, NULL, 0, 0 } }; static struct test_dsync_mailbox conflict_mailboxes[] = { { { "box1", { { 0x12, 0x34, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x21, 0x43, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe } }, 1234567890, 4321, 888 }, conflict_src_msgs, conflict_dest_msgs, FLAG_EXISTS }, { { NULL, { { 0, } }, 0, 0, 0 }, NULL, NULL, 0 } }; static void test_dsync_brain_verify_uid_conflict(struct test_dsync_worker *dest_test_worker) { struct test_dsync_msg_event event; struct test_dsync_box_event box_event; test_assert(test_dsync_worker_next_msg_event(dest_test_worker, &event)); test_assert(event.type == LAST_MSG_TYPE_EXPUNGE); test_assert(event.msg.uid == 2); test_assert(test_dsync_worker_next_msg_event(dest_test_worker, &event)); test_assert(event.type == LAST_MSG_TYPE_EXPUNGE); test_assert(event.msg.uid == 7); test_assert(test_dsync_worker_next_msg_event(dest_test_worker, &event)); test_assert(event.type == LAST_MSG_TYPE_SAVE); test_assert(event.msg.uid == 4321); test_assert(strcmp(event.msg.guid, "guid3") == 0); test_assert(test_dsync_worker_next_msg_event(dest_test_worker, &event)); test_assert(event.type == LAST_MSG_TYPE_SAVE); test_assert(event.msg.uid == 6); test_assert(strcmp(event.msg.guid, "guidy") == 0); test_assert(test_dsync_worker_next_msg_event(dest_test_worker, &event)); test_assert(event.type == LAST_MSG_TYPE_UPDATE_UID); test_assert(event.msg.uid == 3); test_assert(test_dsync_worker_next_msg_event(dest_test_worker, &event)); test_assert(event.type == LAST_MSG_TYPE_UPDATE_UID); test_assert(event.msg.uid == 8); test_assert(!test_dsync_worker_next_msg_event(dest_test_worker, &event)); while (test_dsync_worker_next_box_event(dest_test_worker, &box_event) && box_event.type == LAST_BOX_TYPE_SELECT) ; test_assert(box_event.type == LAST_BOX_TYPE_UPDATE); test_assert(box_event.box.uid_next == 4322); test_assert(box_event.box.uid_validity == 1234567890); test_assert(box_event.box.highest_modseq == 888); while (test_dsync_worker_next_box_event(dest_test_worker, &box_event)) test_assert(box_event.type == LAST_BOX_TYPE_SELECT); } static void test_dsync_brain_uid_conflict(void) { test_begin("dsync brain uid conflict"); test_dsync_brain_run(conflict_mailboxes, test_dsync_brain_verify_uid_conflict); test_end(); } int main(void) { static void (*test_functions[])(void) = { test_dsync_brain, test_dsync_brain_uid_conflict, NULL }; return test_run(test_functions); }