"""

  fs.tests.test_expose:  testcases for fs.expose and associated FS classes

"""

import unittest
import sys
import os
import os.path
import socket
import threading
import time

from fs.tests import FSTestCases, ThreadingTestCases
from fs.tempfs import TempFS
from fs.osfs import OSFS
from fs.memoryfs import MemoryFS
from fs.path import *
from fs.errors import *

from fs import rpcfs
from fs.expose.xmlrpc import RPCFSServer

import six
from six import PY3, b

from fs.tests.test_rpcfs import TestRPCFS

try:
    from fs import sftpfs
    from fs.expose.sftp import BaseSFTPServer
except ImportError:
    if not PY3:
        raise

import logging
logging.getLogger('paramiko').setLevel(logging.ERROR)
logging.getLogger('paramiko.transport').setLevel(logging.ERROR)


class TestSFTPFS(TestRPCFS):

    __test__ = not PY3

    def makeServer(self,fs,addr):
        return BaseSFTPServer(addr,fs)

    def setUp(self):
        self.startServer()
        self.fs = sftpfs.SFTPFS(self.server_addr, no_auth=True)

    def bump(self):
        # paramiko doesn't like being bumped, just wait for it to timeout.
        # TODO: do this using a paramiko.Transport() connection
        pass


try:
    from fs.expose import fuse
except ImportError:
    pass
else:
    from fs.osfs import OSFS
    class TestFUSE(unittest.TestCase, FSTestCases, ThreadingTestCases):

        def setUp(self):
            self.temp_fs = TempFS()
            self.temp_fs.makedir("root")
            self.temp_fs.makedir("mount")
            self.mounted_fs = self.temp_fs.opendir("root")
            self.mount_point = self.temp_fs.getsyspath("mount")
            self.fs = OSFS(self.temp_fs.getsyspath("mount"))
            self.mount_proc = fuse.mount(self.mounted_fs, self.mount_point)

        def tearDown(self):
            self.mount_proc.unmount()
            try:
                self.temp_fs.close()
            except OSError:
                # Sometimes FUSE hangs onto the mountpoint if mount_proc is
                # forcibly killed.  Shell out to fusermount to make sure.
                fuse.unmount(self.mount_point)
                self.temp_fs.close()

        def check(self, p):
            return self.mounted_fs.exists(p)


from fs.expose import dokan
if dokan.is_available:
    from fs.osfs import OSFS
    class DokanTestCases(FSTestCases):
        """Specialised testcases for filesystems exposed via Dokan.

        This modifies some of the standard tests to work around apparent
        bugs in the current Dokan implementation.
        """

        def test_remove(self):
            self.fs.createfile("a.txt")
            self.assertTrue(self.check("a.txt"))
            self.fs.remove("a.txt")
            self.assertFalse(self.check("a.txt"))
            self.assertRaises(ResourceNotFoundError,self.fs.remove,"a.txt")
            self.fs.makedir("dir1")
            #  This appears to be a bug in Dokan - DeleteFile will happily
            #  delete an empty directory.
            #self.assertRaises(ResourceInvalidError,self.fs.remove,"dir1")
            self.fs.createfile("/dir1/a.txt")
            self.assertTrue(self.check("dir1/a.txt"))
            self.fs.remove("dir1/a.txt")
            self.assertFalse(self.check("/dir1/a.txt"))

        def test_open_on_directory(self):
            #  Dokan seems quite happy to ask me to open a directory and
            #  then treat it like a file.
            pass

        def test_settimes(self):
            #  Setting the times does actually work, but there's some sort
            #  of caching effect which prevents them from being read back
            #  out.  Disabling the test for now.
            pass

        def test_safety_wrapper(self):
            rawfs = MemoryFS()
            safefs = dokan.Win32SafetyFS(rawfs)
            rawfs.setcontents("autoRun.inf", b("evilcodeevilcode"))
            self.assertTrue(safefs.exists("_autoRun.inf"))
            self.assertTrue("autoRun.inf" not in safefs.listdir("/"))
            safefs.setcontents("file:stream",b("test"))
            self.assertFalse(rawfs.exists("file:stream"))
            self.assertTrue(rawfs.exists("file__colon__stream"))
            self.assertTrue("file:stream" in safefs.listdir("/"))

    class TestDokan(unittest.TestCase,DokanTestCases,ThreadingTestCases):

        def setUp(self):
            self.temp_fs = TempFS()
            self.drive = "K"
            while os.path.exists(self.drive+":\\") and self.drive <= "Z":
                self.drive = chr(ord(self.drive) + 1)
            if self.drive > "Z":
                raise RuntimeError("no free drive letters")
            fs_to_mount = OSFS(self.temp_fs.getsyspath("/"))
            self.mount_proc = dokan.mount(fs_to_mount,self.drive)#,flags=dokan.DOKAN_OPTION_DEBUG|dokan.DOKAN_OPTION_STDERR,numthreads=1)
            self.fs = OSFS(self.mount_proc.path)

        def tearDown(self):
            self.mount_proc.unmount()
            for _ in range(10):
                try:
                    if self.mount_proc.poll() is None:
                        self.mount_proc.terminate()
                except EnvironmentError:
                    time.sleep(0.1)
                else:
                    break
            else:
                if self.mount_proc.poll() is None:
                    self.mount_proc.terminate()
            self.temp_fs.close()

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