#!/usr/bin/env python2
# encoding=utf-8

import sys
import os
import shutil
import subprocess
import unittest

try:
    from email.utils import formataddr
except ImportError:
    # Prior to Python 2.5, the email module used different names:
    from email.Utils import formataddr

TEST_DIR = os.path.abspath(os.path.dirname(sys.argv[0]))
PROJ_DIR = os.path.dirname(TEST_DIR)
REPO = os.path.realpath(os.path.join(TEST_DIR, 'env-repo.git'))

sys.path.insert(0, os.path.join(PROJ_DIR, 'git-multimail'))

import git_multimail
from git_multimail import Config
from git_multimail import GenericEnvironment
from git_multimail import GitoliteEnvironment
from git_multimail import addr_header_encode, header_encode


class HelperTest(unittest.TestCase):
    LONG_ASCII_ADDR = formataddr((
        'Nobodywouldhavesuchaninsanelongname Butwhoknowswhathappens',
        'somelong.sampleaddr@example.com',
        ))
    LONG_UNICODE_ADDR = formataddr((
        u'Raphaël Gilbert André Hertzog',
        'somelong.sampleaddr@example.com',
        ))
    SHORT_ASCII_ADDR = 'John Doe <john@example.com>'
    SHORT_UNICODE_ADDR = u'Joël Doe <joel@example.com>'

    def test_addr_header_encode_short_ascii(self):
        self.assertEqual(
            addr_header_encode(self.SHORT_ASCII_ADDR),
            self.SHORT_ASCII_ADDR,
            "incorrect encoding or wrapping of short ascii address"
            )

    def test_addr_header_encode_short_unicode(self):
        self.assertEqual(
            addr_header_encode(self.SHORT_UNICODE_ADDR),
            "=?utf-8?b?Sm/Dq2wgRG9l?= <joel@example.com>",
            "incorrect encoding or wrapping of short unicode address"
            )

    def test_addr_header_encode_long_ascii(self):
        self.assertEqual(
            addr_header_encode(self.LONG_ASCII_ADDR),
            "Nobodywouldhavesuchaninsanelongname Butwhoknowswhathappens\n" +
            " <somelong.sampleaddr@example.com>",
            "incorrect encoding or wrapping of long ascii address"
            )

    def test_addr_header_encode_long_unicode(self):
        self.assertEqual(
            addr_header_encode(self.LONG_UNICODE_ADDR),
            "=?utf-8?q?Rapha=C3=ABl_Gilbert_Andr=C3=A9_Hertzog?=\n" +
            " <somelong.sampleaddr@example.com>",
            "incorrect encoding or wrapping of long unicode address"
            )

    def test_addr_header_encode_long_mixed(self):
        addrlist = ", ".join([self.LONG_UNICODE_ADDR, self.LONG_ASCII_ADDR])
        self.assertEqual(
            addr_header_encode(addrlist),
            "=?utf-8?q?Rapha=C3=ABl_Gilbert_Andr=C3=A9_Hertzog?=\n" +
            " <somelong.sampleaddr@example.com>,\n" +
            " Nobodywouldhavesuchaninsanelongname Butwhoknowswhathappens\n" +
            " <somelong.sampleaddr@example.com>",
            "incorrect encoding or wrapping of address list mixing " +
            "unicode and ascii"
            )


class EnvTest(unittest.TestCase):
    def __init__(
            self,
            description,
            user_config=None,
            extra_config=None, extra_osenv=None,
            tests=None,
            ):
        unittest.TestCase.__init__(self)
        self.description = description
        self.user_config = user_config
        self.extra_config = extra_config
        if extra_osenv:
            self.osenv = os.environ.copy()
            self.osenv.update(extra_osenv)
        else:
            self.osenv = os.environ
        self.tests = tests

    def shortDescription(self):
        return self.description

    def setUp(self):
        self.old_dir = os.getcwd()
        if os.path.isdir(REPO):
            shutil.rmtree(REPO)
        retcode = subprocess.call(
            ['git', 'init', '--bare', REPO],
            stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT,
            )
        self.assertEqual(retcode, 0,
                         'git init --bare "%s" returned %d' % (REPO, retcode))
        os.chdir(REPO)
        user_config = Config('user')
        if self.user_config is None:
            user_config.set('name', 'Joe User')
            user_config.set('email', 'user@example.com')
        else:
            for (name, value) in self.user_config.items():
                user_config.set(name, value)

        self.config = Config('multimailhook')
        self.add_to_config(mailinglist='Mailing List <mailinglist@example.com>')
        if self.extra_config:
            self.add_to_config(**self.extra_config)

    def add_to_config(self, **extra_config):
        for (name, value) in extra_config.items():
            if value is None:
                self.config.unset_all(name)
            else:
                self.config.set(name, value)

    def create_environment(self):
        raise NotImplementedError()

    def check_environment(self, environment):
        values = environment.get_values()
        for (name, value) in self.tests.items():
            if value is None:
                if name in values:
                    self.fail(
                        'value for %s should not be set; is %r'
                        % (name, values[name],)
                        )
            else:
                self.assertEquals(
                    values[name], value,
                    'value for %s: %r != %r' % (name, values[name], value,)
                    )

    def runTest(self):
        self.check_environment(self.create_environment())

    def tearDown(self):
        os.chdir(self.old_dir)
        shutil.rmtree(REPO)


