1 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "env-util.h"
6 #include "test-common.h"
7 #include "mail-index-private.h"
8 #include "mail-index-transaction-private.h"
9 
10 #include <time.h>
11 
12 static struct mail_index_header hdr;
13 static struct mail_index_record rec;
14 
15 const struct mail_index_header *
mail_index_get_header(struct mail_index_view * view ATTR_UNUSED)16 mail_index_get_header(struct mail_index_view *view ATTR_UNUSED)
17 {
18 	return &hdr;
19 }
20 
21 const struct mail_index_record *
mail_index_lookup(struct mail_index_view * view ATTR_UNUSED,uint32_t seq ATTR_UNUSED)22 mail_index_lookup(struct mail_index_view *view ATTR_UNUSED,
23 		  uint32_t seq ATTR_UNUSED)
24 {
25 	return &rec;
26 }
27 
mail_index_lookup_keywords(struct mail_index_view * view ATTR_UNUSED,uint32_t seq ATTR_UNUSED,ARRAY_TYPE (keyword_indexes)* keyword_idx ATTR_UNUSED)28 void mail_index_lookup_keywords(struct mail_index_view *view ATTR_UNUSED,
29 				uint32_t seq ATTR_UNUSED,
30 				ARRAY_TYPE(keyword_indexes) *keyword_idx ATTR_UNUSED)
31 {
32 	array_clear(keyword_idx);
33 }
34 
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)35 bool mail_index_map_get_ext_idx(struct mail_index_map *map ATTR_UNUSED,
36 				uint32_t ext_id ATTR_UNUSED,
37 				uint32_t *idx_r ATTR_UNUSED)
38 {
39 	return FALSE;
40 }
41 
mail_index_view_get_messages_count(struct mail_index_view * view ATTR_UNUSED)42 uint32_t mail_index_view_get_messages_count(struct mail_index_view *view ATTR_UNUSED)
43 {
44 	return hdr.messages_count;
45 }
46 
mail_index_transaction_lookup_latest_keywords(struct mail_index_transaction * t ATTR_UNUSED,uint32_t seq ATTR_UNUSED,ARRAY_TYPE (keyword_indexes)* keywords ATTR_UNUSED)47 void mail_index_transaction_lookup_latest_keywords(struct mail_index_transaction *t ATTR_UNUSED,
48 						   uint32_t seq ATTR_UNUSED,
49 						   ARRAY_TYPE(keyword_indexes) *keywords ATTR_UNUSED)
50 {
51 }
52 
53 struct mail_keywords *
mail_index_keywords_create_from_indexes(struct mail_index * index ATTR_UNUSED,const ARRAY_TYPE (keyword_indexes)* keyword_indexes ATTR_UNUSED)54 mail_index_keywords_create_from_indexes(struct mail_index *index ATTR_UNUSED,
55 					const ARRAY_TYPE(keyword_indexes)
56 						*keyword_indexes ATTR_UNUSED)
57 {
58 	return NULL;
59 }
60 
mail_index_keywords_unref(struct mail_keywords ** keywords ATTR_UNUSED)61 void mail_index_keywords_unref(struct mail_keywords **keywords ATTR_UNUSED)
62 {
63 }
64 
65 static struct mail_index_transaction *
mail_index_transaction_new(void)66 mail_index_transaction_new(void)
67 {
68 	struct mail_index_transaction *t;
69 
70 	t = t_new(struct mail_index_transaction, 1);
71 	t->first_new_seq = hdr.messages_count + 1;
72 	return t;
73 }
mail_index_transaction_cleanup(struct mail_index_transaction * t)74 static void mail_index_transaction_cleanup(struct mail_index_transaction *t)
75 {
76 	if (array_is_created(&t->appends))
77 		array_free(&t->appends);
78 	if (array_is_created(&t->updates))
79 		array_free(&t->updates);
80 	if (array_is_created(&t->modseq_updates))
81 		array_free(&t->modseq_updates);
82 	if (array_is_created(&t->expunges))
83 		array_free(&t->expunges);
84 }
85 
test_mail_index_append(void)86 static void test_mail_index_append(void)
87 {
88 	struct mail_index_transaction *t;
89 	const struct mail_index_record *appends;
90 	ARRAY_TYPE(seq_range) saved_uids_arr;
91 	const struct seq_range *saved_uids;
92 	unsigned int count;
93 	uint32_t seq;
94 
95 	hdr.messages_count = 4;
96 	t = mail_index_transaction_new();
97 
98 	test_begin("mail index append");
99 	mail_index_append(t, 0, &seq);
100 	test_assert(t->log_updates);
101 	test_assert(seq == 5);
102 	mail_index_append(t, 0, &seq);
103 	test_assert(seq == 6);
104 	test_assert(!t->appends_nonsorted);
105 
106 	t_array_init(&saved_uids_arr, 128);
107 	mail_index_append_finish_uids(t, 123, &saved_uids_arr);
108 	saved_uids = array_get(&saved_uids_arr, &count);
109 	test_assert(count == 1);
110 	test_assert(saved_uids[0].seq1 == 123 && saved_uids[0].seq2 == 124);
111 
112 	appends = array_get(&t->appends, &count);
113 	test_assert(appends[0].uid == 123);
114 	test_assert(appends[0].flags == 0);
115 	test_assert(appends[1].uid == 124);
116 	test_assert(appends[1].flags == 0);
117 	test_end();
118 	mail_index_transaction_cleanup(t);
119 
120 	/* test with some uids */
121 	t = mail_index_transaction_new();
122 
123 	test_begin("mail index append with uids");
124 	mail_index_append(t, 0, &seq);
125 	test_assert(seq == 5);
126 	mail_index_append(t, 126, &seq);
127 	test_assert(seq == 6);
128 	test_assert(!t->appends_nonsorted);
129 	mail_index_append(t, 124, &seq);
130 	test_assert(seq == 7);
131 	test_assert(t->appends_nonsorted);
132 	mail_index_append(t, 0, &seq);
133 	test_assert(seq == 8);
134 	mail_index_append(t, 128, &seq);
135 	test_assert(seq == 9);
136 	test_assert(t->highest_append_uid == 128);
137 
138 	mail_index_append_finish_uids(t, 125, &saved_uids_arr);
139 	saved_uids = array_get(&saved_uids_arr, &count);
140 	test_assert(count == 4);
141 	test_assert(saved_uids[0].seq1 == 129 && saved_uids[0].seq2 == 129);
142 	test_assert(saved_uids[1].seq1 == 126 && saved_uids[1].seq2 == 126);
143 	test_assert(saved_uids[2].seq1 == 130 && saved_uids[2].seq2 == 131);
144 	test_assert(saved_uids[3].seq1 == 128 && saved_uids[3].seq2 == 128);
145 
146 	appends = array_get(&t->appends, &count);
147 	test_assert(count == 5);
148 	test_assert(appends[0].uid == 129);
149 	test_assert(appends[1].uid == 126);
150 	test_assert(appends[2].uid == 130);
151 	test_assert(appends[3].uid == 131);
152 	test_assert(appends[4].uid == 128);
153 	test_end();
154 
155 	mail_index_transaction_cleanup(t);
156 }
157 
test_mail_index_flag_update_fastpath(void)158 static void test_mail_index_flag_update_fastpath(void)
159 {
160 	struct mail_index_transaction *t;
161 	const struct mail_index_flag_update *updates;
162 	unsigned int count;
163 
164 	hdr.messages_count = 20;
165 	t = mail_index_transaction_new();
166 
167 	test_begin("mail index flag update fast paths");
168 
169 	mail_index_update_flags_range(t, 13, 14, MODIFY_REPLACE,
170 				      MAIL_DELETED);
171 	test_assert(t->last_update_idx == 0);
172 	test_assert(array_count(&t->updates) == 1);
173 
174 	mail_index_update_flags_range(t, 15, 15, MODIFY_REPLACE,
175 				      MAIL_DELETED);
176 	test_assert(t->last_update_idx == 0);
177 	test_assert(array_count(&t->updates) == 1);
178 
179 	mail_index_update_flags_range(t, 16, 16, MODIFY_ADD,
180 				      MAIL_DELETED);
181 	test_assert(t->last_update_idx == 1);
182 	test_assert(array_count(&t->updates) == 2);
183 
184 	updates = array_get(&t->updates, &count);
185 	test_assert(updates[0].uid1 == 13);
186 	test_assert(updates[0].uid2 == 15);
187 	test_assert(updates[0].add_flags == MAIL_DELETED);
188 	test_assert(updates[0].remove_flags ==
189 		    (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_SEEN | MAIL_DRAFT));
190 	test_assert(updates[1].uid1 == 16);
191 	test_assert(updates[1].uid2 == 16);
192 	test_assert(updates[1].add_flags == MAIL_DELETED);
193 	test_assert(updates[1].remove_flags == 0);
194 	test_assert(!t->log_updates);
195 	test_end();
196 
197 	mail_index_transaction_cleanup(t);
198 }
199 
test_mail_index_flag_update_simple_merges(void)200 static void test_mail_index_flag_update_simple_merges(void)
201 {
202 	struct mail_index_transaction *t;
203 	const struct mail_index_flag_update *updates;
204 	unsigned int count;
205 
206 	hdr.messages_count = 20;
207 	t = mail_index_transaction_new();
208 
209 	test_begin("mail index flag update simple merges");
210 
211 	mail_index_update_flags_range(t, 6, 8, MODIFY_ADD,
212 				      MAIL_FLAGGED);
213 	test_assert(t->last_update_idx == 0);
214 	mail_index_update_flags_range(t, 5, 6, MODIFY_ADD,
215 				      MAIL_FLAGGED);
216 	test_assert(t->last_update_idx == 0);
217 	mail_index_update_flags_range(t, 4, 4, MODIFY_ADD,
218 				      MAIL_FLAGGED);
219 	test_assert(t->last_update_idx == 0);
220 	mail_index_update_flags_range(t, 7, 9, MODIFY_ADD,
221 				      MAIL_FLAGGED);
222 	test_assert(t->last_update_idx == 0);
223 	mail_index_update_flags_range(t, 10, 10, MODIFY_ADD,
224 				      MAIL_FLAGGED);
225 	updates = array_get(&t->updates, &count);
226 	test_assert(count == 1);
227 	test_assert(updates[0].uid1 == 4);
228 	test_assert(updates[0].uid2 == 10);
229 	test_assert(updates[0].add_flags == MAIL_FLAGGED);
230 	test_assert(updates[0].remove_flags == 0);
231 
232 	mail_index_update_flags_range(t, 12, 12, MODIFY_ADD,
233 				      MAIL_FLAGGED);
234 	mail_index_update_flags_range(t, 11, 11, MODIFY_ADD,
235 				      MAIL_FLAGGED);
236 	updates = array_get(&t->updates, &count);
237 	test_assert(count == 1);
238 	test_assert(updates[0].uid1 == 4);
239 	test_assert(updates[0].uid2 == 12);
240 	test_end();
241 
242 	mail_index_transaction_cleanup(t);
243 }
244 
test_mail_index_flag_update_complex_merges(void)245 static void test_mail_index_flag_update_complex_merges(void)
246 {
247 	struct mail_index_transaction *t;
248 	const struct mail_index_flag_update *updates;
249 	unsigned int count;
250 
251 	hdr.messages_count = 20;
252 	t = mail_index_transaction_new();
253 
254 	test_begin("mail index flag update complex merges");
255 
256 	mail_index_update_flags_range(t, 6, 8, MODIFY_REPLACE,
257 				      MAIL_SEEN);
258 	mail_index_update_flags_range(t, 3, 6, MODIFY_ADD,
259 				      MAIL_FLAGGED);
260 	mail_index_update_flags_range(t, 5, 7, MODIFY_ADD,
261 				      MAIL_DRAFT);
262 	mail_index_update_flags_range(t, 6, 6, MODIFY_REPLACE,
263 				      MAIL_SEEN | MAIL_ANSWERED);
264 	mail_index_update_flags_range(t, 5, 10, MODIFY_REMOVE,
265 				      MAIL_ANSWERED);
266 	mail_index_update_flags_range(t, 7, 12, MODIFY_ADD,
267 				      MAIL_DELETED);
268 
269 	updates = array_get(&t->updates, &count);
270 	test_assert(count == 7);
271 	test_assert(updates[0].uid1 == 3);
272 	test_assert(updates[0].uid2 == 4);
273 	test_assert(updates[0].add_flags == MAIL_FLAGGED);
274 	test_assert(updates[0].remove_flags == 0);
275 	test_assert(updates[1].uid1 == 5);
276 	test_assert(updates[1].uid2 == 5);
277 	test_assert(updates[1].add_flags == (MAIL_DRAFT | MAIL_FLAGGED));
278 	test_assert(updates[1].remove_flags == MAIL_ANSWERED);
279 	test_assert(updates[2].uid1 == 6);
280 	test_assert(updates[2].uid2 == 6);
281 	test_assert(updates[2].add_flags == MAIL_SEEN);
282 	test_assert(updates[2].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_DELETED | MAIL_DRAFT));
283 	test_assert(updates[3].uid1 == 7);
284 	test_assert(updates[3].uid2 == 7);
285 	test_assert(updates[3].add_flags == (MAIL_SEEN | MAIL_DRAFT | MAIL_DELETED));
286 	test_assert(updates[3].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED));
287 	test_assert(updates[4].uid1 == 8);
288 	test_assert(updates[4].uid2 == 8);
289 	test_assert(updates[4].add_flags == (MAIL_SEEN | MAIL_DELETED));
290 	test_assert(updates[4].remove_flags == (MAIL_ANSWERED | MAIL_FLAGGED | MAIL_DRAFT));
291 	test_assert(updates[5].uid1 == 9);
292 	test_assert(updates[5].uid2 == 10);
293 	test_assert(updates[5].add_flags == MAIL_DELETED);
294 	test_assert(updates[5].remove_flags == MAIL_ANSWERED);
295 	test_assert(updates[6].uid1 == 11);
296 	test_assert(updates[6].uid2 == 12);
297 	test_assert(updates[6].add_flags == MAIL_DELETED);
298 	test_assert(updates[6].remove_flags == 0);
299 
300 	test_end();
301 
302 	mail_index_transaction_cleanup(t);
303 }
304 
305 static void
flags_array_check(struct mail_index_transaction * t,const enum mail_flags * flags,unsigned int msg_count)306 flags_array_check(struct mail_index_transaction *t,
307 		  const enum mail_flags *flags, unsigned int msg_count)
308 {
309 	const struct mail_index_flag_update *updates;
310 	unsigned int i, count, seq;
311 
312 	if (array_is_created(&t->updates))
313 		updates = array_get(&t->updates, &count);
314 	else {
315 		updates = NULL;
316 		count = 0;
317 	}
318 	for (seq = 1, i = 0; i < count; i++) {
319 		if (i > 0) {
320 			test_assert(updates[i-1].uid2 < updates[i].uid1);
321 			test_assert(updates[i-1].uid2 + 1 != updates[i].uid1 ||
322 				    updates[i-1].add_flags != updates[i].add_flags ||
323 				    updates[i-1].remove_flags != updates[i].remove_flags);
324 		}
325 		for (; seq != updates[i].uid1; seq++)
326 			test_assert(flags[seq] == 0);
327 		for (; seq <= updates[i].uid2; seq++)
328 			test_assert(flags[seq] == updates[i].add_flags);
329 	}
330 	for (; seq <= msg_count; seq++)
331 		test_assert(flags[seq] == 0);
332 }
333 
test_mail_index_flag_update_random(void)334 static void test_mail_index_flag_update_random(void)
335 {
336 	struct mail_index_transaction *t;
337 	unsigned int r, seq1, seq2, seq;
338 	enum mail_flags *flags, change;
339 	enum modify_type modify_type;
340 
341 	hdr.messages_count = 20;
342 	t = mail_index_transaction_new();
343 
344 	test_begin("mail index flag update random");
345 
346 	flags = t_new(enum mail_flags, hdr.messages_count + 1);
347 	for (r = 0; r < 1000; r++) {
348 		change = i_rand_limit(MAIL_FLAGS_NONRECENT + 1);
349 		seq1 = i_rand_minmax(1, hdr.messages_count);
350 		seq2 = seq1 == hdr.messages_count ? seq1 :
351 			i_rand_minmax(seq1, hdr.messages_count);
352 
353 		switch (i_rand_limit(3)) {
354 		case 0:
355 			modify_type = MODIFY_ADD;
356 			for (seq = seq1; seq <= seq2; seq++)
357 				flags[seq] |= change;
358 			break;
359 		case 1:
360 			modify_type = MODIFY_REMOVE;
361 			for (seq = seq1; seq <= seq2; seq++)
362 				flags[seq] &= ENUM_NEGATE(change);
363 			break;
364 		case 2:
365 			modify_type = MODIFY_REPLACE;
366 			for (seq = seq1; seq <= seq2; seq++)
367 				flags[seq] = change;
368 			break;
369 		default:
370 			i_unreached();
371 		}
372 		mail_index_update_flags_range(t, seq1, seq2, modify_type,
373 					      change);
374 		flags_array_check(t, flags, hdr.messages_count);
375 	}
376 	test_end();
377 
378 	mail_index_transaction_cleanup(t);
379 }
380 
test_mail_index_cancel_flag_updates(void)381 static void test_mail_index_cancel_flag_updates(void)
382 {
383 	struct mail_index_transaction *t;
384 	const struct mail_index_flag_update *updates;
385 	unsigned int count;
386 
387 	hdr.messages_count = 20;
388 	t = mail_index_transaction_new();
389 
390 	test_begin("mail index cancel flag updates");
391 
392 	mail_index_update_flags_range(t, 5, 7, MODIFY_REPLACE, 0);
393 	updates = array_get(&t->updates, &count);
394 	test_assert(count == 1);
395 	test_assert(updates[0].uid1 == 5 && updates[0].uid2 == 7);
396 	test_assert(mail_index_cancel_flag_updates(t, 5));
397 	test_assert(updates[0].uid1 == 6 && updates[0].uid2 == 7);
398 	test_assert(mail_index_cancel_flag_updates(t, 7));
399 	test_assert(updates[0].uid1 == 6 && updates[0].uid2 == 6);
400 	test_assert(mail_index_cancel_flag_updates(t, 6));
401 	test_assert(!array_is_created(&t->updates));
402 
403 	mail_index_update_flags_range(t, 5, 7, MODIFY_REPLACE, 0);
404 	test_assert(mail_index_cancel_flag_updates(t, 6));
405 	updates = array_get(&t->updates, &count);
406 	test_assert(count == 2);
407 	test_assert(updates[0].uid1 == 5 && updates[0].uid2 == 5);
408 	test_assert(updates[1].uid1 == 7 && updates[1].uid2 == 7);
409 
410 	test_end();
411 
412 	mail_index_transaction_cleanup(t);
413 }
414 
test_mail_index_flag_update_appends(void)415 static void test_mail_index_flag_update_appends(void)
416 {
417 	struct mail_index_transaction *t;
418 	const struct mail_index_record *appends;
419 	const struct mail_index_flag_update *updates;
420 	unsigned int count;
421 	uint32_t seq;
422 
423 	hdr.messages_count = 4;
424 	t = mail_index_transaction_new();
425 
426 	test_begin("mail index flag update appends");
427 	mail_index_append(t, 0, &seq);
428 	test_assert(seq == 5);
429 	mail_index_append(t, 0, &seq);
430 	test_assert(seq == 6);
431 	mail_index_append(t, 0, &seq);
432 	test_assert(seq == 7);
433 
434 	mail_index_update_flags_range(t, 5, 6, MODIFY_REPLACE,
435 				      MAIL_SEEN | MAIL_FLAGGED);
436 	mail_index_update_flags_range(t, 6, 7, MODIFY_ADD,
437 				      MAIL_DRAFT | MAIL_FLAGGED);
438 	mail_index_update_flags_range(t, 5, 7, MODIFY_REMOVE,
439 				      MAIL_FLAGGED);
440 
441 	appends = array_get(&t->appends, &count);
442 	test_assert(count == 3);
443 	test_assert(appends[0].flags == MAIL_SEEN);
444 	test_assert(appends[1].flags == (MAIL_SEEN | MAIL_DRAFT));
445 	test_assert(appends[2].flags == MAIL_DRAFT);
446 
447 	/* mixed existing/appends */
448 	mail_index_update_flags_range(t, 4, 5, MODIFY_ADD,
449 				      MAIL_ANSWERED);
450 	test_assert(appends[0].flags == (MAIL_SEEN | MAIL_ANSWERED));
451 
452 	updates = array_get(&t->updates, &count);
453 	test_assert(count == 1);
454 	test_assert(updates[0].uid1 == 4);
455 	test_assert(updates[0].uid2 == 4);
456 	test_assert(updates[0].add_flags == MAIL_ANSWERED);
457 	test_end();
458 
459 	mail_index_transaction_cleanup(t);
460 }
461 
test_flag_update_pos(struct mail_index_transaction * t,uint32_t seq,unsigned int idx)462 static bool test_flag_update_pos(struct mail_index_transaction *t,
463 				 uint32_t seq, unsigned int idx)
464 {
465 	unsigned int i, j, count;
466 
467 	count = array_count(&t->updates);
468 	for (i = 0; i < idx; i++) {
469 		for (j = idx + 1; j <= count; j++) {
470 			if (mail_index_transaction_get_flag_update_pos(t, i, j, seq) != idx) {
471 				test_assert(FALSE);
472 				return FALSE;
473 			}
474 		}
475 	}
476 	return TRUE;
477 }
478 
test_mail_index_transaction_get_flag_update_pos(void)479 static void test_mail_index_transaction_get_flag_update_pos(void)
480 {
481 	struct mail_index_transaction *t;
482 
483 	test_begin("mail index transaction get flag update pos");
484 
485 	hdr.messages_count = 10;
486 	t = mail_index_transaction_new();
487 	mail_index_update_flags_range(t, 1, 1, MODIFY_REPLACE, 0);
488 	mail_index_update_flags_range(t, 3, 4, MODIFY_REPLACE, 0);
489 	mail_index_update_flags_range(t, 6, 7, MODIFY_REPLACE, 0);
490 	mail_index_update_flags_range(t, 9, 10, MODIFY_REPLACE, 0);
491 
492 	test_assert(test_flag_update_pos(t, 1, 0));
493 	test_assert(test_flag_update_pos(t, 2, 1));
494 	test_assert(test_flag_update_pos(t, 3, 1));
495 	test_assert(test_flag_update_pos(t, 4, 1));
496 	test_assert(test_flag_update_pos(t, 5, 2));
497 	test_assert(test_flag_update_pos(t, 6, 2));
498 	test_assert(test_flag_update_pos(t, 7, 2));
499 	test_assert(test_flag_update_pos(t, 8, 3));
500 	test_assert(test_flag_update_pos(t, 9, 3));
501 	test_assert(test_flag_update_pos(t, 10, 3));
502 	test_assert(test_flag_update_pos(t, 11, 4));
503 	test_assert(test_flag_update_pos(t, 12, 4));
504 	test_end();
505 
506 	mail_index_transaction_cleanup(t);
507 }
508 
test_mail_index_modseq_update(void)509 static void test_mail_index_modseq_update(void)
510 {
511 	struct mail_index_transaction *t;
512 	const struct mail_transaction_modseq_update *ups;
513 	unsigned int count;
514 
515 	test_begin("mail index modseq update");
516 
517 	hdr.messages_count = 10;
518 	t = mail_index_transaction_new();
519 
520 	mail_index_update_modseq(t, 4, 0x8234fefa02747429ULL);
521 	mail_index_update_modseq(t, 6, 0x1234567890abcdefULL);
522 	mail_index_update_modseq(t, 2, 0xfeed);
523 	mail_index_update_modseq(t, 4, 2);
524 	/* modseq=1 updates are ignored: */
525 	mail_index_update_modseq(t, 5, 1);
526 	mail_index_update_modseq(t, 6, 1);
527 
528 	ups = array_get(&t->modseq_updates, &count);
529 	test_assert(count == 4);
530 	test_assert(ups[0].uid == 4 &&
531 		    ups[0].modseq_high32 == 0x8234fefa &&
532 		    ups[0].modseq_low32 == 0x02747429);
533 	test_assert(ups[1].uid == 6 &&
534 		    ups[1].modseq_high32 == 0x12345678 &&
535 		    ups[1].modseq_low32 == 0x90abcdef);
536 	test_assert(ups[2].uid == 2 &&
537 		    ups[2].modseq_high32 == 0 &&
538 		    ups[2].modseq_low32 == 0xfeed);
539 	test_assert(ups[3].uid == 4 &&
540 		    ups[3].modseq_high32 == 0 &&
541 		    ups[3].modseq_low32 == 2);
542 	test_end();
543 
544 	mail_index_transaction_cleanup(t);
545 }
546 
test_mail_index_expunge(void)547 static void test_mail_index_expunge(void)
548 {
549 	static guid_128_t empty_guid = { 0, };
550 	struct mail_index_transaction *t;
551 	const struct mail_transaction_expunge_guid *expunges;
552 	guid_128_t guid2, guid3, guid4;
553 	unsigned int i, count;
554 
555 	test_begin("mail index expunge");
556 
557 	hdr.messages_count = 10;
558 	t = mail_index_transaction_new();
559 	for (i = 0; i < sizeof(guid2); i++) {
560 		guid2[i] = i + 1;
561 		guid3[i] = i ^ 0xff;
562 		guid4[i] = i + 0x80;
563 	}
564 
565 	mail_index_expunge_guid(t, 4, guid4);
566 	test_assert(!t->expunges_nonsorted);
567 	mail_index_expunge_guid(t, 2, guid2);
568 	test_assert(t->expunges_nonsorted);
569 	mail_index_expunge_guid(t, 3, guid3);
570 	mail_index_expunge(t, 1);
571 	mail_index_expunge(t, 5);
572 
573 	expunges = array_get(&t->expunges, &count);
574 	test_assert(count == 5);
575 	test_assert(expunges[0].uid == 4);
576 	test_assert(memcmp(expunges[0].guid_128, guid4, sizeof(guid4)) == 0);
577 	test_assert(expunges[1].uid == 2);
578 	test_assert(memcmp(expunges[1].guid_128, guid2, sizeof(guid2)) == 0);
579 	test_assert(expunges[2].uid == 3);
580 	test_assert(memcmp(expunges[2].guid_128, guid3, sizeof(guid3)) == 0);
581 	test_assert(expunges[3].uid == 1);
582 	test_assert(memcmp(expunges[3].guid_128, empty_guid, sizeof(empty_guid)) == 0);
583 	test_assert(expunges[4].uid == 5);
584 	test_assert(memcmp(expunges[4].guid_128, empty_guid, sizeof(empty_guid)) == 0);
585 
586 	test_end();
587 
588 	mail_index_transaction_cleanup(t);
589 }
590 
test_mail_index_update_day_first_uid(void)591 static void test_mail_index_update_day_first_uid(void)
592 {
593 	struct {
594 		uint32_t now;
595 		uint32_t old_day_stamp;
596 		uint32_t new_day_stamp;
597 		uint32_t new_day_first_uid[8];
598 	} tests[] = {
599 		/* 1487116800 = 2017-02-15 00:00:00 UTC */
600 		{ 1487116800, 1487116800, 1487116800, { 8, 7, 6, 5, 4, 3, 2, 1 } },
601 		/* still same day */
602 		{ 1487116800+3600*24-1, 1487116800, 1487116800, { 8, 7, 6, 5, 4, 3, 2, 1 } },
603 		/* one day earlier */
604 		{ 1487116800-1, 1487116800, 1487116800, { 8, 7, 6, 5, 4, 3, 2, 1 } },
605 		/* next day */
606 		{ 1487116800+3600*24, 1487116800, 1487116800+3600*24, { 9, 8, 7, 6, 5, 4, 3, 2 } },
607 		{ 1487116800+3600*24*2-1, 1487116800, 1487116800+3600*24, { 9, 8, 7, 6, 5, 4, 3, 2 } },
608 		/* 2 days */
609 		{ 1487116800+3600*24*2, 1487116800, 1487116800+3600*24*2, { 9, 8, 8, 7, 6, 5, 4, 3 } },
610 		/* 3 days */
611 		{ 1487116800+3600*24*3, 1487116800, 1487116800+3600*24*3, { 9, 8, 8, 8, 7, 6, 5, 4 } },
612 		/* 4 days */
613 		{ 1487116800+3600*24*4, 1487116800, 1487116800+3600*24*4, { 9, 8, 8, 8, 8, 7, 6, 5 } },
614 		/* 5 days */
615 		{ 1487116800+3600*24*5, 1487116800, 1487116800+3600*24*5, { 9, 8, 8, 8, 8, 8, 7, 6 } },
616 		/* 6 days */
617 		{ 1487116800+3600*24*6, 1487116800, 1487116800+3600*24*6, { 9, 8, 8, 8, 8, 8, 8, 7 } },
618 		/* 7 days */
619 		{ 1487116800+3600*24*7, 1487116800, 1487116800+3600*24*7, { 9, 8, 8, 8, 8, 8, 8, 8 } },
620 		/* 8 days */
621 		{ 1487116800+3600*24*8, 1487116800, 1487116800+3600*24*8, { 9, 8, 8, 8, 8, 8, 8, 8 } },
622 		/* 366 days */
623 		{ 1487116800+3600*24*366, 1487116800, 1487116800+3600*24*366, { 9, 8, 8, 8, 8, 8, 8, 8 } },
624 	};
625 	struct mail_index_transaction *t;
626 	struct mail_index_record *rec;
627 	unsigned int i, j;
628 
629 	test_begin("mail index update day first uid");
630 
631 	/* daylight savings times were confusing these tests, so we'll now
632 	   just assume that TZ=UTC */
633 	test_assert(timezone == 0);
634 
635 	hdr.messages_count = 10;
636 	t = mail_index_transaction_new();
637 	t->view = t_new(struct mail_index_view, 1);
638 	t->view->map = t_new(struct mail_index_map, 1);
639 
640 	t_array_init(&t->appends, 1);
641 	rec = array_append_space(&t->appends);
642 	rec->uid = 9;
643 
644 	for (i = 0; i < N_ELEMENTS(tests); i++) {
645 		i_zero(&hdr);
646 		for (j = 0; j < N_ELEMENTS(hdr.day_first_uid); j++)
647 			hdr.day_first_uid[j] = 8-j;
648 		hdr.day_stamp = tests[i].old_day_stamp + timezone;
649 		memcpy(t->post_hdr_change, &hdr, sizeof(hdr));
650 		mail_index_update_day_headers(t, tests[i].now + timezone);
651 
652 		struct mail_index_header new_hdr;
653 		memcpy(&new_hdr, t->post_hdr_change, sizeof(new_hdr));
654 		test_assert_idx(new_hdr.day_stamp == tests[i].new_day_stamp + timezone, i);
655 		test_assert_idx(memcmp(new_hdr.day_first_uid,
656 				       tests[i].new_day_first_uid,
657 				       sizeof(uint32_t) * 8) == 0, i);
658 	}
659 
660 	test_end();
661 }
662 
main(void)663 int main(void)
664 {
665 	static void (*const test_functions[])(void) = {
666 		test_mail_index_append,
667 		test_mail_index_flag_update_fastpath,
668 		test_mail_index_flag_update_simple_merges,
669 		test_mail_index_flag_update_complex_merges,
670 		test_mail_index_flag_update_random,
671 		test_mail_index_flag_update_appends,
672 		test_mail_index_cancel_flag_updates,
673 		test_mail_index_transaction_get_flag_update_pos,
674 		test_mail_index_modseq_update,
675 		test_mail_index_expunge,
676 		test_mail_index_update_day_first_uid,
677 		NULL
678 	};
679 	/* daylight saving time confuses things */
680 	env_put("TZ", "UTC");
681 	tzset();
682 	return test_run(test_functions);
683 }
684