update for upstream 52eca667bbd1cf5a929d3f2352c3c1ee8e91625d
[libmaildir.git] / test / maildir.myr
blob240ce36e51ba7a1dbcfa75fc8b452ecbf9a0bfa2
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         for q : p
62                 /* TODO: once syscalls are cleaned up, this needs to change */
63                 match sys.utimes(sys.cstring(q), &tvs[0])
64                 | 0:
65                 | e:
66                         testr.fail(c, "cannot set time of {}: {}", q, e)
67                         -> false
68                 ;;
69         ;;
71         -> true
74 const tree = {p : byte[:][:] -> std.result(byte[:][:], byte[:])
75         var r : byte[:][:] = std.slalloc(0)
77         for q : p
78                 for f : fileutil.bywalk(q)
79                         std.slpush(&r, std.sldup(f))
80                 ;;
81         ;;
83         -> `std.Ok std.sort(r, std.strcmp)
86 const mkpaths = {c : testr.ctx#, p : byte[:][:] -> bool
87         for q : p
88                 match std.mkpath(q)
89                 | std.Enone:
90                 | e:
91                         testr.fail(c, "could not mkpath {}: {}", q, e)
92                         -> false
93                 ;;
94         ;;
96         -> true
99 const mkfiles = {c : testr.ctx#, f : byte[:][:] -> bool
100         var s : std.strbuf# = std.mksb()
101         var p : byte[:]
102         var j : int
104         for ff : f
105                 std.sbtrim(s, 0)
106                 std.sbfmt(s, "{}", ff)
107                 p = std.sbpeek(s)
108                 for j = p.len - 1; j > 0; j--
109                         if p[j] == ('/' : byte)
110                                 std.sbtrim(s, (j : std.size))
111                                 match std.mkpath(std.sbpeek(s))
112                                 | std.Enone:
113                                 | e:
114                                         testr.fail(c, "could not mkpath {}: {}", std.sbpeek(s), e)
115                                         std.sbfree(s)
116                                         -> false
117                                 ;;
118                                 break
119                         ;;
120                 ;;
121         ;;
123         std.sbfree(s)
125         for ff : f
126                 match std.openmode(ff, std.Ocreat | std.Oappend, 0o700)
127                 | `std.Ok h: std.close(h)
128                 | `std.Err e:
129                         testr.fail(c, "could not open {}: {}", ff, e)
130                         -> false
131                 ;;
132         ;;
134         -> true
137 const beat = {c : testr.ctx#, d : byte[:] -> bool
138         if !std.chdir(testroot)
139                 testr.fail(c, "could not chdir to {}", testroot)
140                  -> false
141         ;;
143         match std.mkpath(d)
144         | std.Enone:
145         | e:
146                 testr.fail(c, "could not mkpath {}: {}", d, e)
147                  -> false
148         ;;
150         if !std.chdir(d)
151                 testr.fail(c, "could not chdir to {}", d)
152                  -> false
153         ;;
155         -> true
158 const strseq = {a : byte[:][:], b : byte[:][:] -> bool
159         var i : uint = 0
161         if a.len != b.len
162                 -> false
163         ;;
165         while i < a.len
166                 match std.strcmp(a[i], b[i])
167                 | `std.Equal:
168                 | _: -> false
169                 ;;
171                 i++
172         ;;
174         -> true
178 /* Actual tests */
179 const changeinfo01 = {c : testr.ctx#
180         var expected : byte[:] = [][:]
181         var expected2 : byte[:][:] = [][:]
182         var i : maildir.info = [ .passed = true, .seen = true ]
184         if !beat(c, "changeinfo01")
185                 -> void
186         ;;
188         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp" ][:])
189                 -> void
190         ;;
192         if !mkfiles(c, [ "a/cur/abcdefg.123:2,S" ][:])
193                 -> void
194         ;;
196         expected = "a/cur/abcdefg.123:2,PS"
198         match maildir.changeinfo("a/cur/abcdefg.123:2,S", i)
199         | `std.Ok actual:
200                 testr.check(c, std.eq(actual, expected), \
201                             "expected “{}”, got “{}”", expected, actual)
202         | `std.Err e:
203                 testr.fail(c, "{}", e)
204                 -> void
205         ;;
207         expected = "link(\"a/cur/abcdefg.123:2,S\", \"a/cur/abcdefg.123:2,PS\"): -2"
209         match maildir.changeinfo("a/cur/abcdefg.123:2,S", i)
210         | `std.Ok actual:
211                 testr.fail(c, "got “{}”, expected error")
212                 -> void
213         | `std.Err actual:
214                 testr.check(c, std.eq(actual, expected), \
215                             "expected “{}”, got “{}”", expected, actual)
216         ;;
218         expected2 = [ "a/cur/abcdefg.123:2,PS" ][:]
220         match tree([ "a" ][:])
221         | `std.Ok actual:
222                 testr.check(c, strseq(expected2, actual), \
223                             "expected “{}”, got “{}”", expected2, actual)
224         | `std.Err e:
225                 testr.fail(c, e)
226                 -> void
227         ;;
230 const cleanfs01 = {c : testr.ctx#
231         var s : std.strbuf# = std.mksb()
232         var expected : byte[:][:] = [][:]
233         var ago35 : uint64 = ((std.now() / std.Sec) : uint64) - (35 * 60 * 60)
234         var ago37 : uint64 = ((std.now() / std.Sec) : uint64) - (37 * 60 * 60)
236         if !beat(c, "cleanfs01")
237                 std.sbfree(s)
238                 -> void
239         ;;
241         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp",
242                          "b/cur", "b/new", "b/tmp",
243                          "c/cur", "c/new", "c/tmp" ][:])
244                 std.sbfree(s)
245                 -> void
246         ;;
248         expected = [ "a/cur/101:2,T",
249                      "a/cur/102:2,FS",
250                      "a/new/103:2,",
251                      "a/new/104:2,R",
252                      "a/tmp/105",
253                      "a/tmp/106",
254                      "b/cur/107:2,T",
255                      "b/cur/108:2,S",
256                      "b/new/109:2,",
257                      "b/new/110:2,R",
258                      "b/tmp/111",
259                      "b/tmp/112",
260                      "c/cur/113:2,T",
261                      "c/cur/114:2,FP",
262                      "c/new/115:2,",
263                      "c/new/116:2,R",
264                      "c/tmp/117",
265                      "c/tmp/118",
266                     ][:]
268         if !mkfiles(c, expected)
269                 -> void
270         ;;
272         match tree(["a", "b", "c"][:])
273         | `std.Ok actual:
274                 testr.check(c, strseq(expected, actual), \
275                             "expected “{}”, got “{}”", expected, actual)
276         | `std.Err e:
277                 testr.fail(c, e)
278                 -> void
279         ;;
281         if !settimes(c, [ "a/cur/101:2,T",
282                           "a/new/103:2,",
283                           "a/tmp/105",
284                           "b/tmp/112",
285                           "c/tmp/117" ][:], ago37)
286                 -> void
287         ;;
289         if !settimes(c, [ "a/tmp/106",
290                           "b/tmp/111",
291                           "c/tmp/118" ][:], ago35)
292                 -> void
293         ;;
295         expected = [ "a/cur/102:2,FS",
296                      "a/new/103:2,",
297                      "a/new/104:2,R",
298                      "a/tmp/106",
299                      "b/cur/108:2,S",
300                      "b/new/109:2,",
301                      "b/new/110:2,R",
302                      "b/tmp/111",
303                      "c/cur/113:2,T",
304                      "c/cur/114:2,FP",
305                      "c/new/115:2,",
306                      "c/new/116:2,R",
307                      "c/tmp/117",
308                      "c/tmp/118",
309                     ][:]
310         match maildir.cleanfs(["a", "b"][:])
311         | `std.Ok _:
312         | `std.Err e:
313                 testr.fail(c, e)
314                 -> void
315         ;;
317         match tree(["a", "b", "c"][:])
318         | `std.Ok actual:
319                 testr.check(c, strseq(expected, actual), \
320                             "expected “{}”, got “{}”", expected, actual)
321         | `std.Err e:
322                 testr.fail(c, e)
323                 -> void
324         ;;
328 const info01 = {c : testr.ctx#
329         var m : byte[:][:] = [][:]
330         var i : maildir.info = [ .trashed = false ]
331         var as : std.strbuf# = std.mksb()
332         var es : std.strbuf# = std.mksb()
333         var asp : byte[:] = ""
334         var esp : byte[:] = ""
335         var expected : (byte[:], maildir.info)[:]
336         var checked : bool = false
338         if !beat(c, "info01")
339                 std.sbfree(as)
340                 std.sbfree(es)
341                 -> void
342         ;;
344         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp" ][:])
345                 std.sbfree(as)
346                 std.sbfree(es)
347                 -> void
348         ;;
350         if !mkfiles(c, [ "a/new/somethinguniq⁷_##$$Foo",
351                          "a/new/765786.288.257968732.6795821",
352                          "a/cur/abc.def:2,P",
353                          "a/cur/zzzz:2",
354                          "a/cur/zzzy:2,",
355                          "a/cur/abc.d∞g:2,FRS",
356                          "a/cur/abc:Fdeg:2,DPT",
357                         ][:])
358                 std.sbfree(as)
359                 std.sbfree(es)
360                 -> void
361         ;;
363         expected = [
364                 ("a/new/somethinguniq⁷_##$$Foo", \
365                  [ .passed = false ]),
366                 ("a/new/765786.288.257968732.6795821", \
367                  [ .passed = false ]),
368                 ("a/cur/abc.def:2,P", \
369                  [ .passed = true ]),
370                 ("a/cur/zzzz:2", \
371                  [ .passed = false ]),
372                 ("a/cur/zzzy:2,", \
373                  [ .passed = false ]),
374                 ("a/cur/abc.d∞g:2,FRS", \
375                  [ .flagged = true, .replied = true, .seen = true ]),
376                 ("a/cur/abc:Fdeg:2,DPT", \
377                  [ .draft = true, .passed = true, .trashed = true ]),
378         ][:]
380         match maildir.messages("a")
381         | `std.Ok mm:
382                 m = mm
383                 if m.len != expected.len
384                         testr.fail(c, "some expected messages missing")
385                         std.sbfree(as)
386                         std.sbfree(es)
387                         -> void
388                 ;;
389         | `std.Err e:
390                 testr.fail(c, "can't list messages of a: {}", e)
391                 std.sbfree(as)
392                 std.sbfree(es)
393                 -> void
394         ;;
396         for p : m
397                 i = maildir.info(p)
398                 std.sbtrim(as, 0)
399                 std.sbfmt(as, "{}", i)
400                 asp = std.sbpeek(as)
401                 checked = false
403                 for (ep, ei) : expected
404                         if std.eq(p, ep)
405                                 std.sbtrim(es, 0)
406                                 std.sbfmt(es, "{}", ei)
407                                 esp = std.sbpeek(es)
408                                 checked = true
409                                 testr.check(c, std.eq(asp, esp), \
410                                             "for {}, expected “{}”, got “{}”", \
411                                             p, esp, asp)
412                         ;;
413                 ;;
415                 if !checked
416                         testr.fail(c, "unexpected message {}", p)
417                 ;;
418         ;;
420         std.sbfree(as)
421         std.sbfree(es)
424 const infofmt01 = {c : testr.ctx#
425         var s : std.strbuf# = std.mksb()
426         var i : maildir.info = [ .passed = true ]
427         var actual : byte[:] = [][:]
428         var expected : byte[:] = [][:]
430         std.sbtrim(s, 0)
431         std.sbfmt(s, "{}", i)
432         expected = "P"
433         actual = std.sbpeek(s)
434         testr.check(c, std.eq(expected, actual), "expected “{}”, got “{}”", \
435                     expected, actual)
437         i = [ .flagged = true, .replied = true, .trashed = true ]
438         std.sbtrim(s, 0)
439         std.sbfmt(s, "{}", i)
440         expected = "FRT"
441         actual = std.sbpeek(s)
442         testr.check(c, std.eq(expected, actual), "expected “{}”, got “{}”", \
443                     expected, actual)
445         i = [ .flagged = false ]
446         std.sbtrim(s, 0)
447         std.sbfmt(s, "{}", i)
448         expected = ""
449         actual = std.sbpeek(s)
450         testr.check(c, std.eq(expected, actual), "expected “{}”, got “{}”", \
451                     expected, actual)
454 const maildirs01 = {c : testr.ctx#
455         var expected : byte[:][:] = [][:]
456         var actual : byte[:][:] = [][:]
457         if !beat(c, "maildirs01")
458                 -> void
459         ;;
461         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp",
462                          "b/cur", "b/new", "b/tmq",
463                          "c/cur", "c/new", "c/tmq", "c/other",
464                          "d",
465                          "cur/cur", "cur/new", "cur/tmp",
466                     ][:])
467                 -> void
468         ;;
470         expected = [ "a", "cur" ][:]
471         match maildir.maildirs(".")
472         | `std.Ok a:
473                 actual = a
474                 testr.check(c, strseq(expected, actual), \
475                             "expected “{}”, got “{}”", expected, actual)
476                 for aa : a
477                         std.slfree(aa)
478                 ;;
479                 std.slfree(a)
480         | `std.Err e:
481                 testr.fail(c, "can't examine .: {}", e)
482         ;;
485 const maildirs02 = {c : testr.ctx#
486         var expected : byte[:][:] = [][:]
487         var actual : byte[:][:] = [][:]
489         if !beat(c, "maildirs02/a/long/way/down")
490                 -> void
491         ;;
493         if !mkpaths(c, [ "d/cur", "d/new", "d/tmp",
494                          "a/cur", "a/new", "a/tmq",
495                          "c/cur", "c/new", "c/tmq", "c/other",
496                          "d/extraneous",
497                          "cur/cur", "cur/new", "cur/tmp",
498                     ][:])
499                 -> void
500         ;;
502         if !beat(c, "maildirs02")
503                 -> void
504         ;;
506         expected = [ "a/long/way/down/cur", "a/long/way/down/d" ][:]
507         match maildir.maildirs("a/long/way/down")
508         | `std.Ok a:
509                 actual = a
510                 testr.check(c, strseq(expected, actual), \
511                             "expected “{}”, got “{}”", expected, actual)
512                 for aa : a
513                         std.slfree(aa)
514                 ;;
515                 std.slfree(a)
516         | `std.Err e:
517                 testr.fail(c, "can't examine a/long/way/down: {}", e)
518         ;;
521 const maildirs03 = {c : testr.ctx#
522         var expected : byte[:] = [][:]
524         if !beat(c, "maildirs03")
525                 -> void
526         ;;
528         match maildir.maildirs("/dev/some/place/that/really/shouldn't/exist")
529         | `std.Ok _:
530                 testr.fail(c, "got maildirs where none should be")
531         | `std.Err e:
532                 /* TODO: should we really be checking this pedantically? */
533                 expected = "cannot open /dev/some/place/that/really/shouldn't/exist: couldn't open directory"
534                 testr.check(c, std.eq(e, expected), \
535                             "expected “{}”, got “{}”", \
536                             expected, e)
537         ;;
540 const messages01 = {c : testr.ctx#
541         var expected : byte[:][:] = [][:]
542         var actual : byte[:][:] = [][:]
543         if !beat(c, "messages01")
544                 -> void
545         ;;
547         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp", "a/.qmail",
548                          "a/new/foo", "a/cur/baz/quux",
549                          "a/new/.ignorethis", "a/cur/......thistoo"
550                     ][:])
551                 -> void
552         ;;
554         expected = std.sort([ "a/new/foo", "a/cur/baz" ][:], std.strcmp)
555         match maildir.messages("a")
556         | `std.Ok a:
557                 testr.check(c, strseq(expected, a), \
558                             "expected “{}”, got “{}”", expected, a)
559                 for aa : a
560                         std.slfree(aa)
561                 ;;
562                 std.slfree(a)
563         | `std.Err e:
564                 testr.fail(c, "can't list messages of a: {}", e)
565         ;;
568 const messages02 = {c : testr.ctx#
569         var expected : byte[:] = [][:]
570         if !beat(c, "messages02")
571                 -> void
572         ;;
574         if !mkpaths(c, [ "b/cur", "b/new", "b/NOTtmp",
575                          "b/new/foo", "b/cur/baz"
576                     ][:])
577                 -> void
578         ;;
580         match maildir.messages("b")
581         | `std.Ok a:
582                 testr.fail(c, "got messages where none should be")
583         | `std.Err e:
584                 expected = "b is not a maildir"
585                 testr.check(c, std.eq(e, expected), \
586                             "expected “{}”, got “{}”", \
587                             expected, e)
588         ;;
591 const movemessage01 = {c : testr.ctx#
592         var s : std.strbuf# = std.mksb()
593         var expected : byte[:][:] = [][:]
595         if !beat(c, "movemessage01")
596                 std.sbfree(s)
597                 -> void
598         ;;
600         if !mkpaths(c, [ "a/cur", "a/new", "a/tmp",
601                          "b/cur", "b/new", "b/tmp" ][:])
602                 std.sbfree(s)
603                 -> void
604         ;;
606         if !mkfiles(c, [ "a/cur/101:2,FT" ][:])
607                 -> void
608         ;;
610         expected = [ "a/cur/101:2,FT" ][:]
612         match tree(["a", "b"][:])
613         | `std.Ok actual:
614                 testr.check(c, strseq(expected, actual), \
615                             "expected “{}”, got “{}”", expected, actual)
616         | `std.Err e:
617                 testr.fail(c, e)
618                 -> void
619         ;;
621         match maildir.movemessage("a/cur/101:2,FT", "b")
622         | `std.Ok _:
623         | `std.Err e:
624                 testr.fail(c, e)
625                 -> void
626         ;;
628         expected = [ "b/cur/101:2,FT" ][:]
630         match tree(["a", "b"][:])
631         | `std.Ok actual:
632                 testr.check(c, strseq(expected, actual), \
633                             "expected “{}”, got “{}”", expected, actual)
634         | `std.Err e:
635                 testr.fail(c, e)
636                 -> void
637         ;;