# -*- coding: utf-8 -*-
r"""Unit tests

"""

from __future__ import absolute_import, print_function

import os
import shutil
import sys
import unittest

from _test_setup import (DATADIR, TMPDIR)

from cutils.util import walk


class SurrogateEscapeTests(unittest.TestCase):

    @unittest.skipIf(sys.version_info[0] >= 3, "Skip on Python3")
    def test_simple_escape(self):
        w = b"\xc4"

        d = walk.WalkDirEntry.surrogate_decode(w)
        self.assertEqual(u"\udcc4", d)

    @unittest.skipIf(sys.version_info[0] >= 3, "Skip on Python3")
    def test_no_escape_min(self):
        w = b"\x00"

        d = walk.WalkDirEntry.surrogate_decode(w)
        self.assertEqual(u"\x00", d)

    @unittest.skipIf(sys.version_info[0] >= 3, "Skip on Python3")
    def test_no_escape_max(self):
        w = b"\x7f"

        d = walk.WalkDirEntry.surrogate_decode(w)
        self.assertEqual(u"\x7f", d)

    @unittest.skipIf(sys.version_info[0] >= 3, "Skip on Python3")
    def test_escape_min(self):
        w = b"\x80"

        d = walk.WalkDirEntry.surrogate_decode(w)
        self.assertEqual(u"\udc80", d)

    @unittest.skipIf(sys.version_info[0] >= 3, "Skip on Python3")
    def test_escape_max(self):
        w = b"\xff"

        d = walk.WalkDirEntry.surrogate_decode(w)
        self.assertEqual(u"\udcff", d)

    @unittest.skipIf(sys.version_info[0] >= 3, "Skip on Python3")
    def test_complex(self):
        w = b"abc\xc4d\x80\x81\xffefg"
        d = walk.WalkDirEntry.surrogate_decode(w)
        self.assertEqual(u"abc\udcc4d\udc80\udc81\udcffefg", d)


