#! /usr/bin/env python
# vim: fileencoding=utf-8
#
# Copyright (c) 2007-2008 Adeodato Simó (dato@net.com.org.es)
#
# This software may be used and distributed according to the terms
# of the MIT License, incorporated herein by reference.

import os
import shutil
import tempfile
import unittest

import miw

class TestWithTmpDir(unittest.TestCase):
    def setUp(self):
        self.tmpdir = tempfile.mkdtemp(prefix='test_miw.')

    def tearDown(self):
        shutil.rmtree(self.tmpdir)

    def make_maildir(self, relpath):
        for d in ('cur', 'new', 'tmp'):
            os.makedirs(os.path.join(self.tmpdir, relpath, d))

###

class StubWatchManager:
    def add_watch(*args):
        pass
    
###

class TestWatchedMaildir(TestWithTmpDir):
    def setUp(self):
        super(TestWatchedMaildir, self).setUp()
        self.watch_manager = StubWatchManager()

    def watched_maildir(self, relpath):
        return miw.WatchedMaildir(self.tmpdir, relpath, self.watch_manager)

    def create_messages(self, maildir):
        self.messages = [(15, 'a'), (10, 'b'), (5, 'c')]

        for mtime, name in self.messages:
            self.create_message(maildir, mtime, name)

    def create_message(self, maildir, mtime, name):
        path = os.path.join(self.tmpdir, maildir, 'new', name)
        file(path, 'w').close()
        os.utime(path, (mtime, mtime))

    def test_empty_maildir(self):
        self.make_maildir('m')
        m = self.watched_maildir('m')
        self.assert_(len(m.messages) == 0)
        self.assert_(m.mtime == 0)

    def test_nonexisting_maildir(self):
        self.assertRaises(Exception, self.watched_maildir, 'm')
        os.mkdir(os.path.join(self.tmpdir, 'm'))
        self.assertRaises(Exception, self.watched_maildir, 'm') # no 'new' subdir

    def test_nonempty_maildir(self):
        self.make_maildir('m')
        self.create_messages('m')
        m = self.watched_maildir('m')
        self.assertEquals(self.messages, m.messages)
        self.assertEquals(self.messages[0][0], m.mtime)

    def test_new_message(self):
        self.make_maildir('m')
        self.create_messages('m')
        m = self.watched_maildir('m')

        message = (20, 'x')
        self.messages.insert(0, message)
        self.create_message('m', *message)
        m._new_message(message[1])
        self.assertEquals(self.messages, m.messages)
        self.assertEquals(message[0], m.mtime)

        message = (4, 'y')
        self.messages.append(message)
        self.create_message('m', *message)
        m._new_message(message[1])
        self.assertEquals(self.messages, m.messages)
        self.assertEquals(self.messages[0][0], m.mtime)

        message = (12, 'z')
        self.messages.insert(2, message)
        self.create_message('m', *message)
        m._new_message(message[1])
        self.assertEquals(self.messages, m.messages)
        self.assertEquals(self.messages[0][0], m.mtime)

    def test_new_message_in_empty(self):
        self.make_maildir('m')
        m = self.watched_maildir('m')

        message = (10, 'a')
        self.create_message('m', *message)
        m._new_message(message[1])
        self.assertEquals([message], m.messages)
        self.assertEquals(message[0], m.mtime)

    def test_new_message_nonexistant(self):
        self.make_maildir('m')
        m = self.watched_maildir('m')
        m._new_message('nonexistant')
        self.assertEquals([], m.messages)
        self.assertEquals(0, m.mtime)

    def test_del_message(self):
        def pop_and_delete(i):
            message = self.messages.pop(i)
            m._del_message(message[1])
            self.assertEquals(self.messages, m.messages)
            self.assertEquals(self.messages and self.messages[0][0] or 0, m.mtime)

        self.make_maildir('m')

        for i in range(3):
            self.create_messages('m')
            m = self.watched_maildir('m')
            pop_and_delete(i)

        self.create_messages('m')
        m = self.watched_maildir('m')

        for _ in self.messages:
            pop_and_delete(0)

        self.create_messages('m')
        m = self.watched_maildir('m')

        for _ in self.messages:
            pop_and_delete(-1)

    def test_del_message_in_empty(self):
        self.make_maildir('m')
        m = self.watched_maildir('m')
        m._del_message('x') # should not raise
        self.assertEquals([], m.messages)
        self.assertEquals(0, m.mtime)

    def test_del_message_nonexistant(self):
        self.make_maildir('m')
        self.create_messages('m')
        m = self.watched_maildir('m')
        m._del_message('nonexistant') # should not raise
        self.assertEquals(self.messages, m.messages)
        self.assertEquals(self.messages[0][0], m.mtime)

###

