run-test: ensure the test ports are available before launching test
authorPierre-Yves David <pierre-yves.david@fb.com>
Thu, 07 May 2015 17:14:00 -0700
changeset 24967 00790cc2b753
parent 24966 554d6fcc3c84
child 24968 80c9e99d68e0
run-test: ensure the test ports are available before launching test I'm running into a systematic issue because there is always some port taken in the 1500-wide range of ports used by the test (3 for each test file).
tests/run-tests.py
--- a/tests/run-tests.py	Fri May 08 18:11:26 2015 +0900
+++ b/tests/run-tests.py	Thu May 07 17:14:00 2015 -0700
@@ -49,6 +49,7 @@
 import shutil
 import subprocess
 import signal
+import socket
 import sys
 import tempfile
 import time
@@ -78,6 +79,18 @@
 
 wifexited = getattr(os, "WIFEXITED", lambda x: False)
 
+def checkportisavailable(port):
+    """return true if a port seems free to bind on localhost"""
+    try:
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.bind(('localhost', port))
+        s.close()
+        return True
+    except socket.error, exc:
+        if not exc.errno == errno.EADDRINUSE:
+            raise
+        return False
+
 closefds = os.name == 'posix'
 def Popen4(cmd, wd, timeout, env=None):
     processlock.acquire()
@@ -1608,6 +1621,8 @@
         self._coveragefile = None
         self._createdfiles = []
         self._hgpath = None
+        self._portoffset = 0
+        self._ports = {}
 
     def run(self, args, parser=None):
         """Run the test suite."""
@@ -1812,6 +1827,24 @@
         if warned:
             return 80
 
+    def _getport(self, count):
+        port = self._ports.get(count) # do we have a cached entry?
+        if port is None:
+            port = self.options.port + self._portoffset
+            portneeded = 3
+            # above 100 tries we just give up and let test reports failure
+            for tries in xrange(100):
+                allfree = True
+                for idx in xrange(portneeded):
+                    if not checkportisavailable(port + idx):
+                        allfree = False
+                        break
+                self._portoffset += portneeded
+                if allfree:
+                    break
+            self._ports[count] = port
+        return port
+
     def _gettest(self, test, count):
         """Obtain a Test by looking at its filename.
 
@@ -1833,7 +1866,7 @@
                     keeptmpdir=self.options.keep_tmpdir,
                     debug=self.options.debug,
                     timeout=self.options.timeout,
-                    startport=self.options.port + count * 3,
+                    startport=self._getport(count),
                     extraconfigopts=self.options.extra_config_opt,
                     py3kwarnings=self.options.py3k_warnings,
                     shell=self.options.shell)