add changeinfo
[libmaildir.git] / test / maildir.myr
blob8b8bd5c603b3673977f55d672cec3b9a282181f3
1 use fileutil
2 use std
3 use sys
4 use testr
6 use maildir
8 var testroot : byte[:]
10 const main = {
11         testroot = ""
12         match std.mkdtemp("test_root_for_libmaildir", 0o700)
13         | `std.Ok p:
14                 testroot = p
15         | `std.Err e:
16                 testr.run([
17                         [.name="setup", .fn = {c : testr.ctx#
18                                 testr.fail(c, "mktemp failed: {}", e)
19                         }]
20                 ][:])
21                 std.exit(0)
22         ;;
24         if !std.chdir(testroot)
25                 testr.run([
26                         [.name="setup", .fn = {c : testr.ctx#
27                                 testr.fail(c, "chdir({}) failed", testroot)
28                         }]
29                 ][:])
30                 std.exit(0)
31         ;;
33         testr.run([
34                 [.name="changeinfo01", .fn = changeinfo01],
36                 [.name="cleanfs01", .fn = cleanfs01],
38                 [.name="info01", .fn = info01],
40                 [.name="infofmt01", .fn = infofmt01],
42                 [.name="maildirs01", .fn = maildirs01],
43                 [.name="maildirs02", .fn = maildirs02],
44                 [.name="maildirs03", .fn = maildirs03],
46                 [.name="messages01", .fn = messages01],
47                 [.name="messages02", .fn = messages02],
49                 [.name="movemessage01", .fn = movemessage01],
50         ][:])
52         /* We don't remove testroot. It shouldn't be a concern */
53         std.slfree(testroot)
54         testroot = ""
57 /* Utility functions */
58 const settimes = {c : testr.ctx#, p : byte[:][:], t : uint64
59         var tv :  sys.timeval = [ .sec = t ]
60         var tvs : sys.timeval[2] = [ tv, tv ]
61         var zt : byte[:]
62         for q : p
63                 zt = std.sldup(q)
64                 std.slpush(&zt, (0 : byte))
66                 match sys.utimes(&zt[0], &tvs[0])
67                 | 0:
68                 | e:
69                         testr.fail(c, "cannot set time of {}: {}", q, e)
70                         -> false
71                 ;;
73                 std.slfree(zt)
74         ;;
76         -> true
79 const tree = {p : byte[:][:] -> std.result(byte[:][:], byte[:])
80         var r : byte[:][:] = std.slalloc(0)
82         for q : p
83                 for f : fileutil.bywalk(q)
84                         std.slpush(&r, std.sldup(f))
85                 ;;
86         ;;
88         -> `std.Ok std.sort(r, std.strcmp)
91 const mkpaths = {c : testr.ctx#, p : byte[:][:] -> bool
92         for q : p
93                 match std.mkpath(q)
94                 | std.Enone:
95                 | e:
96                         testr.fail(c, "could not mkpath {}: {}", q, e)
97                         -> false
98                 ;;
99         ;;
101         -> true
104 const mkfiles = {c : testr.ctx#, f : byte[:][:] -> bool
105         var s : std.strbuf# = std.mksb()
106         var p : byte[:]
107         var j : int
109         for ff : f
110                 std.sbtrim(s, 0)
111                 std.sbfmt(s, "{}", ff)
112                 p = std.sbpeek(s)
113                 for j = p.len - 1; j > 0; j--
114                         if p[j] == ('/' : byte)
115                                 std.sbtrim(s, (j : std.size))
116                                 match std.mkpath(std.sbpeek(s))
117                                 | std.Enone:
118                                 | e:
119                                         testr.fail(c, "could not mkpath {}: {}", std.sbpeek(s), e)
120                                         std.sbfree(s)
121                                         -> false
122                                 ;;
123                                 break
124                         ;;
125                 ;;
126         ;;
128         std.sbfree(s)
130         for ff : f
131                 match std.openmode(ff, std.Ocreat | std.Oappend, 0o700)
132                 | `std.Ok h: std.close(h)
133                 | `std.Err e:
134                         testr.fail(c, "could not open {}: {}", ff, e)
135                         -> false
136                 ;;
137         ;;
139         -> true
142 const beat = {c : testr.ctx#, d : byte[:] -> bool
143         if !std.chdir(testroot)
144                 testr.fail(c, "could not chdir to {}", testroot)
145                  -> false
146         ;;
148         match std.mkpath(d)
149         | std.Enone:
150         | e:
151                 testr.fail(c, "could not mkpath {}: {}", d, e)
152                  -> false
153         ;;
155         if !std.chdir(d)
156                 testr.fail(c, "could not chdir to {}", d)
157                  -> false
158         ;;
160         -> true
163 const strseq = {a : byte[:][:], b : byte[:][:] -> bool
164         var i : uint = 0
166         if a.len != b.len
167                 -> false
168         ;;
170         while i < a.len
171                 match std.strcmp(a[i], b[i])
172                 | `std.Equal:
173                 | _: -> false
174                 ;;
176                 i++
177         ;;
179         -> true
183 /* Actual tests */
184 const changeinfo01 = {c : testr.ctx#
185         var expected : byte[:] = [][:]
186         var expected2 : byte[:][:] = [][:]
187         var i : maildir.info = [ .passed = true, .seen = true ]
189         if !beat(c, "changeinfo01")
190                 -> void
191         ;;
193         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp" ][:])
194                 -> void
195         ;;
197         if !mkfiles(c, [ "a/cur/abcdefg.123:2,S" ][:])
198                 -> void
199         ;;
201         expected = "a/cur/abcdefg.123:2,PS"
203         match maildir.changeinfo("a/cur/abcdefg.123:2,S", i)
204         | `std.Ok actual:
205                 testr.check(c, std.sleq(actual, expected), \
206                             "expected “{}”, got “{}”", expected, actual)
207         | `std.Err e:
208                 testr.fail(c, "{}", e)
209                 -> void
210         ;;
212         expected = "link(\"a/cur/abcdefg.123:2,S\", \"a/cur/abcdefg.123:2,PS\"): -2"
214         match maildir.changeinfo("a/cur/abcdefg.123:2,S", i)
215         | `std.Ok actual:
216                 testr.fail(c, "got “{}”, expected error")
217                 -> void
218         | `std.Err actual:
219                 testr.check(c, std.sleq(actual, expected), \
220                             "expected “{}”, got “{}”", expected, actual)
221         ;;
223         expected2 = [ "a/cur/abcdefg.123:2,PS" ][:]
225         match tree([ "a" ][:])
226         | `std.Ok actual:
227                 testr.check(c, strseq(expected2, actual), \
228                             "expected “{}”, got “{}”", expected2, actual)
229         | `std.Err e:
230                 testr.fail(c, e)
231                 -> void
232         ;;
235 const cleanfs01 = {c : testr.ctx#
236         var s : std.strbuf# = std.mksb()
237         var expected : byte[:][:] = [][:]
238         var ago35 : uint64 = ((std.now() / std.Sec) : uint64) - (35 * 60 * 60)
239         var ago37 : uint64 = ((std.now() / std.Sec) : uint64) - (37 * 60 * 60)
241         if !beat(c, "cleanfs01")
242                 std.sbfree(s)
243                 -> void
244         ;;
246         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp",
247                          "b/cur", "b/new", "b/tmp",
248                          "c/cur", "c/new", "c/tmp" ][:])
249                 std.sbfree(s)
250                 -> void
251         ;;
253         expected = [ "a/cur/101:2,T",
254                      "a/cur/102:2,FS",
255                      "a/new/103:2,",
256                      "a/new/104:2,R",
257                      "a/tmp/105",
258                      "a/tmp/106",
259                      "b/cur/107:2,T",
260                      "b/cur/108:2,S",
261                      "b/new/109:2,",
262                      "b/new/110:2,R",
263                      "b/tmp/111",
264                      "b/tmp/112",
265                      "c/cur/113:2,T",
266                      "c/cur/114:2,FP",
267                      "c/new/115:2,",
268                      "c/new/116:2,R",
269                      "c/tmp/117",
270                      "c/tmp/118",
271                     ][:]
273         if !mkfiles(c, expected)
274                 -> void
275         ;;
277         match tree(["a", "b", "c"][:])
278         | `std.Ok actual:
279                 testr.check(c, strseq(expected, actual), \
280                             "expected “{}”, got “{}”", expected, actual)
281         | `std.Err e:
282                 testr.fail(c, e)
283                 -> void
284         ;;
286         if !settimes(c, [ "a/cur/101:2,T",
287                           "a/new/103:2,",
288                           "a/tmp/105",
289                           "b/tmp/112",
290                           "c/tmp/117" ][:], ago37)
291                 -> void
292         ;;
294         if !settimes(c, [ "a/tmp/106",
295                           "b/tmp/111",
296                           "c/tmp/118" ][:], ago35)
297                 -> void
298         ;;
300         expected = [ "a/cur/102:2,FS",
301                      "a/new/103:2,",
302                      "a/new/104:2,R",
303                      "a/tmp/106",
304                      "b/cur/108:2,S",
305                      "b/new/109:2,",
306                      "b/new/110:2,R",
307                      "b/tmp/111",
308                      "c/cur/113:2,T",
309                      "c/cur/114:2,FP",
310                      "c/new/115:2,",
311                      "c/new/116:2,R",
312                      "c/tmp/117",
313                      "c/tmp/118",
314                     ][:]
315         match maildir.cleanfs(["a", "b"][:])
316         | `std.Ok _:
317         | `std.Err e:
318                 testr.fail(c, e)
319                 -> void
320         ;;
322         match tree(["a", "b", "c"][:])
323         | `std.Ok actual:
324                 testr.check(c, strseq(expected, actual), \
325                             "expected “{}”, got “{}”", expected, actual)
326         | `std.Err e:
327                 testr.fail(c, e)
328                 -> void
329         ;;
333 const info01 = {c : testr.ctx#
334         var m : byte[:][:] = [][:]
335         var i : maildir.info = [ .trashed = false ]
336         var as : std.strbuf# = std.mksb()
337         var es : std.strbuf# = std.mksb()
338         var asp : byte[:] = ""
339         var esp : byte[:] = ""
340         var expected : (byte[:], maildir.info)[:]
341         var checked : bool = false
343         if !beat(c, "info01")
344                 std.sbfree(as)
345                 std.sbfree(es)
346                 -> void
347         ;;
349         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp" ][:])
350                 std.sbfree(as)
351                 std.sbfree(es)
352                 -> void
353         ;;
355         if !mkfiles(c, [ "a/new/somethinguniq⁷_##$$Foo",
356                          "a/new/765786.288.257968732.6795821",
357                          "a/cur/abc.def:2,P",
358                          "a/cur/zzzz:2",
359                          "a/cur/zzzy:2,",
360                          "a/cur/abc.d∞g:2,FRS",
361                          "a/cur/abc.deg:2,DPT",
362                         ][:])
363                 std.sbfree(as)
364                 std.sbfree(es)
365                 -> void
366         ;;
368         expected = [
369                 ("a/new/somethinguniq⁷_##$$Foo", \
370                  [ .passed = false ]),
371                 ("a/new/765786.288.257968732.6795821", \
372                  [ .passed = false ]),
373                 ("a/cur/abc.def:2,P", \
374                  [ .passed = true ]),
375                 ("a/cur/zzzz:2", \
376                  [ .passed = false ]),
377                 ("a/cur/zzzy:2,", \
378                  [ .passed = false ]),
379                 ("a/cur/abc.d∞g:2,FRS", \
380                  [ .flagged = true, .replied = true, .seen = true ]),
381                 ("a/cur/abc.deg:2,DPT", \
382                  [ .draft = true, .passed = true, .trashed = true ]),
383         ][:]
385         match maildir.messages("a")
386         | `std.Ok mm:
387                 m = mm
388                 if m.len != expected.len
389                         testr.fail(c, "some expected messages missing")
390                         std.sbfree(as)
391                         std.sbfree(es)
392                         -> void
393                 ;;
394         | `std.Err e:
395                 testr.fail(c, "can't list messages of a: {}", e)
396                 std.sbfree(as)
397                 std.sbfree(es)
398                 -> void
399         ;;
401         for p : m
402                 i = maildir.info(p)
403                 std.sbtrim(as, 0)
404                 std.sbfmt(as, "{}", i)
405                 asp = std.sbpeek(as)
406                 checked = false
408                 for (ep, ei) : expected
409                         if std.sleq(p, ep)
410                                 std.sbtrim(es, 0)
411                                 std.sbfmt(es, "{}", ei)
412                                 esp = std.sbpeek(es)
413                                 checked = true
414                                 testr.check(c, std.sleq(asp, esp), \
415                                             "for {}, expected “{}”, got “{}”", \
416                                             p, esp, asp)
417                         ;;
418                 ;;
420                 if !checked
421                         testr.fail(c, "unexpected message {}", p)
422                 ;;
423         ;;
425         std.sbfree(as)
426         std.sbfree(es)
429 const infofmt01 = {c : testr.ctx#
430         var s : std.strbuf# = std.mksb()
431         var i : maildir.info = [ .passed = true ]
432         var actual : byte[:] = [][:]
433         var expected : byte[:] = [][:]
435         std.sbtrim(s, 0)
436         std.sbfmt(s, "{}", i)
437         expected = ":2,P"
438         actual = std.sbpeek(s)
439         testr.check(c, std.sleq(expected, actual), "expected “{}”, got “{}”", \
440                     expected, actual)
442         i = [ .flagged = true, .replied = true, .trashed = true ]
443         std.sbtrim(s, 0)
444         std.sbfmt(s, "{}", i)
445         expected = ":2,FRT"
446         actual = std.sbpeek(s)
447         testr.check(c, std.sleq(expected, actual), "expected “{}”, got “{}”", \
448                     expected, actual)
450         i = [ .flagged = false ]
451         std.sbtrim(s, 0)
452         std.sbfmt(s, "{}", i)
453         expected = ":2,"
454         actual = std.sbpeek(s)
455         testr.check(c, std.sleq(expected, actual), "expected “{}”, got “{}”", \
456                     expected, actual)
459 const maildirs01 = {c : testr.ctx#
460         var expected : byte[:][:] = [][:]
461         var actual : byte[:][:] = [][:]
462         if !beat(c, "maildirs01")
463                 -> void
464         ;;
466         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp",
467                          "b/cur", "b/new", "b/tmq",
468                          "c/cur", "c/new", "c/tmq", "c/other",
469                          "d",
470                          "cur/cur", "cur/new", "cur/tmp",
471                     ][:])
472                 -> void
473         ;;
475         expected = [ "a", "cur" ][:]
476         match maildir.maildirs(".")
477         | `std.Ok a:
478                 actual = a
479                 testr.check(c, strseq(expected, actual), \
480                             "expected “{}”, got “{}”", expected, actual)
481                 for aa : a
482                         std.slfree(aa)
483                 ;;
484                 std.slfree(a)
485         | `std.Err e:
486                 testr.fail(c, "can't examine .: {}", e)
487         ;;
490 const maildirs02 = {c : testr.ctx#
491         var expected : byte[:][:] = [][:]
492         var actual : byte[:][:] = [][:]
494         if !beat(c, "maildirs02/a/long/way/down")
495                 -> void
496         ;;
498         if !mkpaths(c, [ "d/cur", "d/new", "d/tmp",
499                          "a/cur", "a/new", "a/tmq",
500                          "c/cur", "c/new", "c/tmq", "c/other",
501                          "d/extraneous",
502                          "cur/cur", "cur/new", "cur/tmp",
503                     ][:])
504                 -> void
505         ;;
507         if !beat(c, "maildirs02")
508                 -> void
509         ;;
511         expected = [ "a/long/way/down/cur", "a/long/way/down/d" ][:]
512         match maildir.maildirs("a/long/way/down")
513         | `std.Ok a:
514                 actual = a
515                 testr.check(c, strseq(expected, actual), \
516                             "expected “{}”, got “{}”", expected, actual)
517                 for aa : a
518                         std.slfree(aa)
519                 ;;
520                 std.slfree(a)
521         | `std.Err e:
522                 testr.fail(c, "can't examine a/long/way/down: {}", e)
523         ;;
526 const maildirs03 = {c : testr.ctx#
527         var expected : byte[:] = [][:]
529         if !beat(c, "maildirs03")
530                 -> void
531         ;;
533         match maildir.maildirs("/dev/some/place/that/really/shouldn't/exist")
534         | `std.Ok _:
535                 testr.fail(c, "got maildirs where none should be")
536         | `std.Err e:
537                 /* TODO: should we really be checking this pedantically? */
538                 expected = "cannot open /dev/some/place/that/really/shouldn't/exist: couldn't open directory"
539                 testr.check(c, std.sleq(e, expected), \
540                             "expected “{}”, got “{}”", \
541                             expected, e)
542         ;;
545 const messages01 = {c : testr.ctx#
546         var expected : byte[:][:] = [][:]
547         var actual : byte[:][:] = [][:]
548         if !beat(c, "messages01")
549                 -> void
550         ;;
552         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp", "a/.qmail",
553                          "a/new/foo", "a/cur/baz/quux",
554                          "a/new/.ignorethis", "a/cur/......thistoo"
555                     ][:])
556                 -> void
557         ;;
559         expected = std.sort([ "a/new/foo", "a/cur/baz" ][:], std.strcmp)
560         match maildir.messages("a")
561         | `std.Ok a:
562                 testr.check(c, strseq(expected, a), \
563                             "expected “{}”, got “{}”", expected, a)
564                 for aa : a
565                         std.slfree(aa)
566                 ;;
567                 std.slfree(a)
568         | `std.Err e:
569                 testr.fail(c, "can't list messages of a: {}", e)
570         ;;
573 const messages02 = {c : testr.ctx#
574         var expected : byte[:] = [][:]
575         if !beat(c, "messages02")
576                 -> void
577         ;;
579         if !mkpaths(c, [ "b/cur", "b/new", "b/NOTtmp",
580                          "b/new/foo", "b/cur/baz"
581                     ][:])
582                 -> void
583         ;;
585         match maildir.messages("b")
586         | `std.Ok a:
587                 testr.fail(c, "got messages where none should be")
588         | `std.Err e:
589                 expected = "b is not a maildir"
590                 testr.check(c, std.sleq(e, expected), \
591                             "expected “{}”, got “{}”", \
592                             expected, e)
593         ;;
596 const movemessage01 = {c : testr.ctx#
597         var s : std.strbuf# = std.mksb()
598         var expected : byte[:][:] = [][:]
600         if !beat(c, "movemessage01")
601                 std.sbfree(s)
602                 -> void
603         ;;
605         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp",
606                          "b/cur", "b/new", "b/tmp" ][:])
607                 std.sbfree(s)
608                 -> void
609         ;;
611         if !mkfiles(c, [ "a/cur/101:2,FT" ][:])
612                 -> void
613         ;;
615         expected = [ "a/cur/101:2,FT" ][:]
617         match tree(["a", "b"][:])
618         | `std.Ok actual:
619                 testr.check(c, strseq(expected, actual), \
620                             "expected “{}”, got “{}”", expected, actual)
621         | `std.Err e:
622                 testr.fail(c, e)
623                 -> void
624         ;;
626         match maildir.movemessage("a/cur/101:2,FT", "b")
627         | `std.Ok _:
628         | `std.Err e:
629                 testr.fail(c, e)
630                 -> void
631         ;;
633         expected = [ "b/cur/101:2,FT" ][:]
635         match tree(["a", "b"][:])
636         | `std.Ok actual:
637                 testr.check(c, strseq(expected, actual), \
638                             "expected “{}”, got “{}”", expected, actual)
639         | `std.Err e:
640                 testr.fail(c, e)
641                 -> void
642         ;;