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

zhangduo pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-2 by this push:
     new d747df4a543 HBASE-29637 Implement ResourceCheckerJUnitListener for 
junit 5 (#7366)
d747df4a543 is described below

commit d747df4a54340918a047f92de55cdaa13efff154
Author: Duo Zhang <[email protected]>
AuthorDate: Thu Oct 9 16:46:18 2025 +0800

    HBASE-29637 Implement ResourceCheckerJUnitListener for junit 5 (#7366)
    
    Co-authored-by: Copilot <[email protected]>
    
    Signed-off-by: Istvan Toth <[email protected]>
    (cherry picked from commit d8b1912361218ce9de3c3f7d4c5194c23519fa69)
---
 .../apache/hadoop/hbase/HBaseJupiterExtension.java |  63 +++++++++---
 ...nitListener.java => JUnitResourceCheckers.java} |  74 +++----------
 .../hadoop/hbase/ResourceCheckerJUnitListener.java | 114 +--------------------
 3 files changed, 62 insertions(+), 189 deletions(-)

diff --git 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/HBaseJupiterExtension.java 
b/hbase-common/src/test/java/org/apache/hadoop/hbase/HBaseJupiterExtension.java
index 997e3dfa357..9d4ea87e0ec 100644
--- 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/HBaseJupiterExtension.java
+++ 
b/hbase-common/src/test/java/org/apache/hadoop/hbase/HBaseJupiterExtension.java
@@ -37,7 +37,9 @@ import org.apache.hadoop.hbase.testclassification.MediumTests;
 import org.apache.hadoop.hbase.testclassification.SmallTests;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
 import org.junit.jupiter.api.extension.ExtensionContext.Store;
 import org.junit.jupiter.api.extension.InvocationInterceptor;
@@ -60,14 +62,18 @@ import 
org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFacto
  * the tag.
  * <p>
  * It also controls the timeout for the whole test class running, while the 
timeout annotation in
- * JUnit5 can only enforce the timeout for each test method.
+ * JUnit5 can only enforce the timeout for each test method. When a test is 
timed out, a thread dump
+ * will be printed to log output.
  * <p>
- * Finally, it also forbid System.exit call in tests. TODO: need to find a new 
way as
- * SecurityManager has been removed since Java 21.
+ * It also implements resource check for each test method, using the {@link 
ResourceChecker} class.
+ * <p>
+ * Finally, it also forbid System.exit call in tests. <br>
+ * TODO: need to find a new way as SecurityManager was deprecated in Java 17 
and permanently
+ * disabled since Java 24.
  */
 @InterfaceAudience.Private
-public class HBaseJupiterExtension
-  implements InvocationInterceptor, BeforeAllCallback, AfterAllCallback {
+public class HBaseJupiterExtension implements InvocationInterceptor, 
BeforeAllCallback,
+  AfterAllCallback, BeforeEachCallback, AfterEachCallback {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(HBaseJupiterExtension.class);
 
@@ -84,6 +90,8 @@ public class HBaseJupiterExtension
 
   private static final String DEADLINE = "deadline";
 
+  private static final String RESOURCE_CHECK = "rc";
+
   private Duration pickTimeout(ExtensionContext ctx) {
     Set<String> timeoutTags = TAG_TO_TIMEOUT.keySet();
     Set<String> timeoutTag = Sets.intersection(timeoutTags, ctx.getTags());
@@ -130,7 +138,8 @@ public class HBaseJupiterExtension
     System.setSecurityManager(null);
   }
 
-  private <T> T runWithTimeout(Invocation<T> invocation, ExtensionContext ctx) 
throws Throwable {
+  private <T> T runWithTimeout(Invocation<T> invocation, ExtensionContext ctx, 
String name)
+    throws Throwable {
     Store store = ctx.getStore(NAMESPACE);
     ExecutorService executor = store.get(EXECUTOR, ExecutorService.class);
     if (executor == null) {
@@ -139,12 +148,12 @@ public class HBaseJupiterExtension
     Instant deadline = store.get(DEADLINE, Instant.class);
     Instant now = Instant.now();
     if (!now.isBefore(deadline)) {
-      fail("Test " + ctx.getDisplayName() + " timed out, deadline is " + 
deadline);
+      fail("Test " + name + " timed out, deadline is " + deadline);
       return null;
     }
 
     Duration remaining = Duration.between(now, deadline);
-    LOG.info("remaining timeout for {} is {}", ctx.getDisplayName(), 
remaining);
+    LOG.info("remaining timeout for {} is {}", name, remaining);
     Future<T> future = executor.submit(() -> {
       try {
         return invocation.proceed();
@@ -157,14 +166,13 @@ public class HBaseJupiterExtension
       return future.get(remaining.toNanos(), TimeUnit.NANOSECONDS);
     } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
-      fail("Test " + ctx.getDisplayName() + " interrupted");
+      fail("Test " + name + " interrupted");
       return null;
     } catch (ExecutionException e) {
       throw ExceptionUtils.throwAsUncheckedException(e.getCause());
     } catch (TimeoutException e) {
       printThreadDump();
-      throw new JUnitException(
-        "Test " + ctx.getDisplayName() + " timed out, deadline is " + 
deadline, e);
+      throw new JUnitException("Test " + name + " timed out, deadline is " + 
deadline, e);
     }
   }
 
@@ -177,41 +185,62 @@ public class HBaseJupiterExtension
   public void interceptBeforeAllMethod(Invocation<Void> invocation,
     ReflectiveInvocationContext<Method> invocationContext, ExtensionContext 
extensionContext)
     throws Throwable {
-    runWithTimeout(invocation, extensionContext);
+    runWithTimeout(invocation, extensionContext, 
extensionContext.getDisplayName() + ".beforeAll");
   }
 
   @Override
   public void interceptBeforeEachMethod(Invocation<Void> invocation,
     ReflectiveInvocationContext<Method> invocationContext, ExtensionContext 
extensionContext)
     throws Throwable {
-    runWithTimeout(invocation, extensionContext);
+    runWithTimeout(invocation, extensionContext, 
extensionContext.getDisplayName() + ".beforeEach");
   }
 
   @Override
   public void interceptTestMethod(Invocation<Void> invocation,
     ReflectiveInvocationContext<Method> invocationContext, ExtensionContext 
extensionContext)
     throws Throwable {
-    runWithTimeout(invocation, extensionContext);
+    runWithTimeout(invocation, extensionContext, 
extensionContext.getDisplayName());
   }
 
   @Override
   public void interceptAfterEachMethod(Invocation<Void> invocation,
     ReflectiveInvocationContext<Method> invocationContext, ExtensionContext 
extensionContext)
     throws Throwable {
-    runWithTimeout(invocation, extensionContext);
+    runWithTimeout(invocation, extensionContext, 
extensionContext.getDisplayName() + ".afterEach");
   }
 
   @Override
   public void interceptAfterAllMethod(Invocation<Void> invocation,
     ReflectiveInvocationContext<Method> invocationContext, ExtensionContext 
extensionContext)
     throws Throwable {
-    runWithTimeout(invocation, extensionContext);
+    runWithTimeout(invocation, extensionContext, 
extensionContext.getDisplayName() + ".afterAll");
   }
 
   @Override
   public <T> T interceptTestClassConstructor(Invocation<T> invocation,
     ReflectiveInvocationContext<Constructor<T>> invocationContext,
     ExtensionContext extensionContext) throws Throwable {
-    return runWithTimeout(invocation, extensionContext);
+    return runWithTimeout(invocation, extensionContext,
+      extensionContext.getDisplayName() + ".constructor");
+  }
+
+  // below are for implementing resource checker around test method
+
+  @Override
+  public void beforeEach(ExtensionContext ctx) throws Exception {
+    ResourceChecker rc = new ResourceChecker(ctx.getDisplayName());
+    JUnitResourceCheckers.addResourceAnalyzer(rc);
+    Store store = ctx.getStore(NAMESPACE);
+    store.put(RESOURCE_CHECK, rc);
+    rc.start();
+  }
+
+  @Override
+  public void afterEach(ExtensionContext ctx) throws Exception {
+    Store store = ctx.getStore(NAMESPACE);
+    ResourceChecker rc = store.remove(RESOURCE_CHECK, ResourceChecker.class);
+    if (rc != null) {
+      rc.end();
+    }
   }
 }
diff --git 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
 b/hbase-common/src/test/java/org/apache/hadoop/hbase/JUnitResourceCheckers.java
similarity index 60%
copy from 
hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
copy to 
hbase-common/src/test/java/org/apache/hadoop/hbase/JUnitResourceCheckers.java
index 4dfce7f536b..aee49dc2c60 100644
--- 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
+++ 
b/hbase-common/src/test/java/org/apache/hadoop/hbase/JUnitResourceCheckers.java
@@ -22,27 +22,20 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import org.apache.hadoop.hbase.ResourceChecker.Phase;
 import org.apache.hadoop.hbase.util.JVM;
-import org.junit.runner.notification.RunListener;
 
 /**
- * Listen to the test progress and check the usage of:
- * <ul>
- * <li>threads</li>
- * <li>open file descriptor</li>
- * <li>max open file descriptor</li>
- * </ul>
- * <p>
- * When surefire forkMode=once/always/perthread, this code is executed on the 
forked process.
+ * ResourceCheckers when running JUnit tests.
  */
-public class ResourceCheckerJUnitListener extends RunListener {
-  private Map<String, ResourceChecker> rcs = new ConcurrentHashMap<>();
+public final class JUnitResourceCheckers {
 
-  static class ThreadResourceAnalyzer extends ResourceChecker.ResourceAnalyzer 
{
-    private static Set<String> initialThreadNames = new HashSet<>();
-    private static List<String> stringsToLog = null;
+  private JUnitResourceCheckers() {
+  }
+
+  private static class ThreadResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
+    private Set<String> initialThreadNames = new HashSet<>();
+    private List<String> stringsToLog = null;
 
     @Override
     public int getVal(Phase phase) {
@@ -80,7 +73,7 @@ public class ResourceCheckerJUnitListener extends RunListener 
{
     }
   }
 
-  static class OpenFileDescriptorResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
+  private static class OpenFileDescriptorResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
     @Override
     public int getVal(Phase phase) {
       if (!JVM.isUnix()) {
@@ -96,7 +89,7 @@ public class ResourceCheckerJUnitListener extends RunListener 
{
     }
   }
 
-  static class MaxFileDescriptorResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
+  private static class MaxFileDescriptorResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
     @Override
     public int getVal(Phase phase) {
       if (!JVM.isUnix()) {
@@ -107,7 +100,7 @@ public class ResourceCheckerJUnitListener extends 
RunListener {
     }
   }
 
-  static class SystemLoadAverageResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
+  private static class SystemLoadAverageResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
     @Override
     public int getVal(Phase phase) {
       if (!JVM.isUnix()) {
@@ -117,7 +110,7 @@ public class ResourceCheckerJUnitListener extends 
RunListener {
     }
   }
 
-  static class ProcessCountResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
+  private static class ProcessCountResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
     @Override
     public int getVal(Phase phase) {
       if (!JVM.isUnix()) {
@@ -127,7 +120,7 @@ public class ResourceCheckerJUnitListener extends 
RunListener {
     }
   }
 
-  static class AvailableMemoryMBResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
+  private static class AvailableMemoryMBResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
     @Override
     public int getVal(Phase phase) {
       if (!JVM.isUnix()) {
@@ -137,51 +130,12 @@ public class ResourceCheckerJUnitListener extends 
RunListener {
     }
   }
 
-  /**
-   * To be implemented by sub classes if they want to add specific 
ResourceAnalyzer.
-   */
-  protected void addResourceAnalyzer(ResourceChecker rc) {
-  }
-
-  private void start(String testName) {
-    ResourceChecker rc = new ResourceChecker(testName);
+  public static void addResourceAnalyzer(ResourceChecker rc) {
     rc.addResourceAnalyzer(new ThreadResourceAnalyzer());
     rc.addResourceAnalyzer(new OpenFileDescriptorResourceAnalyzer());
     rc.addResourceAnalyzer(new MaxFileDescriptorResourceAnalyzer());
     rc.addResourceAnalyzer(new SystemLoadAverageResourceAnalyzer());
     rc.addResourceAnalyzer(new ProcessCountResourceAnalyzer());
     rc.addResourceAnalyzer(new AvailableMemoryMBResourceAnalyzer());
-
-    addResourceAnalyzer(rc);
-
-    rcs.put(testName, rc);
-
-    rc.start();
-  }
-
-  private void end(String testName) {
-    ResourceChecker rc = rcs.remove(testName);
-    assert rc != null;
-    rc.end();
-  }
-
-  /**
-   * Get the test name from the JUnit Description
-   * @return the string for the short test name
-   */
-  private String descriptionToShortTestName(org.junit.runner.Description 
description) {
-    final int toRemove = "org.apache.hadoop.hbase.".length();
-    return description.getTestClass().getName().substring(toRemove) + "#"
-      + description.getMethodName();
-  }
-
-  @Override
-  public void testStarted(org.junit.runner.Description description) throws 
java.lang.Exception {
-    start(descriptionToShortTestName(description));
-  }
-
-  @Override
-  public void testFinished(org.junit.runner.Description description) throws 
java.lang.Exception {
-    end(descriptionToShortTestName(description));
   }
 }
diff --git 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
 
b/hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
index 4dfce7f536b..2a796cc4077 100644
--- 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
+++ 
b/hbase-common/src/test/java/org/apache/hadoop/hbase/ResourceCheckerJUnitListener.java
@@ -17,14 +17,8 @@
  */
 package org.apache.hadoop.hbase;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import org.apache.hadoop.hbase.ResourceChecker.Phase;
-import org.apache.hadoop.hbase.util.JVM;
 import org.junit.runner.notification.RunListener;
 
 /**
@@ -38,104 +32,8 @@ import org.junit.runner.notification.RunListener;
  * When surefire forkMode=once/always/perthread, this code is executed on the 
forked process.
  */
 public class ResourceCheckerJUnitListener extends RunListener {
-  private Map<String, ResourceChecker> rcs = new ConcurrentHashMap<>();
 
-  static class ThreadResourceAnalyzer extends ResourceChecker.ResourceAnalyzer 
{
-    private static Set<String> initialThreadNames = new HashSet<>();
-    private static List<String> stringsToLog = null;
-
-    @Override
-    public int getVal(Phase phase) {
-      Map<Thread, StackTraceElement[]> stackTraces = 
Thread.getAllStackTraces();
-      if (phase == Phase.INITIAL) {
-        stringsToLog = null;
-        for (Thread t : stackTraces.keySet()) {
-          initialThreadNames.add(t.getName());
-        }
-      } else if (phase == Phase.END) {
-        if (stackTraces.size() > initialThreadNames.size()) {
-          stringsToLog = new ArrayList<>();
-          for (Thread t : stackTraces.keySet()) {
-            if (!initialThreadNames.contains(t.getName())) {
-              stringsToLog.add("\nPotentially hanging thread: " + t.getName() 
+ "\n");
-              StackTraceElement[] stackElements = stackTraces.get(t);
-              for (StackTraceElement ele : stackElements) {
-                stringsToLog.add("\t" + ele + "\n");
-              }
-            }
-          }
-        }
-      }
-      return stackTraces.size();
-    }
-
-    @Override
-    public int getMax() {
-      return 500;
-    }
-
-    @Override
-    public List<String> getStringsToLog() {
-      return stringsToLog;
-    }
-  }
-
-  static class OpenFileDescriptorResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
-    @Override
-    public int getVal(Phase phase) {
-      if (!JVM.isUnix()) {
-        return 0;
-      }
-      JVM jvm = new JVM();
-      return (int) jvm.getOpenFileDescriptorCount();
-    }
-
-    @Override
-    public int getMax() {
-      return 1024;
-    }
-  }
-
-  static class MaxFileDescriptorResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
-    @Override
-    public int getVal(Phase phase) {
-      if (!JVM.isUnix()) {
-        return 0;
-      }
-      JVM jvm = new JVM();
-      return (int) jvm.getMaxFileDescriptorCount();
-    }
-  }
-
-  static class SystemLoadAverageResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
-    @Override
-    public int getVal(Phase phase) {
-      if (!JVM.isUnix()) {
-        return 0;
-      }
-      return (int) (new JVM().getSystemLoadAverage() * 100);
-    }
-  }
-
-  static class ProcessCountResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
-    @Override
-    public int getVal(Phase phase) {
-      if (!JVM.isUnix()) {
-        return 0;
-      }
-      return new JVM().getNumberOfRunningProcess();
-    }
-  }
-
-  static class AvailableMemoryMBResourceAnalyzer extends 
ResourceChecker.ResourceAnalyzer {
-    @Override
-    public int getVal(Phase phase) {
-      if (!JVM.isUnix()) {
-        return 0;
-      }
-      return (int) (new JVM().getFreeMemory() / (1024L * 1024L));
-    }
-  }
+  private final Map<String, ResourceChecker> rcs = new ConcurrentHashMap<>();
 
   /**
    * To be implemented by sub classes if they want to add specific 
ResourceAnalyzer.
@@ -145,17 +43,9 @@ public class ResourceCheckerJUnitListener extends 
RunListener {
 
   private void start(String testName) {
     ResourceChecker rc = new ResourceChecker(testName);
-    rc.addResourceAnalyzer(new ThreadResourceAnalyzer());
-    rc.addResourceAnalyzer(new OpenFileDescriptorResourceAnalyzer());
-    rc.addResourceAnalyzer(new MaxFileDescriptorResourceAnalyzer());
-    rc.addResourceAnalyzer(new SystemLoadAverageResourceAnalyzer());
-    rc.addResourceAnalyzer(new ProcessCountResourceAnalyzer());
-    rc.addResourceAnalyzer(new AvailableMemoryMBResourceAnalyzer());
-
+    JUnitResourceCheckers.addResourceAnalyzer(rc);
     addResourceAnalyzer(rc);
-
     rcs.put(testName, rc);
-
     rc.start();
   }
 

Reply via email to