class WalkDirEntryTests(unittest.TestCase):

    def setUp(self):
        self._orig_fsencoding = walk._FSENCODING
        walk._FSENCODING = "ascii"

    def tearDown(self):
        walk._FSENCODING = self._orig_fsencoding

    def test_ascii(self):
        entry = walk.WalkDirEntry.from_path_name("tests", "_test_setup.py")
        self.assertEqual("_test_setup.py", entry.name)
        self.assertEqual("tests/_test_setup.py", entry.path)
        self.assertEqual(u"_test_setup.py", entry.uname)
        self.assertEqual(u"tests/_test_setup.py", entry.upath)
        self.assertEqual(b"_test_setup.py", entry.u8name)
        self.assertEqual(b"tests/_test_setup.py", entry.u8path)
        self.assertEqual(b"_test_setup.py", entry.alt_u8name)
        self.assertEqual(b"tests/_test_setup.py", entry.alt_u8path)
        self.assertEqual(b"_test_setup.py", entry.alt_fsname)
        self.assertEqual(b"tests/_test_setup.py", entry.alt_fspath)

    @unittest.skipIf(sys.version_info[0] < 3, "Skip on Python2")
    def test_with_surrogate_escaped_name(self):
        # instantiate with a surrogate escaped path from PEP 383
        entry = walk.WalkDirEntry.from_path_name(
            "tests", "test-\udcc4", _do_stat=False)
        self.assertEqual("test-\udcc4", entry.name)
        self.assertEqual("tests/test-\udcc4", entry.path)
        self.assertEqual(b"test-\xc4", entry.fsname)
        self.assertEqual(b"tests/test-\xc4", entry.fspath)
        self.assertEqual(b"test-\xc4", entry.alt_fsname)
        self.assertEqual(b"tests/test-\xc4", entry.alt_fspath)

        self.assertIsNone(entry.uname)
        self.assertIsNone(entry.upath)
        self.assertIsNone(entry.u8name)
        self.assertIsNone(entry.u8path)

        self.assertEqual(b"test-\\udcc4", entry.alt_u8name)
        self.assertEqual(b"tests/test-\\udcc4", entry.alt_u8path)

    @unittest.skipIf(sys.version_info[0] < 3, "Skip on Python2")
    def test_with_surrogate_escaped_path(self):
        # instantiate with a surrogate escaped path from PEP 383
        entry = walk.WalkDirEntry.from_path_name(
            "tests\udcc5", "test", _do_stat=False)
        self.assertEqual("test", entry.name)
        self.assertEqual("tests\udcc5/test", entry.path)
        self.assertEqual(b"test", entry.fsname)
        self.assertEqual(b"tests\xc5/test", entry.fspath)
        self.assertEqual(b"test", entry.alt_fsname)
        self.assertEqual(b"tests\xc5/test", entry.alt_fspath)

        self.assertEqual("test", entry.uname)
        self.assertIsNone(entry.upath)
        self.assertEqual(b"test", entry.u8name)
        self.assertIsNone(entry.u8path)

        self.assertEqual(b"test", entry.alt_u8name)
        self.assertEqual(b"tests\\udcc5/test", entry.alt_u8path)

    @unittest.skipIf(sys.version_info[0] > 2, "Skip on Python3")
    def test_py2_with_non_fsdecodable_name(self):
        entry = walk.WalkDirEntry.from_path_name(
            b"tests", b"test-\xc4", _do_stat=False)
        self.assertEqual(b"test-\xc4", entry.name)
        self.assertEqual(b"tests/test-\xc4", entry.path)
        self.assertIsNone(entry.fsname)
        self.assertIsNone(entry.fspath)
        self.assertEqual(b"test-\xc4", entry.alt_fsname)
        self.assertEqual(b"tests/test-\xc4", entry.alt_fspath)

        self.assertIsNone(entry.uname)
        self.assertIsNone(entry.upath)
        self.assertIsNone(entry.u8name)
        self.assertIsNone(entry.u8path)

        self.assertEqual(b"test-\\udcc4", entry.alt_u8name)
        self.assertEqual(b"tests/test-\\udcc4", entry.alt_u8path)

    @unittest.skipIf(sys.version_info[0] > 2, "Skip on Python3")
    def test_py2_with_non_fsdecodable_path(self):
        entry = walk.WalkDirEntry.from_path_name(
            b"tests\xc5", b"test", _do_stat=False)
        self.assertEqual(b"test", entry.name)
        self.assertEqual(b"tests\xc5/test", entry.path)
        self.assertEqual(b"test", entry.fsname)
        self.assertIsNone(entry.fspath)
        self.assertEqual(b"test", entry.alt_fsname)
        self.assertEqual(b"tests\xc5/test", entry.alt_fspath)

        self.assertEqual(b"test", entry.uname)
        self.assertIsNone(entry.upath)
        self.assertEqual(b"test", entry.u8name)
        self.assertIsNone(entry.u8path)

        self.assertEqual(b"test", entry.alt_u8name)
        self.assertEqual(b"tests\\udcc5/test", entry.alt_u8path)

    @unittest.skipIf(sys.version_info[0] > 2, "Skip on Python3")
    def test_py2_with_non_fsencodable_unicode_name(self):
        entry = walk.WalkDirEntry.from_path_name(
            u"tests", u"test-\xc4", _do_stat=False)
        self.assertEqual(u"test-\xc4", entry.name)
        self.assertEqual(u"tests/test-\xc4", entry.path)
        self.assertIsNone(entry.fsname)
        self.assertIsNone(entry.fspath)
        self.assertEqual(b"test-\\xc4", entry.alt_fsname)
        self.assertEqual(b"tests/test-\\xc4", entry.alt_fspath)

        self.assertEqual(u"test-\xc4", entry.uname)
        self.assertEqual(u"tests/test-\xc4", entry.upath)
        self.assertEqual(b"test-\xc3\x84", entry.u8name)
        self.assertEqual(b"tests/test-\xc3\x84", entry.u8path)

        self.assertEqual(b"test-\\xc4", entry.alt_u8name)
        self.assertEqual(b"tests/test-\\xc4", entry.alt_u8path)

    @unittest.skipIf(sys.version_info[0] > 2, "Skip on Python3")
    def test_py2_with_non_fsencodable_unicode_path(self):
        entry = walk.WalkDirEntry.from_path_name(
            u"tests\xc5", u"test", _do_stat=False)
        self.assertEqual(u"test", entry.name)
        self.assertEqual(u"tests\xc5/test", entry.path)
        self.assertEqual(b"test", entry.fsname)
        self.assertIsNone(entry.fspath)
        self.assertEqual(b"test", entry.alt_fsname)
        self.assertEqual(b"tests\\xc5/test", entry.alt_fspath)

        self.assertEqual(u"test", entry.uname)
        self.assertEqual(u"tests\xc5/test", entry.upath)
        self.assertEqual(b"test", entry.u8name)
        self.assertEqual(b"tests\xc3\x85/test", entry.u8path)

        self.assertEqual(b"test", entry.alt_u8name)
        self.assertEqual(b"tests\\xc5/test", entry.alt_u8path)

    def test_special_chars_name(self):
        entry = walk.WalkDirEntry.from_path_name(
            "testdir", "test\r-\n-\\rest", _do_stat=False)
        self.assertEqual(u"test\r-\n-\\rest", entry.uname)
        self.assertIsNone(entry.fsname)
        self.assertIsNone(entry.u8name)
        self.assertEqual(b"test\\x0d-\\x0a-\\x5crest", entry.alt_fsname)
        self.assertEqual(b"test\\x0d-\\x0a-\\x5crest", entry.alt_u8name)

    def test_special_chars_path(self):
        entry = walk.WalkDirEntry.from_path_name(
            "testdir-\r-\n-rest", "testfile", _do_stat=False)
        self.assertEqual(u"testdir-\r-\n-rest/testfile", entry.upath)
        self.assertIsNone(entry.fspath)
        self.assertIsNone(entry.u8path)
        self.assertEqual(b"testdir-\\x0d-\\x0a-rest/testfile",
                         entry.alt_fspath)
        self.assertEqual(b"testdir-\\x0d-\\x0a-rest/testfile",
                         entry.alt_u8path)

    def test_special_chars_fullpath(self):
        entry = walk.WalkDirEntry.from_path_name(
            "testdir-\r-\n-rest", "test-\n-\r-file", _do_stat=False)
        self.assertEqual(u"testdir-\r-\n-rest/test-\n-\r-file", entry.upath)
        self.assertIsNone(entry.fspath)
        self.assertIsNone(entry.u8path)
        self.assertEqual(b"testdir-\\x0d-\\x0a-rest/test-\\x0a-\\x0d-file",
                         entry.alt_fspath)
        self.assertEqual(b"testdir-\\x0d-\\x0a-rest/test-\\x0a-\\x0d-file",
                         entry.alt_u8path)


