# -*- coding: utf-8 -*-
"""sdist tests"""


import os
import shutil
import sys
import tempfile
import unittest
import urllib
import unicodedata
import posixpath
from StringIO import StringIO


from setuptools.command.sdist import sdist
from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution


SETUP_ATTRS = {
    'name': 'sdist_test',
    'version': '0.0',
    'packages': ['sdist_test'],
    'package_data': {'sdist_test': ['*.txt']}
}


SETUP_PY = """\
from setuptools import setup

setup(**%r)
""" % SETUP_ATTRS


if sys.version_info >= (3,):
    LATIN1_FILENAME = 'smörbröd.py'.encode('latin-1')
else:
    LATIN1_FILENAME = 'sm\xf6rbr\xf6d.py'


# Cannot use context manager because of Python 2.4
def quiet():
    global old_stdout, old_stderr
    old_stdout, old_stderr = sys.stdout, sys.stderr
    sys.stdout, sys.stderr = StringIO(), StringIO()

def unquiet():
    sys.stdout, sys.stderr = old_stdout, old_stderr


# Fake byte literals to shut up Python <= 2.5
def b(s, encoding='utf-8'):
    if sys.version_info >= (3,):
        return s.encode(encoding)
    return s


# HFS Plus returns decomposed UTF-8
def decompose(path):
    if isinstance(path, unicode):
        return unicodedata.normalize('NFD', path)
    try:
        path = path.decode('utf-8')
        path = unicodedata.normalize('NFD', path)
        path = path.encode('utf-8')
    except UnicodeError:
        pass # Not UTF-8
    return path


# HFS Plus quotes unknown bytes like so: %F6
def hfs_quote(path):
    if isinstance(path, unicode):
        raise TypeError('bytes are required')
    try:
        u = path.decode('utf-8')
    except UnicodeDecodeError:
        path = urllib.quote(path) # Not UTF-8
    else:
        if sys.version_info >= (3,):
            path = u
    return path


