diff --git a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php index cebae20f0b..ea477fa4a9 100644 --- a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php +++ b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php @@ -322,11 +322,13 @@ protected function instantiateServer($hostname, $port, $docroot = NULL) { ->start(); // Wait until the web server has started. It is started if the port is no // longer available. - while ($available_port = $this->findAvailablePort($port)) { - if ($available_port !== $port) { + for ($i = 0; $i < 1000; $i++) { + if (!$this->checkPortIsAvailable($port)) { return $ps; } + usleep(1000); } + throw new \RuntimeException('Unable to start the web server.'); } /** @@ -345,37 +347,49 @@ protected function stopServer() { /** * Discover an available port number. * - * @param int $min - * The lowest port number to start with. - * @param int $max - * The highest port number to allow. - * * @return int * The available port number that we discovered. * * @throws \RuntimeException * Thrown when there are no available ports within the range. */ - protected function findAvailablePort($min = 8000, $max = 8100) { - // If we can open the port, then it's unavailable to us. - for ($port = $min; $port <= $max; $port++) { - if ($this->lockAcquired($port)) { - $fp = @fsockopen(static::$hostName, $port, $errno, $errstr, 1); - // If fsockopen() fails to connect, probably nothing is listening. - // It could be a firewall but that's impossible to detect, so as a - // best guess let's return it as available. - if ($fp === FALSE) { - return $port; - } - else { - $this->lockRelease($port); - fclose($fp); - } + protected function findAvailablePort() { + $counter = 100; + while ($counter--) { + $port = unpack('n', random_bytes(2))[1] % 64511 + 1024; + if ($this->checkPortIsAvailable($port)) { + return $port; } } throw new \RuntimeException('Unable to find a port available to run the web server.'); } + /** + * Checks whether a port is available. + * + * @param $port + * A number between 1024 and 65536. + * + * @return bool + */ + protected function checkPortIsAvailable($port) { + if ($this->lockAcquired($port)) { + $fp = @fsockopen(static::$hostName, $port, $errno, $errstr, 1); + // If fsockopen() fails to connect, probably nothing is listening. + // It could be a firewall but that's impossible to detect, so as a + // best guess let's return it as available. + if ($fp === FALSE) { + return TRUE; + } + else { + $this->lockRelease($port); + fclose($fp); + } + } + return FALSE; + } + + /** * Get the port number for requests. * diff --git a/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php b/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php index 99ae014de6..4b21421250 100644 --- a/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php +++ b/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php @@ -126,26 +126,6 @@ public function testCopyCodebaseExclude() { $base->tearDown(); } - /** - * @covers ::findAvailablePort - */ - public function testPort() { - $iterator = (new Finder())->in($this->getDrupalRoot()) - ->ignoreDotFiles(FALSE) - ->path('/^.ht.router.php$/') - ->exclude(['sites/simpletest']) - ->getIterator(); - $this->copyCodebase($iterator); - // We should find the same port twice in a row, since we don't use it. - $this->assertEquals($this->findAvailablePort(), $this->findAvailablePort()); - - // Grab a port, make a server, make sure the next port we grab is not the - // same. - $port = $this->findAvailablePort(); - $this->instantiateServer(static::$hostName, $port); - $this->assertNotEquals($port, $this->findAvailablePort()); - } - /** * @covers ::findAvailablePort */ @@ -165,6 +145,7 @@ public function testPortMany() { $processes[$port] = $this->instantiateServer(static::$hostName, $port); $this->assertNotEmpty($processes[$port]); $this->assertTrue($processes[$port] ->isRunning(), 'Process on port ' . $port . ' is not still running.'); + $this->assertFalse($this->checkPortIsAvailable($port)); } }