From fb24b240aa95758f53309b37b489ad5e8070111f Mon Sep 17 00:00:00 2001
From: catch <6915-catch@users.noreply.drupalcode.org>
Date: Mon, 14 Apr 2025 02:43:08 +0100
Subject: [PATCH] Issue #3513618 by mstrelan, mondrake: [random test failure]
 ConnectionUnitTest::testOpenQueryClose

---
 .../DriverSpecificConnectionUnitTestBase.php  | 42 +++++++++++++++----
 1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/core/tests/Drupal/KernelTests/Core/Database/DriverSpecificConnectionUnitTestBase.php b/core/tests/Drupal/KernelTests/Core/Database/DriverSpecificConnectionUnitTestBase.php
index 2af1bec7d0dc..9bd8f14d2030 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/DriverSpecificConnectionUnitTestBase.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/DriverSpecificConnectionUnitTestBase.php
@@ -95,7 +95,39 @@ protected function assertConnection(int $id): void {
    * @internal
    */
   protected function assertNoConnection(int $id): void {
-    $this->assertArrayNotHasKey($id, $this->monitor->query($this->getQuery()['processlist'])->fetchAllKeyed(0, 0));
+    // Wait up to 100ms to give the database engine sufficient time to react.
+    $this->assertTrue($this->waitFor(0.1, function () use ($id) {
+      $key = $this->monitor->query($this->getQuery()['processlist'])->fetchAllKeyed(0, 0);
+      return !array_key_exists($id, $key);
+    }));
+  }
+
+  /**
+   * Wait for a callback to return a truthy value.
+   *
+   * @param int|float $timeout
+   *   Number of seconds to wait for.
+   * @param callable $callback
+   *   The callback to call.
+   *
+   * @return mixed
+   *   The result of the callback.
+   */
+  protected function waitFor(int|float $timeout, callable $callback): mixed {
+    $start = microtime(TRUE);
+    $end = $start + $timeout;
+
+    do {
+      $result = call_user_func($callback, $this);
+
+      if ($result) {
+        break;
+      }
+
+      usleep(10000);
+    } while (microtime(TRUE) < $end);
+
+    return $result;
   }
 
   /**
@@ -106,8 +138,6 @@ protected function assertNoConnection(int $id): void {
   public function testOpenClose(): void {
     // Close the connection.
     Database::closeConnection(static::TEST_TARGET_CONNECTION);
-    // Wait 20ms to give the database engine sufficient time to react.
-    usleep(20000);
 
     // Verify that we are back to the original connection count.
     $this->assertNoConnection($this->id);
@@ -122,8 +152,6 @@ public function testOpenQueryClose(): void {
 
     // Close the connection.
     Database::closeConnection(static::TEST_TARGET_CONNECTION);
-    // Wait 20ms to give the database engine sufficient time to react.
-    usleep(20000);
 
     // Verify that we are back to the original connection count.
     $this->assertNoConnection($this->id);
@@ -138,8 +166,6 @@ public function testOpenQueryPrefetchClose(): void {
 
     // Close the connection.
     Database::closeConnection(static::TEST_TARGET_CONNECTION);
-    // Wait 20ms to give the database engine sufficient time to react.
-    usleep(20000);
 
     // Verify that we are back to the original connection count.
     $this->assertNoConnection($this->id);
@@ -171,8 +197,6 @@ public function testOpenSelectQueryClose(): void {
 
     // Close the connection.
     Database::closeConnection(static::TEST_TARGET_CONNECTION);
-    // Wait 20ms to give the database engine sufficient time to react.
-    usleep(20000);
 
     // Verify that we are back to the original connection count.
     $this->assertNoConnection($this->id);
-- 
GitLab