[PATCH stable?] run-test: ensure the test port are available before launching test

Pierre-Yves David pierre-yves.david at ens-lyon.org
Fri May 8 01:21:31 UTC 2015


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1431044040 25200
#      Thu May 07 17:14:00 2015 -0700
# Node ID c25b2adb3664cd3c488e2c53aab0c64100d40af7
# Parent  17ba4ccd20b48511b3d06ab47fb1b2faf31410d7
run-test: ensure the test port are available before launching test

I'm running into systematic issue because there is always some port taken in the
1500 wide range of port used by the test (3 for each test file).

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -47,10 +47,11 @@ import errno
 import optparse
 import os
 import shutil
 import subprocess
 import signal
+import socket
 import sys
 import tempfile
 import time
 import random
 import re
@@ -76,10 +77,22 @@ processlock = threading.Lock()
 if sys.version_info < (2, 5):
     subprocess._cleanup = lambda: None
 
 wifexited = getattr(os, "WIFEXITED", lambda x: False)
 
+def checkportisavailable(port):
+    """return true is nobody seem 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()
     p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd, env=env,
                          close_fds=closefds,
@@ -1601,10 +1614,12 @@ class TestRunner(object):
         self._tmpbinddir = None
         self._pythondir = None
         self._coveragefile = None
         self._createdfiles = []
         self._hgpath = None
+        self._portoffset = 0
+        self._ports = {}
 
     def run(self, args, parser=None):
         """Run the test suite."""
         oldmask = os.umask(022)
         try:
@@ -1805,10 +1820,28 @@ class TestRunner(object):
         if failed:
             return 1
         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.
 
         Returns a Test instance. The Test may not be runnable if it doesn't
         map to a known type.
@@ -1826,11 +1859,11 @@ class TestRunner(object):
 
         t = testcls(refpath, tmpdir,
                     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)
         t.should_reload = True
         return t


More information about the Mercurial-devel mailing list