This is an automated email from the ASF dual-hosted git repository.

yuqi4733 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 709759510b [#10235]fix(test): improve test stability by handling stale 
elements and ensuring HDFS readiness (#10240)
709759510b is described below

commit 709759510bdcc04042bd8f1745e113796da0cc38
Author: geyanggang <[email protected]>
AuthorDate: Thu Mar 5 18:58:08 2026 +0800

    [#10235]fix(test): improve test stability by handling stale elements and 
ensuring HDFS readiness (#10240)
    
    ### What changes were proposed in this pull request?
    
    This PR fixes two flaky test issues:
    1. Added retry logic in `CatalogsPage.verifyTreeNodes()` to handle
    StaleElementReferenceException that occurs when DOM is updated during
    element traversal
    2. Added HDFS health check in `launch.sh` to ensure DataNode is fully
    ready before running tests
    
    
    ### Why are the changes needed?
    
    These changes improve CI test stability:
    1. The StaleElementReferenceException occurs when Selenium tries to
    access DOM elements that have been refreshed/updated. Retry logic
    ensures tests wait for DOM to stabilize.
    2. HDFS DataNode needs time to fully initialize after container startup.
    Without proper health checks, tests may fail when trying to write
    Iceberg metadata files before HDFS is ready.
    
    
    Fix: #10235
    
    ### Does this PR introduce _any_ user-facing change?
    
    No
    
    ### How was this patch tested?
    
    1. Manually tested web-v2 integration tests with page refresh scenarios
    2. Manually tested Trino connector integration tests with HDFS
    operations
    3. Verified tests pass consistently in local environment
---
 integration-test-common/docker-script/launch.sh    | 23 +++++++++
 .../test/web/ui/pages/CatalogsPage.java            | 56 +++++++++++++++-------
 2 files changed, 61 insertions(+), 18 deletions(-)

diff --git a/integration-test-common/docker-script/launch.sh 
b/integration-test-common/docker-script/launch.sh
index 1cc340de29..e75d74da72 100755
--- a/integration-test-common/docker-script/launch.sh
+++ b/integration-test-common/docker-script/launch.sh
@@ -74,5 +74,28 @@ while true; do
     sleep 1
 done
 
+# Wait for HDFS to be fully ready
+echo "Waiting for HDFS to be fully ready..."
+hdfs_attempts=0
+hdfs_max_attempts=60
+
+while true; do
+    # Check if HDFS DataNode is ready by testing file creation
+    docker compose exec -T hive hdfs dfs -test -d / >/dev/null 2>&1 && \
+    docker compose exec -T hive hdfs dfsadmin -report 2>/dev/null | grep -q 
"Live datanodes" && {
+        echo "HDFS is ready."
+        # Give HDFS a bit more time to stabilize
+        sleep 5
+        break;
+    }
+
+    if [ "$hdfs_attempts" -ge "$hdfs_max_attempts" ]; then
+        echo "WARNING: HDFS did not become fully ready within 
$hdfs_max_attempts seconds, but continuing..."
+        break
+    fi
+
+    ((hdfs_attempts++))
+    sleep 1
+done
 
 echo "All docker compose service is now available."
diff --git 
a/web-v2/integration-test/src/test/java/org/apache/gravitino/integration/test/web/ui/pages/CatalogsPage.java
 
b/web-v2/integration-test/src/test/java/org/apache/gravitino/integration/test/web/ui/pages/CatalogsPage.java
index 5ea601cfd6..2b6eeaeb32 100644
--- 
a/web-v2/integration-test/src/test/java/org/apache/gravitino/integration/test/web/ui/pages/CatalogsPage.java
+++ 
b/web-v2/integration-test/src/test/java/org/apache/gravitino/integration/test/web/ui/pages/CatalogsPage.java
@@ -1618,25 +1618,45 @@ public class CatalogsPage extends BaseWebIT {
 
   public boolean verifyTreeNodes(List<String> treeNodes) {
     WebDriverWait wait = new WebDriverWait(driver, 
Duration.ofSeconds(ACTION_SLEEP));
-    List<WebElement> list =
-        wait.until(
-            ExpectedConditions.visibilityOfAllElementsLocatedBy(
-                By.xpath(
-                    "//div[@data-refer='tree-view']"
-                        + "//div[@class='ant-tree-list-holder']"
-                        + "/div/div[@class='ant-tree-list-holder-inner']"
-                        + "/div[contains(@class, 'ant-tree-treenode')]")));
-    List<String> texts = new ArrayList<>();
-    for (WebElement webElement : list) {
-      String nodeName =
-          
webElement.findElement(By.xpath(".//span[@class='ant-tree-title']")).getText();
-      texts.add(nodeName);
-    }
-    if (!treeNodes.containsAll(texts)) {
-      LOG.error("tree nodes list: {} does not containsAll treeNodes: {}", 
texts, treeNodes);
-      return false;
+
+    // Retry logic to handle stale element references
+    int maxRetries = 3;
+    for (int attempt = 0; attempt < maxRetries; attempt++) {
+      try {
+        List<WebElement> list =
+            wait.until(
+                ExpectedConditions.visibilityOfAllElementsLocatedBy(
+                    By.xpath(
+                        "//div[@data-refer='tree-view']"
+                            + "//div[@class='ant-tree-list-holder']"
+                            + "/div/div[@class='ant-tree-list-holder-inner']"
+                            + "/div[contains(@class, 'ant-tree-treenode')]")));
+        List<String> texts = new ArrayList<>();
+        for (WebElement webElement : list) {
+          String nodeName =
+              
webElement.findElement(By.xpath(".//span[@class='ant-tree-title']")).getText();
+          texts.add(nodeName);
+        }
+        if (!treeNodes.containsAll(texts)) {
+          LOG.error("tree nodes list: {} does not containsAll treeNodes: {}", 
texts, treeNodes);
+          return false;
+        }
+        return true;
+      } catch (org.openqa.selenium.StaleElementReferenceException e) {
+        if (attempt == maxRetries - 1) {
+          LOG.error(
+              "Failed to verify tree nodes after {} attempts due to stale 
elements", maxRetries);
+          throw e;
+        }
+        LOG.warn("Stale element encountered, retrying... (attempt {}/{})", 
attempt + 1, maxRetries);
+        try {
+          Thread.sleep(500);
+        } catch (InterruptedException ie) {
+          Thread.currentThread().interrupt();
+        }
+      }
     }
-    return true;
+    return false;
   }
 
   public boolean verifySelectedNode(String nodeName) {

Reply via email to