class TestSdistTest(unittest.TestCase):

    def setUp(self):
        self.temp_dir = tempfile.mkdtemp()
        f = open(os.path.join(self.temp_dir, 'setup.py'), 'w')
        f.write(SETUP_PY)
        f.close()
        # Set up the rest of the test package
        test_pkg = os.path.join(self.temp_dir, 'sdist_test')
        os.mkdir(test_pkg)
        # *.rst was not included in package_data, so c.rst should not be
        # automatically added to the manifest when not under version control
        for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']:
            # Just touch the files; their contents are irrelevant
            open(os.path.join(test_pkg, fname), 'w').close()

        self.old_cwd = os.getcwd()
        os.chdir(self.temp_dir)

    def tearDown(self):
        os.chdir(self.old_cwd)
        shutil.rmtree(self.temp_dir)

    def test_package_data_in_sdist(self):
        """Regression test for pull request #4: ensures that files listed in
        package_data are included in the manifest even if they're not added to
        version control.
        """

        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        cmd = sdist(dist)
        cmd.ensure_finalized()

        # squelch output
        quiet()
        try:
            cmd.run()
        finally:
            unquiet()

        manifest = cmd.filelist.files
        self.assertTrue(os.path.join('sdist_test', 'a.txt') in manifest)
        self.assertTrue(os.path.join('sdist_test', 'b.txt') in manifest)
        self.assertTrue(os.path.join('sdist_test', 'c.rst') not in manifest)

    def test_manifest_is_written_with_utf8_encoding(self):
        # Test for #303.
        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        mm = manifest_maker(dist)
        mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
        os.mkdir('sdist_test.egg-info')

        # UTF-8 filename
        filename = posixpath.join('sdist_test', 'smörbröd.py')

        # Add UTF-8 filename and write manifest
        quiet()
        try:
            mm.run()
            mm.filelist.files.append(filename)
            mm.write_manifest()
        finally:
            unquiet()

        manifest = open(mm.manifest, 'rbU')
        contents = manifest.read()
        manifest.close()

        # The manifest should be UTF-8 encoded
        try:
            u = contents.decode('UTF-8')
        except UnicodeDecodeError, e:
            self.fail(e)

        # The manifest should contain the UTF-8 filename
        if sys.version_info >= (3,):
            self.assertTrue(filename in u)
        else:
            self.assertTrue(filename in contents)

    def test_manifest_is_written_with_surrogateescape_error_handler(self):
        # Test for #303.
        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        mm = manifest_maker(dist)
        mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
        os.mkdir('sdist_test.egg-info')

        # Latin-1 filename
        filename = posixpath.join(b('sdist_test'), LATIN1_FILENAME)

        # Add filename with surrogates and write manifest
        quiet()
        try:
            mm.run()
            if sys.version_info >= (3,):
                u = filename.decode('utf-8', 'surrogateescape')
                mm.filelist.files.append(u)
            else:
                mm.filelist.files.append(filename)
            mm.write_manifest()
        finally:
            unquiet()

        manifest = open(mm.manifest, 'rbU')
        contents = manifest.read()
        manifest.close()

        # The manifest should contain the Latin-1 filename
        self.assertTrue(filename in contents)

    def test_manifest_is_read_with_utf8_encoding(self):
        # Test for #303.
        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        cmd = sdist(dist)
        cmd.ensure_finalized()

        # UTF-8 filename
        filename = os.path.join('sdist_test', 'smörbröd.py')
        open(filename, 'w').close()

        quiet()
        try:
            cmd.run()
        finally:
            unquiet()

        # The filelist should contain the UTF-8 filename
        if sys.platform == 'darwin':
            filename = decompose(filename)
        self.assertTrue(filename in cmd.filelist.files)

    def test_manifest_is_read_with_surrogateescape_error_handler(self):
        # Test for #303.

        # This is hard to test on HFS Plus because it quotes unknown
        # bytes (see previous test). Furthermore, egg_info.FileList
        # only appends filenames that os.path.exist.

        # We therefore write the manifest file by hand and check whether
        # read_manifest produces a UnicodeDecodeError.
        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        cmd = sdist(dist)
        cmd.ensure_finalized()

        filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)

        quiet()
        try:
            cmd.run()
            # Add Latin-1 filename to manifest
            cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
            manifest = open(cmd.manifest, 'ab')
            manifest.write(filename+b('\n'))
            manifest.close()
            # Re-read manifest
            try:
                cmd.read_manifest()
            except UnicodeDecodeError, e:
                self.fail(e)
        finally:
            unquiet()

    def test_sdist_with_utf8_encoded_filename(self):
        # Test for #303.
        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        cmd = sdist(dist)
        cmd.ensure_finalized()

        # UTF-8 filename
        filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
        open(filename, 'w').close()

        quiet()
        try:
            cmd.run()
        finally:
            unquiet()

        # The filelist should contain the UTF-8 filename
        # (in one representation or other)
        if sys.version_info >= (3,):
            filename = filename.decode(sys.getfilesystemencoding(), 'surrogateescape')
        if sys.platform == 'darwin':
            filename = decompose(filename)
        self.assertTrue(filename in cmd.filelist.files)

    def test_sdist_with_latin1_encoded_filename(self):
        # Test for #303.
        dist = Distribution(SETUP_ATTRS)
        dist.script_name = 'setup.py'
        cmd = sdist(dist)
        cmd.ensure_finalized()

        # Latin-1 filename
        filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
        open(filename, 'w').close()

        quiet()
        try:
            cmd.run()
        finally:
            unquiet()

        # The filelist should contain the Latin-1 filename
        # (in one representation or other)
        if sys.platform == 'darwin':
            filename = hfs_quote(filename)
        elif sys.version_info >= (3,):
            filename = filename.decode(sys.getfilesystemencoding(), 'surrogateescape')
        self.assertTrue(filename in cmd.filelist.files)

    def test_decompose(self):
        self.assertNotEqual('smörbröd.py', decompose('smörbröd.py'))

        if sys.version_info >= (3,):
            self.assertEqual(len('smörbröd.py'), 11)
            self.assertEqual(len(decompose('smörbröd.py')), 13)
        else:
            self.assertEqual(len('smörbröd.py'), 13)
            self.assertEqual(len(decompose('smörbröd.py')), 15)

    def test_hfs_quote(self):
        self.assertEqual(hfs_quote(LATIN1_FILENAME), 'sm%F6rbr%F6d.py')

        # Bytes are required
        if sys.version_info >= (3,):
            self.assertRaises(TypeError, hfs_quote, 'smörbröd.py')
        else:
            self.assertRaises(TypeError, hfs_quote, 'smörbröd.py'.decode('utf-8'))


def test_suite():
    return unittest.defaultTestLoader.loadTestsFromName(__name__)