class GenericEnvTest(EnvTest):
    PUSHER = 'fakeuser'
    REPONAME = 'env-repo'

    def __init__(self, description, user_config=None, extra_config=None, tests=None):
        EnvTest.__init__(
            self,
            description,
            user_config=user_config,
            extra_config=extra_config,
            extra_osenv=dict(USER=self.PUSHER),
            tests=tests,
            )

    def create_environment(self):
        return GenericEnvironment(osenv=self.osenv, config=self.config)


class GitoliteEnvTest(EnvTest):
    PUSHER = 'gluser'
    REPONAME = 'gitolite-repo'

    def __init__(self, description, user_config=None, extra_config=None, tests=None):
        EnvTest.__init__(
            self,
            description,
            user_config=user_config,
            extra_config=extra_config,
            extra_osenv=dict(GL_USER=self.PUSHER, GL_REPO=self.REPONAME),
            tests=tests,
            )

    def create_environment(self):
        return GitoliteEnvironment(osenv=self.osenv, config=self.config)


class ProjectDescTest(GenericEnvTest):
    PROJECTDESC = (
        'A test repository\n'
        'More text that should not be used\n'
        )

    def __init__(self):
        GenericEnvTest.__init__(
            self,
            'Test reading project description',
            tests={'projectdesc': self.PROJECTDESC.splitlines(False)[0]},
            )

    def setUp(self):
        GenericEnvTest.setUp(self)
        open(os.path.join(REPO, 'description'), 'w').write(self.PROJECTDESC)


GENERIC_ENVIRONMENT = dict(
    repo_shortname=GenericEnvTest.REPONAME,
    emailprefix='[%s] ' % (GenericEnvTest.REPONAME,),
    pusher=GenericEnvTest.PUSHER,
    pusher_email=None,
    administrator='the administrator of this repository',
    charset='utf-8',
    fromaddr='Joe User <user@example.com>',
    projectdesc='UNNAMED PROJECT',
    repo_path=REPO,
    )
GITOLITE_ENVIRONMENT = GENERIC_ENVIRONMENT.copy()
GITOLITE_ENVIRONMENT.update(
    repo_shortname=GitoliteEnvTest.REPONAME,
    emailprefix='[%s] ' % (GitoliteEnvTest.REPONAME,),
    pusher=GitoliteEnvTest.PUSHER,
    )

FROM = 'From Fellow <from@example.com>'
ADMIN = 'Admin Guy <admin@example.com>'
ENVELOPE = 'Flat Stanley <stanley@example.com>'
REPONAME = 'little-test-repo'
EMAILPREFIX = '[%s]' % (REPONAME,)
EMAILDOMAIN = 'git.example.com'


def suite():
    suite = unittest.TestSuite()
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(HelperTest))
    suite.addTest(GenericEnvTest('basic generic test', tests=GENERIC_ENVIRONMENT))
    suite.addTest(GitoliteEnvTest('basic gitolite test', tests=GITOLITE_ENVIRONMENT))

    for test in [GenericEnvTest, GitoliteEnvTest]:
        suite.addTests([
            # Tests of value of "repo_shortname" and "emailprefix":
            test(
                'Test "repo_shortname" and "emailprefix" settings',
                extra_config={'reponame': REPONAME},
                tests={'repo_shortname': REPONAME,
                       'emailprefix': EMAILPREFIX + ' ',
                       },
                ),
            test(
                'Test setting "emailprefix" explicitly',
                extra_config={'emailprefix': EMAILPREFIX},
                tests={'emailprefix': EMAILPREFIX + ' '},
                ),
            test(
                'Test "emailprefix" spaces are correct',
                extra_config={'emailprefix': EMAILPREFIX + ' '},
                tests={'emailprefix': EMAILPREFIX + ' '},
                ),

            # Tests of value of "fromaddr":
            test(
                'Test "from" setting',
                extra_config={'from': FROM},
                tests={'fromaddr': FROM},
                ),
            test(
                'Test that "from" defaults to user settings',
                user_config=dict(name='User', email='user@example.com'),
                extra_config={'envelopesender': ENVELOPE},
                tests={'fromaddr': 'User <user@example.com>'},
                ),
            test(
                'Test that "from" defaults to user email',
                user_config=dict(email='user@example.com'),
                extra_config={'envelopesender': ENVELOPE},
                tests={'fromaddr': 'user@example.com'},
                ),
            test(
                'Test that "from" defaults to "envelopesender"',
                user_config=dict(name='User'),
                extra_config={'envelopesender': ENVELOPE},
                tests={'fromaddr': ENVELOPE},
                ),
            test(
                'Test no "from" source',
                user_config=dict(name='User'),
                tests={'fromaddr': None},
                ),

            # Tests of value of "administrator":
            test(
                'Test "administrator" setting',
                extra_config={'administrator': ADMIN},
                tests={'administrator': ADMIN},
                ),
            test(
                'Test that "administrator" defaults to "envelopesender"',
                extra_config={'envelopesender': ENVELOPE},
                tests={'administrator': ENVELOPE},
                ),

            # Tests of value of "pusher":
            test(
                'Test "pusher" setting',
                extra_config={'emaildomain': EMAILDOMAIN},
                tests={'pusher': test.PUSHER,
                       'pusher_email': '%s@%s' % (test.PUSHER, EMAILDOMAIN),
                       },
                ),
            ])

    suite.addTest(ProjectDescTest())

    return suite


if __name__ == '__main__':
    os.environ['HOME'] = TEST_DIR
    os.environ['XDG_CONFIG_HOME'] = TEST_DIR
    os.environ['GIT_CONFIG_NOSYSTEM'] = '1'
    unittest.TextTestRunner().run(suite())
