Commits

Anonymous committed 343eebd

Added timeout and os.kill to run_tests_sub.py, uses async_sub.py asynchronous Popen. Requires win32 extensions.

  • Participants
  • Parent commits 631af70

Comments (0)

Files changed (2)

File async_sub.py

+import os
+import subprocess
+import errno
+import time
+import sys
+
+PIPE = subprocess.PIPE
+
+if subprocess.mswindows:
+    # sys.path.append('async_libs.zip')
+    from win32file import ReadFile, WriteFile
+    from win32pipe import PeekNamedPipe
+
+    import msvcrt
+else:
+    import select
+    import fcntl
+
+class Popen(subprocess.Popen):
+    def recv(self, maxsize=None):
+        return self._recv('stdout', maxsize)
+    
+    def recv_err(self, maxsize=None):
+        return self._recv('stderr', maxsize)
+
+    def send_recv(self, input='', maxsize=None):
+        return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
+    
+    def read_async(self,  wait=.1, e=1, tr=5, stderr=0):
+        if tr < 1:
+            tr = 1
+        x = time.time()+ wait
+        y = []
+        r = ''
+        pr = self.recv
+        if stderr:
+            pr = self.recv_err
+        while time.time() < x or r:
+            r = pr()
+            if r is None:
+                if e:
+                    raise Exception("Other end disconnected!")
+                else:
+                    break
+            elif r:
+                y.append(r)
+            else:
+                time.sleep(max((x-time.time())/tr, 0))
+        return ''.join(y)
+        
+    def send_all(self, data):
+        data += '\r\n'
+        while len(data):
+            sent = self.send(data)
+            if sent is None:
+                raise Exception("Other end disconnected!")
+            data = buffer(data, sent)
+    
+    def get_conn_maxsize(self, which, maxsize):
+        if maxsize is None:
+            maxsize = 1024
+        elif maxsize < 1:
+            maxsize = 1
+        return getattr(self, which), maxsize
+    
+    def _close(self, which):
+        getattr(self, which).close()
+        setattr(self, which, None)
+    
+    if subprocess.mswindows:
+        def send(self, input):
+            if not self.stdin:
+                return None
+
+            try:
+                x = msvcrt.get_osfhandle(self.stdin.fileno())
+                (errCode, written) = WriteFile(x, input)
+            except ValueError:
+                return self._close('stdin')
+            except (subprocess.pywintypes.error, Exception), why:
+                if why[0] in (109, errno.ESHUTDOWN):
+                    return self._close('stdin')
+                raise
+
+            return written
+
+        def _recv(self, which, maxsize):
+            conn, maxsize = self.get_conn_maxsize(which, maxsize)
+            if conn is None:
+                return None
+            
+            try:
+                x = msvcrt.get_osfhandle(conn.fileno())
+                (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
+                if maxsize < nAvail:
+                    nAvail = maxsize
+                if nAvail > 0:
+                    (errCode, read) = ReadFile(x, nAvail, None)
+            except ValueError:
+                return self._close(which)
+            except (subprocess.pywintypes.error, Exception), why:
+                if why[0] in (109, errno.ESHUTDOWN):
+                    return self._close(which)
+                raise
+            
+            if self.universal_newlines:
+                read = self._translate_newlines(read)
+            return read
+
+    else:
+        def send(self, input):
+            if not self.stdin:
+                return None
+
+            if not select.select([], [self.stdin], [], 0)[1]:
+                return 0
+
+            try:
+                written = os.write(self.stdin.fileno(), input)
+            except OSError, why:
+                if why[0] == errno.EPIPE: #broken pipe
+                    return self._close('stdin')
+                raise
+
+            return written
+
+        def _recv(self, which, maxsize):
+            conn, maxsize = self.get_conn_maxsize(which, maxsize)
+            if conn is None:
+                return None
+            
+            flags = fcntl.fcntl(conn, fcntl.F_GETFL)
+            if not conn.closed:
+                fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+            
+            try:
+                if not select.select([conn], [], [], 0)[0]:
+                    return ''
+                
+                r = conn.read(maxsize)
+                if not r:
+                    return self._close(which)
+    
+                if self.universal_newlines:
+                    r = self._translate_newlines(r)
+                return r
+            finally:
+                if not conn.closed:
+                    fcntl.fcntl(conn, fcntl.F_SETFL, flags)
+
+if __name__ == '__main__':
+    if sys.platform == 'win32':
+        shell, commands, tail = ('cmd', ('echo "hello"', 'echo "HELLO WORLD"'), '\r\n')
+    else:
+        shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n')
+    
+    a = Popen(shell, stdin=PIPE, stdout=PIPE)
+    print a.read_async(),
+    for cmd in commands:
+        a.send_all(cmd + tail)
+        print a.read_async(),
+    a.send_all('exit' + tail)
+    print a.read_async(e=0)
+    a.wait()

File run_tests_sub.py

 #!/usr/bin/env python
 
+"""
+
+Runs tests in subprocesses. Requires win32 extensions for async_sub
+
+"""
+
 #################################### IMPORTS ###################################
 
-import sys, os, re, unittest, subprocess, time
+import sys, os, re, unittest, subprocess, time, pygame.threads, async_sub
 
 main_dir = os.path.split(os.path.abspath(sys.argv[0]))[0]
 test_subdir = os.path.join(main_dir, 'test')
 
 sys.path += [test_subdir] 
 
-import test_utils, pygame.threads
+import test_utils
 
 ################################### CONSTANTS ##################################
 
+TIME_OUT = 30
+
 IGNORE = (
 	"scrap_test.py",
 )
 
+DIV = (70 * "-") + "\nRan"
+
+################################################################################
+
 TEST_MODULE_RE = re.compile('^(.+_test\.py)$')
 
 NUM_TESTS_RE   = re.compile(r"Ran (\d+) tests?")
 NUM_FAILS_RE   = re.compile(r"failures=(\d+)")
 NUM_ERRORS_RE  = re.compile(r"errors=(\d+)")
 
-DIV = (70 * "-") + "\nRan"
+def count_of(regex, test_output):
+    count = regex.search(test_output)
+    return count and int(count.group(1)) or 0
 
 ################################################################################
 
+if sys.platform == 'win32':
+    if not os.system('taskkill /? >> nul'):
+        os.kill = lambda pid: os.system('taskkill /F /T /PID %s' % pid)
+    else:
+        raise Exception('No way of killing unruly processes')
+        
+################################################################################
+
 def run_test(cmd):
     test_name = os.path.basename(cmd)
     print 'running %s' % test_name
 
-    proc = subprocess.Popen (
+    proc = async_sub.Popen (
         cmd, shell = True, bufsize = -1,
-        stdout = subprocess.PIPE, stderr = subprocess.STDOUT
+        stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
+        stderr = subprocess.STDOUT, universal_newlines = 1
     )
 
-    ret_code = proc.wait()
-    response = proc.stdout.read().replace("\r\n", "\n").replace("\r", "\n")
+    ret_code = None
+    response = []
+
+    t = time.time()
+    while ret_code is None and ((time.time() -t) < TIME_OUT):
+        ret_code = proc.poll()
+        response += [proc.read_async(wait=0.1, e=0)]
+
+    if ret_code is None:
+        os.kill(proc.pid)
+        ret_code = 'Process timed out'
+
+    response = ''.join(response)
 
     return test_name, ret_code, response
 
 ################################################################################
 
-def count_of(regex, test_output):
-    count = regex.search(test_output)
-    return count and int(count.group(1)) or 0
-
-################################################################################
-
 os.chdir(main_dir) 
 test_cmds = [('python test/%s' % f) for f in os.listdir(test_subdir) 
                                           if TEST_MODULE_RE.match(f)