class TestFindMaildirs(TestWithTmpDir):
    def setUp(self):
        super(TestFindMaildirs, self).setUp()

        self.maildirs = ['maildir1', 'maildir2', 'dir1/maildir1',
                'dir1/maildir2', 'dir2/maildir1', 'dir2/maildir2',
                'dir3/dir/maildir']

        self.nested_maildirs = ['maildir1/nested', 'dir1/maildir2/nested']

        self.contains_maildirs = ['.', 'dir1', 'dir2']

        self.contains_maildirs_nested = ['maildir1', 'dir1/maildir2']

        self.empty_dirs = ['dir4', 'dir5', 'dir5/dir']

        self.symdest = ['dir1/symdest1', 'dir6/symdest2']

        for x in self.empty_dirs:
            os.makedirs(os.path.join(self.tmpdir, x))

        for x in self.maildirs + self.nested_maildirs + self.symdest:
            self.make_maildir(x)

        os.symlink('dir1/symdest1', os.path.join(self.tmpdir, 'symlink1a'))
        os.symlink('../dir1/symdest1', os.path.join(self.tmpdir, 'dir2/symlink1b'))

        os.symlink(os.path.join(self.tmpdir, 'dir6/symdest2'), os.path.join(self.tmpdir, 'symlink2a'))
        os.symlink(os.path.join(self.tmpdir, 'dir6/symdest2'), os.path.join(self.tmpdir, 'dir2/symlink2b'))

    def runTest(self):
        maildirs1, dirs1 = miw.find_maildirs(self.tmpdir)
        maildirs2, dirs2 = miw.find_maildirs(self.tmpdir, nested=True)

        for x in self.maildirs:
            self.assert_(x in maildirs1)
            self.assert_(x in maildirs2)

        for x in self.nested_maildirs:
            self.assert_(x not in maildirs1)
            self.assert_(x in maildirs2)

        for x in self.contains_maildirs:
            self.assert_(x in dirs1)
            self.assert_(x in dirs2)

        for x in self.contains_maildirs_nested:
            self.assert_(x not in dirs1)
            self.assert_(x in dirs2)

        for x in self.empty_dirs:
            self.assert_(x not in dirs1)
            self.assert_(x not in dirs2)

        for x in self.symdest:
            self.assert_(x not in maildirs1)
            self.assert_(x not in maildirs2)

        self.assert_(('symlink1a' in maildirs1) ^ ('dir2/symlink1b' in maildirs1))
        self.assert_(('symlink1a' in maildirs2) ^ ('dir2/symlink1b' in maildirs2))
        self.assert_(('symlink1a' in maildirs1) ^ ('dir2/symlink2b' in maildirs1))
        self.assert_(('symlink1a' in maildirs2) ^ ('dir2/symlink2b' in maildirs2))

###

class TestMuttfileMailboxes(TestWithTmpDir):
    def setUp(self):
        super(TestMuttfileMailboxes, self).setUp()
        self.file = file(os.path.join(self.tmpdir, 'muttrc'), 'w')

    def test_simple_file(self):
        self.file.write('mailboxes =maildir1  +maildir2\t=maildir3\n')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2', 'maildir3']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_simple_file_without_newline(self):
        self.file.write('mailboxes =maildir1  +maildir2 \t =maildir3')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2', 'maildir3']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_multiline_file(self):
        self.file.write('''mailboxes =maildir1
                           mailboxes +maildir2  maildir3
mailboxes =maildir4\n''')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2', 'maildir3', 'maildir4']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_file_with_trailing_whitespace(self):
        self.file.write('mailboxes =maildir1 \nmailboxes +maildir2\t')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_file_with_semicolon1(self):
        self.file.write('mailboxes =maildir1; other =stuff')
        self.file.close()
        self.assertEqual(['maildir1'], miw.muttfile_mailboxes(self.file.name))

    def test_file_with_semicolon2(self):
        self.file.write('mailboxes =maildir1; mailboxes =maildir2')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_file_with_semicolon3(self):
        self.file.write('mailboxes =maildir1; other stuff; mailboxes =maildir2')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_file_with_comment(self):
        self.file.write(
                'mailboxes =maildir1; other; mailboxes =maildir2 # =maildir3')
        self.file.close()
        self.assertEqual(set(['maildir1', 'maildir2']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_file_with_absolute_paths(self):
        self.file.write('mailboxes /maildir1 +maildir2 /maildir3')
        self.file.close()
        self.assertEqual(set(['/maildir1', 'maildir2', '/maildir3']),
                set(miw.muttfile_mailboxes(self.file.name)))

    def test_stripping_root_from_absolute_paths(self):
        self.file.write('mailboxes /a/b/c/maildir1 /a/bmaildir2 /a/b/maildir3')
        self.file.close()
        self.assertEqual(set(['c/maildir1', '/a/bmaildir2', 'maildir3']),
                set(miw.muttfile_mailboxes(self.file.name, '/a/b')))

###

if __name__ == '__main__':
    unittest.main()