# https://stackoverflow.com/questions/1854/how-to-identify-which-os-python-is-running-on/58071295#58071295
@unittest.skipIf(
    os.name != "posix", "Only supported on POSIX (needs real symlinks)")
class SymlinkTests(unittest.TestCase):

    def test_symlink_to_directory(self):
        entry = walk.WalkDirEntry.from_path_name(
            DATADIR, "tree-1-s")
        self.assertTrue(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertTrue(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertFalse(entry.is_special)

    def test_broken_symlink_to_directory(self):
        entry = walk.WalkDirEntry.from_path_name(
            DATADIR, "tree-nn-s")
        self.assertTrue(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNone(entry.stat)
        self.assertFalse(entry.is_special)

    def test_real_directory(self):
        entry = walk.WalkDirEntry.from_path_name(
            DATADIR, "tree-1")
        self.assertFalse(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertTrue(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertFalse(entry.is_special)

    def test_symlink_to_file(self):
        entry = walk.WalkDirEntry.from_path_name(
            os.path.join(DATADIR, "tree-1"), "file-1-1-s.txt")
        self.assertTrue(entry.is_symlink)
        self.assertTrue(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertFalse(entry.is_special)

    def test_broken_symlink_to_file(self):
        entry = walk.WalkDirEntry.from_path_name(
            os.path.join(DATADIR, "tree-1"), "file-1-nn-s.txt")
        self.assertTrue(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNone(entry.stat)
        self.assertFalse(entry.is_special)

    def test_real_file(self):
        entry = walk.WalkDirEntry.from_path_name(
            os.path.join(DATADIR, "tree-1"), "file-1-1.txt")
        self.assertFalse(entry.is_symlink)
        self.assertTrue(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertFalse(entry.is_special)

    def test_real_file_via_dir_symlink(self):
        entry = walk.WalkDirEntry.from_path_name(
            os.path.join(DATADIR, "tree-1-s"), "file-1-1.txt")
        self.assertFalse(entry.is_symlink)
        self.assertTrue(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertFalse(entry.is_special)


@unittest.skipIf(not hasattr(os, "mkfifo"), "Needs os.mkfifo()")
class FIFOTests(unittest.TestCase):

    def setUp(self):
        if not os.path.isdir(TMPDIR):
            os.mkdir(TMPDIR)

    def tearDown(self):
        if os.path.isdir(TMPDIR):
            shutil.rmtree(TMPDIR)

    def test_real_mkfifo(self):
        fifopath = os.path.join(TMPDIR, "test.fifo")
        os.mkfifo(fifopath)
        entry = walk.WalkDirEntry.from_path_name(TMPDIR, "test.fifo")
        self.assertFalse(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertTrue(entry.is_fifo)
        self.assertTrue(entry.is_special)

    def test_symlink_to_mkfifo(self):
        fifopath = os.path.join(TMPDIR, "test.fifo")
        fifo_s_path = os.path.join(TMPDIR, "test-s.fifo")
        os.mkfifo(fifopath)
        os.symlink(fifopath, fifo_s_path)
        entry = walk.WalkDirEntry.from_path_name(TMPDIR, "test-s.fifo")
        self.assertTrue(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNotNone(entry.stat)
        self.assertTrue(entry.is_fifo)
        self.assertTrue(entry.is_special)

    def test_broken_symlink_to_mkfifo(self):
        fifopath = os.path.join(TMPDIR, "test.fifo")
        fifo_s_path = os.path.join(TMPDIR, "test-s.fifo")
        os.mkfifo(fifopath)
        os.symlink(fifopath, fifo_s_path)
        os.unlink(fifopath)
        entry = walk.WalkDirEntry.from_path_name(TMPDIR, "test-s.fifo")
        self.assertTrue(entry.is_symlink)
        self.assertFalse(entry.is_reg)
        self.assertFalse(entry.is_dir)
        self.assertIsNone(entry.stat)
        self.assertFalse(entry.is_fifo)
        self.assertFalse(entry.is_special)


if __name__ == "__main__":
    sys.exit(unittest.main())
