import subprocess
import os
import time
import StringIO
import unittest
import tempfile
import shutil
import uuid
import sys

from pynailgun import NailgunException, NailgunConnection


POSSIBLE_NAILGUN_CODES_ON_NG_STOP = [
    NailgunException.CONNECT_FAILED,
    NailgunException.CONNECTION_BROKEN,
    NailgunException.UNEXPECTED_CHUNKTYPE,
]


if os.name == 'posix':
    def transport_exists(transport_file):
        return os.path.exists(transport_file)


if os.name == 'nt':
    import ctypes
    from ctypes.wintypes import WIN32_FIND_DATAW as WIN32_FIND_DATA
    INVALID_HANDLE_VALUE = -1
    FindFirstFile = ctypes.windll.kernel32.FindFirstFileW
    FindClose = ctypes.windll.kernel32.FindClose

    # on windows os.path.exists doen't allow to check reliably that a pipe exists
    # (os.path.exists tries to open connection to a pipe)
    def transport_exists(transport_path):
        wfd = WIN32_FIND_DATA()
        handle = FindFirstFile(transport_path, ctypes.byref(wfd))
        result = handle != INVALID_HANDLE_VALUE
        FindClose(handle)
        return result


class TestNailgunConnection(unittest.TestCase):
    def setUp(self):
        self.setUpTransport()
        self.startNailgun()

    def setUpTransport(self):
        self.tmpdir = tempfile.mkdtemp()
        if os.name == 'posix':
            self.transport_file = os.path.join(self.tmpdir, 'sock')
            self.transport_address = 'local:{0}'.format(self.transport_file)
        else:
            pipe_name = u'nailgun-test-{0}'.format(uuid.uuid4().hex)
            self.transport_address = u'local:{0}'.format(pipe_name)
            self.transport_file = ur'\\.\pipe\{0}'.format(pipe_name)

    def getNailgunUberJar(self):
        return 'nailgun-server/target/nailgun-server-0.9.2-SNAPSHOT-uber.jar'

    def startNailgun(self):
        if os.name == 'posix':
            def preexec_fn():
                # Close any open file descriptors to further separate buckd from its
                # invoking context (e.g. otherwise we'd hang when running things like
                # `ssh localhost buck clean`).
                dev_null_fd = os.open("/dev/null", os.O_RDWR)
                os.dup2(dev_null_fd, 0)
                os.dup2(dev_null_fd, 1)
                os.dup2(dev_null_fd, 2)
                os.close(dev_null_fd)
            creationflags = 0
        else:
            preexec_fn = None
            # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx#DETACHED_PROCESS
            DETACHED_PROCESS = 0x00000008
            creationflags = DETACHED_PROCESS

        self.ng_server_process = subprocess.Popen(
            ['java', '-Djna.nosys=true', '-jar', self.getNailgunUberJar(), self.transport_address],
            close_fds=True,
            preexec_fn=preexec_fn,
            creationflags=creationflags,
        )

        self.assertIsNone(self.ng_server_process.poll())

        # Give Java some time to create the listening socket.
        for i in range(0, 600):
            if not transport_exists(self.transport_file):
                time.sleep(0.01)
            else:
                break

        self.assertTrue(transport_exists(self.transport_file))

    def test_nailgun_stats_and_stop(self):
        for i in range(1, 5):
            output = StringIO.StringIO()
            with NailgunConnection(
                    self.transport_address,
                    stderr=None,
                    stdin=None,
                    stdout=output) as c:
                exit_code = c.send_command('ng-stats')
                self.assertEqual(exit_code, 0)
            actual_out = output.getvalue().strip()
            expected_out = 'com.martiansoftware.nailgun.builtins.NGServerStats: {0}/1'.format(i)
            self.assertEqual(actual_out, expected_out)

        try:
            with NailgunConnection(
                    self.transport_address,
                    cwd=os.getcwd(),
                    stderr=None,
                    stdin=None,
                    stdout=None) as c:
                c.send_command('ng-stop')
        except NailgunException as e:
            self.assertIn(e.code, POSSIBLE_NAILGUN_CODES_ON_NG_STOP)

        self.ng_server_process.wait()
        self.assertEqual(self.ng_server_process.poll(), 0)

    def tearDown(self):
        if self.ng_server_process.poll() is None:
            # some test has failed, ng-server was not stopped. killing it
            self.ng_server_process.kill()
        shutil.rmtree(self.tmpdir)


if __name__ == '__main__':
    for i in range(50):
        was_successful = unittest.main(exit=False).result.wasSuccessful()
        if not was_successful:
            sys.exit(1)

@@ -2197,48 +2197,8 @@
  0)%0A
-                os.dup2(dev_null_fd, 1)%0A
     
@@ -2233,16 +2233,16 @@
 _fd, 2)%0A
+
         
@@ -2535,16 +2535,104 @@
 ROCESS%0A%0A
+        stdout = None%0A        if os.name == 'posix':%0A            stdout=subprocess.PIPE%0A
         
@@ -2778,36 +2778,8 @@
 s%5D,%0A
-            close_fds=True,%0A
     
@@ -2850,16 +2850,43 @@
 nflags,%0A
+            stdout=stdout,%0A
         
@@ -2942,24 +2942,59 @@
 ss.poll())%0A%0A
+        if os.name == 'posix':%0A    
         # Gi
@@ -2995,60 +2995,292 @@
   # 
-Give Java some time to create the listening socket.%0A
+on *nix we have to for server to listen on bound socket%0A            the_first_line = self.ng_server_process.stdout.readline().strip()%0A            self.assertTrue(%22NGServer%22 in the_first_line and %22started%22 in the_first_line, %22Got a line: %7B0%7D%22.format(the_first_line))%0A        else:%0A    
     
@@ -3279,33 +3279,33 @@
             for 
-i
+_
  in range(0, 600
@@ -3303,24 +3303,104 @@
 ge(0, 600):%0A
+                # on windows it is OK to rely on existence of the pipe file%0A    
             
@@ -3461,16 +3461,20 @@
         
+    
 time.sle
@@ -3486,38 +3486,46 @@
 01)%0A            
+    
 else:%0A
+    
                 

test fix