package maildir
import (
"fmt"
"io"
"io/ioutil"
"math/rand"
"os"
"testing"
)
// cleanup removes a Dir's directory structure
func cleanup(tb testing.TB, d Dir) {
err := os.RemoveAll(string(d))
if err != nil {
tb.Error(err)
}
}
// exists checks if the given path exists
func exists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
panic(err)
}
// cat returns the content of a file as a string
func cat(t *testing.T, path string) string {
f, err := os.Open(path)
if err != nil {
t.Fatal(err)
}
defer f.Close()
c, err := ioutil.ReadAll(f)
if err != nil {
t.Fatal(err)
}
return string(c)
}
// makeDelivery creates a new message
func makeDelivery(tb testing.TB, d Dir, msg string) {
del, err := NewDelivery(string(d))
if err != nil {
tb.Fatal(err)
}
_, err = del.Write([]byte(msg))
if err != nil {
tb.Fatal(err)
}
err = del.Close()
if err != nil {
tb.Fatal(err)
}
}
func TestInit(t *testing.T) {
t.Parallel()
var d Dir = "test_create"
err := d.Init()
if err != nil {
t.Fatal(err)
}
f, err := os.Open("test_create")
if err != nil {
t.Fatal(err)
}
fis, err := f.Readdir(0)
subdirs := make(map[string]os.FileInfo)
for _, fi := range fis {
if !fi.IsDir() {
t.Errorf("%s was not a directory", fi.Name())
continue
}
subdirs[fi.Name()] = fi
}
// Verify the directories have been created.
if _, ok := subdirs["tmp"]; !ok {
t.Error("'tmp' directory was not created")
}
if _, ok := subdirs["new"]; !ok {
t.Error("'new' directory was not created")
}
if _, ok := subdirs["cur"]; !ok {
t.Error("'cur' directory was not created")
}
// Make sure no error is returned if the directories already exist.
err = d.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d)
}
func TestDelivery(t *testing.T) {
t.Parallel()
var d Dir = "test_delivery"
err := d.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d)
var msg = "this is a message"
makeDelivery(t, d, msg)
keys, err := d.Unseen()
if err != nil {
t.Fatal(err)
}
path, err := d.Filename(keys[0])
if err != nil {
t.Fatal(err)
}
if !exists(path) {
t.Fatal("File doesn't exist")
}
if cat(t, path) != msg {
t.Fatal("Content doesn't match")
}
}
func TestDir_Create(t *testing.T) {
t.Parallel()
var d Dir = "test_create"
err := d.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d)
var msg = "this is a message"
key, w, err := d.Create([]Flag{FlagFlagged})
if err != nil {
t.Fatal(err)
}
defer w.Close()
if _, err := io.WriteString(w, msg); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
flags, err := d.Flags(key)
if err != nil {
t.Fatal(err)
} else if len(flags) != 1 || flags[0] != FlagFlagged {
t.Errorf("Dir.Flags() = %v, want {FlagFlagged}", flags)
}
path, err := d.Filename(key)
if err != nil {
t.Fatal(err)
}
if !exists(path) {
t.Fatal("File doesn't exist")
}
if cat(t, path) != msg {
t.Fatal("Content doesn't match")
}
}
func TestPurge(t *testing.T) {
t.Parallel()
var d Dir = "test_purge"
err := d.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d)
makeDelivery(t, d, "foo")
keys, err := d.Unseen()
if err != nil {
t.Fatal(err)
}
path, err := d.Filename(keys[0])
if err != nil {
t.Fatal(err)
}
err = d.Remove(keys[0])
if err != nil {
t.Fatal(err)
}
if exists(path) {
t.Fatal("File still exists")
}
}
func TestMove(t *testing.T) {
t.Parallel()
var d1 Dir = "test_move1"
err := d1.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d1)
var d2 Dir = "test_move2"
err = d2.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d2)
const msg = "a moving message"
makeDelivery(t, d1, msg)
keys, err := d1.Unseen()
if err != nil {
t.Fatal(err)
}
err = d1.Move(d2, keys[0])
if err != nil {
t.Fatal(err)
}
keys, err = d2.Keys()
if err != nil {
t.Fatal(err)
}
path, err := d2.Filename(keys[0])
if err != nil {
t.Fatal(err)
}
if cat(t, path) != msg {
t.Fatal("Content doesn't match")
}
}
func TestCopy(t *testing.T) {
t.Parallel()
var d1 Dir = "test_copy1"
err := d1.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d1)
var d2 Dir = "test_copy2"
err = d2.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d2)
const msg = "a moving message"
makeDelivery(t, d1, msg)
keys, err := d1.Unseen()
if err != nil {
t.Fatal(err)
}
if err = d1.SetFlags(keys[0], []Flag{FlagSeen}); err != nil {
t.Fatal(err)
}
key2, err := d1.Copy(d2, keys[0])
if err != nil {
t.Fatal(err)
}
path, err := d1.Filename(keys[0])
if err != nil {
t.Fatal(err)
}
if cat(t, path) != msg {
t.Error("original content has changed")
}
path, err = d2.Filename(key2)
if err != nil {
t.Fatal(err)
}
if cat(t, path) != msg {
t.Error("target content doesn't match source")
}
flags, err := d2.Flags(key2)
if err != nil {
t.Fatal(err)
}
if len(flags) != 1 {
t.Fatal("no flags on target")
}
if flags[0] != FlagSeen {
t.Error("seen flag not present on target")
}
}
func TestIllegal(t *testing.T) {
t.Parallel()
var d1 Dir = "test_illegal"
err := d1.Init()
if err != nil {
t.Fatal(err)
}
defer cleanup(t, d1)
const msg = "an illegal message"
makeDelivery(t, d1, msg)
keys, err := d1.Unseen()
if err != nil {
t.Fatal(err)
}
if err = d1.SetFlags(keys[0], []Flag{FlagSeen}); err != nil {
t.Fatal(err)
}
path, err := d1.Filename(keys[0])
if err != nil {
t.Fatal(err)
}
os.Rename(path, "test_illegal/cur/"+keys[0])
_, err = d1.Flags(keys[0])
if err != nil {
if _, ok := err.(*MailfileError); !ok {
t.Fatal(err)
}
}
}
func BenchmarkFilename(b *testing.B) {
// set up test maildir
d := Dir("benchmark_filename")
if err := d.Init(); err != nil {
b.Fatalf("could not set up benchmark: %v", err)
}
defer cleanup(b, d)
// make 5000 deliveries
for i := 0; i < 5000; i++ {
makeDelivery(b, d, fmt.Sprintf("here is message number %d", i))
}
// grab keys
keys, err := d.Unseen()
if err != nil {
b.Fatal(err)
}
// shuffle keys
rand.Shuffle(len(keys), func(i, j int) {
keys[i], keys[j] = keys[j], keys[i]
})
// set some flags
for i, key := range keys {
var err error
switch i % 5 {
case 0:
// no flags
fallthrough
case 1:
err = d.SetFlags(key, []Flag{FlagSeen})
case 2:
err = d.SetFlags(key, []Flag{FlagSeen, FlagReplied})
case 3:
err = d.SetFlags(key, []Flag{FlagReplied})
case 4:
err = d.SetFlags(key, []Flag{FlagFlagged})
}
if err != nil {
b.Fatal(err)
}
}
// run benchmark for the first N shuffled keys
keyIdx := 0
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StartTimer()
_, err := d.Filename(keys[keyIdx])
b.StopTimer()
if err != nil {
b.Errorf("could not get filename for key %s", keys[keyIdx])
}
keyIdx++
if keyIdx >= len(keys) {
keyIdx = 0
}
}
}