From 82122772b264a01d4bd55384e243fee8d48bb566 Mon Sep 17 00:00:00 2001 From: Istvan Toth Date: Fri, 12 Sep 2025 08:15:31 +0200 Subject: [PATCH 1/4] HDFS-17828. SocketTimeoutException not recognized in TestWebHdfsTimeouts with Java 24 --- .../apache/hadoop/test/GenericTestUtils.java | 38 +++++++++++++++++++ .../hadoop/hdfs/web/TestWebHdfsTimeouts.java | 18 +++++---- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java index e4271f0407f52..102f143d9037d 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java @@ -318,6 +318,17 @@ public static void assertExceptionContains(String expectedText, Throwable t) { assertExceptionContains(expectedText, t, ""); } + /** + * Assert that an exception's toString() value + * matches the regex pattern. + * @param pattern regex pattern to match + * @param t thrown exception + * @throws AssertionError if the pattern does not match + */ + public static void assertExceptionMatches(Pattern pattern, Throwable t) { + assertExceptionMatches(pattern, t, ""); + } + /** * Assert that an exception's toString() value * contained the expected text. @@ -345,6 +356,33 @@ public static void assertExceptionContains(String expectedText, } } + /** + * Assert that an exception's toString() value + * matches the pattern. + * @param pattern regex pattern to match + * @param t thrown exception + * @param message any extra text for the string + * @throws AssertionError if the expected string is not found + */ + public static void assertExceptionMatches(Pattern pattern, + Throwable t, + String message) { + assertNotNull(t, E_NULL_THROWABLE); + String msg = t.toString(); + if (msg == null) { + throw new AssertionError(E_NULL_THROWABLE_STRING, t); + } + if (pattern != null && !pattern.matcher(msg).matches()) { + String prefix = org.apache.commons.lang3.StringUtils.isEmpty(message) + ? "" : (message + ": "); + throw new AssertionError( + String.format("%s Expected to match '%s' %s: %s", + prefix, pattern, E_UNEXPECTED_EXCEPTION, + StringUtils.stringifyException(t)), + t); + } + } + /** * Wait for the specified test to return true. The test will be performed * initially and then every {@code checkEveryMillis} until at least diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java index 5924a8dedcef3..67bdb771ba392 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java @@ -37,12 +37,14 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.TimeoutException; +import java.util.regex.Pattern; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.client.HdfsClientConfigKeys; @@ -152,8 +154,8 @@ public void testConnectTimeout(TimeoutSource src) throws Exception { fs.listFiles(new Path("/"), false); fail("expected timeout"); } catch (SocketTimeoutException e) { - GenericTestUtils.assertExceptionContains(fs.getUri().getAuthority() - + ": connect timed out",e); + GenericTestUtils.assertExceptionMatches(Pattern.compile( + ".*" + Pattern.quote(fs.getUri().getAuthority()) + ": [Cc]onnect timed out"), e); } } @@ -190,8 +192,8 @@ public void testAuthUrlConnectTimeout(TimeoutSource src) throws Exception { fs.getDelegationToken("renewer"); fail("expected timeout"); } catch (SocketTimeoutException e) { - GenericTestUtils.assertExceptionContains(fs.getUri().getAuthority() + - ": connect timed out", e); + GenericTestUtils.assertExceptionMatches(Pattern.compile( + ".*" + Pattern.quote(fs.getUri().getAuthority()) + ": [Cc]onnect timed out"), e); } } @@ -230,8 +232,8 @@ public void testRedirectConnectTimeout(TimeoutSource src) throws Exception { fail("expected timeout"); } catch (SocketTimeoutException e) { assumeBacklogConsumed(); - GenericTestUtils.assertExceptionContains( - fs.getUri().getAuthority() + ": connect timed out", e); + GenericTestUtils.assertExceptionMatches(Pattern.compile( + ".*" + Pattern.quote(fs.getUri().getAuthority()) + ": [Cc]onnect timed out"), e); } } @@ -272,8 +274,8 @@ public void testTwoStepWriteConnectTimeout(TimeoutSource src) throws Exception { fail("expected timeout"); } catch (SocketTimeoutException e) { assumeBacklogConsumed(); - GenericTestUtils.assertExceptionContains( - fs.getUri().getAuthority() + ": connect timed out", e); + GenericTestUtils.assertExceptionMatches(Pattern.compile( + ".*" + Pattern.quote(fs.getUri().getAuthority()) + ": [Cc]onnect timed out"), e); } finally { IOUtils.cleanupWithLogger(LOG, os); } From a8140dccef019999f5d4c9ac61d3fefc683d559e Mon Sep 17 00:00:00 2001 From: Istvan Toth Date: Mon, 15 Sep 2025 15:01:11 +0200 Subject: [PATCH 2/4] add tests for AssertExceptionMatches --- .../hadoop/test/TestGenericTestUtils.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java index d64ae7eadc919..6f8619df21f45 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java @@ -25,6 +25,8 @@ import org.slf4j.LoggerFactory; import java.util.function.Supplier; +import java.util.regex.Pattern; + import org.slf4j.event.Level; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -38,6 +40,7 @@ public void testAssertExceptionContainsNullEx() throws Throwable { try { assertExceptionContains("", null); } catch (AssertionError e) { + // AssertionError not actually thrown if (!e.toString().contains(E_NULL_THROWABLE)) { throw e; } @@ -49,6 +52,7 @@ public void testAssertExceptionContainsNullString() throws Throwable { try { assertExceptionContains("", new BrokenException()); } catch (AssertionError e) { + // AssertionError not actually thrown if (!e.toString().contains(E_NULL_THROWABLE_STRING)) { throw e; } @@ -76,6 +80,51 @@ public void testAssertExceptionContainsWorking() throws Throwable { assertExceptionContains("Expected", new Exception("Expected")); } + @Test + public void testAssertExceptionMatchesNullEx() throws Throwable { + try { + assertExceptionMatches(null, null); + } catch (AssertionError e) { + // AssertionError not actually thrown + if (!e.toString().contains(E_NULL_THROWABLE)) { + throw e; + } + } + } + + @Test + public void testAssertExceptionMatchesNullString() throws Throwable { + try { + assertExceptionMatches(null, new BrokenException()); + } catch (AssertionError e) { + // AssertionError not actually thrown + if (!e.toString().contains(E_NULL_THROWABLE_STRING)) { + throw e; + } + } + } + + @Test + public void testAssertExceptionMatchesWrongText() throws Throwable { + try { + assertExceptionMatches(Pattern.compile(".*Expected.*"), new Exception("(actual)")); + } catch (AssertionError e) { + String s = e.toString(); + if (!s.contains(E_UNEXPECTED_EXCEPTION) + || !s.contains("(actual)") ) { + throw e; + } + if (e.getCause() == null) { + throw new AssertionError("No nested cause in assertion", e); + } + } + } + + @Test + public void testAssertExceptionMatchesWorking() throws Throwable { + assertExceptionMatches(Pattern.compile(".*Expected.*"), new Exception("Expected")); + } + private static class BrokenException extends Exception { public BrokenException() { } From 39867c0041e6fdc81f8dfffc8b31a8195c762700 Mon Sep 17 00:00:00 2001 From: Istvan Toth Date: Mon, 15 Sep 2025 18:20:26 +0200 Subject: [PATCH 3/4] improve tests and remove bogus comment --- .../hadoop/test/TestGenericTestUtils.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java index 6f8619df21f45..f62ce5f3986f2 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java @@ -37,33 +37,39 @@ public class TestGenericTestUtils extends GenericTestUtils { @Test public void testAssertExceptionContainsNullEx() throws Throwable { + boolean assertTriggered = false; try { assertExceptionContains("", null); } catch (AssertionError e) { - // AssertionError not actually thrown + assertTriggered = true; if (!e.toString().contains(E_NULL_THROWABLE)) { throw e; } } + assertTrue(assertTriggered); } @Test public void testAssertExceptionContainsNullString() throws Throwable { + boolean assertTriggered = false; try { assertExceptionContains("", new BrokenException()); } catch (AssertionError e) { - // AssertionError not actually thrown + assertTriggered = true; if (!e.toString().contains(E_NULL_THROWABLE_STRING)) { throw e; } } + assertTrue(assertTriggered); } @Test public void testAssertExceptionContainsWrongText() throws Throwable { + boolean assertTriggered = false; try { assertExceptionContains("Expected", new Exception("(actual)")); } catch (AssertionError e) { + assertTriggered = true; String s = e.toString(); if (!s.contains(E_UNEXPECTED_EXCEPTION) || !s.contains("(actual)") ) { @@ -73,6 +79,7 @@ public void testAssertExceptionContainsWrongText() throws Throwable { throw new AssertionError("No nested cause in assertion", e); } } + assertTrue(assertTriggered); } @Test @@ -82,33 +89,40 @@ public void testAssertExceptionContainsWorking() throws Throwable { @Test public void testAssertExceptionMatchesNullEx() throws Throwable { + boolean assertTriggered = false; try { assertExceptionMatches(null, null); } catch (AssertionError e) { - // AssertionError not actually thrown + assertTriggered = true; if (!e.toString().contains(E_NULL_THROWABLE)) { throw e; } } + assertTrue(assertTriggered); } @Test public void testAssertExceptionMatchesNullString() throws Throwable { + boolean assertTriggered = false; try { assertExceptionMatches(null, new BrokenException()); + fail("assertion should have failed"); } catch (AssertionError e) { - // AssertionError not actually thrown + assertTriggered = true; if (!e.toString().contains(E_NULL_THROWABLE_STRING)) { throw e; } } + assertTrue(assertTriggered); } @Test public void testAssertExceptionMatchesWrongText() throws Throwable { + boolean assertTriggered = false; try { assertExceptionMatches(Pattern.compile(".*Expected.*"), new Exception("(actual)")); } catch (AssertionError e) { + assertTriggered = true; String s = e.toString(); if (!s.contains(E_UNEXPECTED_EXCEPTION) || !s.contains("(actual)") ) { @@ -118,6 +132,7 @@ public void testAssertExceptionMatchesWrongText() throws Throwable { throw new AssertionError("No nested cause in assertion", e); } } + assertTrue(assertTriggered); } @Test From d00fe9dc04fa4540ee3c0606a9641a0667b6c093 Mon Sep 17 00:00:00 2001 From: Istvan Toth Date: Wed, 24 Sep 2025 18:46:38 +0200 Subject: [PATCH 4/4] replace tests with intercept() --- .../hadoop/test/TestGenericTestUtils.java | 89 +++++-------------- 1 file changed, 20 insertions(+), 69 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java index f62ce5f3986f2..54a997605fdc9 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestGenericTestUtils.java @@ -29,57 +29,34 @@ import org.slf4j.event.Level; +import static org.apache.hadoop.test.LambdaTestUtils.intercept; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; + public class TestGenericTestUtils extends GenericTestUtils { @Test public void testAssertExceptionContainsNullEx() throws Throwable { - boolean assertTriggered = false; - try { - assertExceptionContains("", null); - } catch (AssertionError e) { - assertTriggered = true; - if (!e.toString().contains(E_NULL_THROWABLE)) { - throw e; - } - } - assertTrue(assertTriggered); + intercept(AssertionError.class, E_NULL_THROWABLE, () -> assertExceptionContains("", null)); } @Test public void testAssertExceptionContainsNullString() throws Throwable { - boolean assertTriggered = false; - try { - assertExceptionContains("", new BrokenException()); - } catch (AssertionError e) { - assertTriggered = true; - if (!e.toString().contains(E_NULL_THROWABLE_STRING)) { - throw e; - } - } - assertTrue(assertTriggered); + intercept(AssertionError.class, E_NULL_THROWABLE_STRING, () -> assertExceptionContains("", new BrokenException())); } @Test public void testAssertExceptionContainsWrongText() throws Throwable { - boolean assertTriggered = false; - try { - assertExceptionContains("Expected", new Exception("(actual)")); - } catch (AssertionError e) { - assertTriggered = true; - String s = e.toString(); - if (!s.contains(E_UNEXPECTED_EXCEPTION) - || !s.contains("(actual)") ) { - throw e; - } - if (e.getCause() == null) { - throw new AssertionError("No nested cause in assertion", e); - } + AssertionError e = intercept(AssertionError.class, E_UNEXPECTED_EXCEPTION, + () -> assertExceptionContains("Expected", new Exception("(actual)"))); + if (!e.toString().contains("(actual)")) { + throw new AssertionError("no actual string in exception", e); + } + if (e.getCause() == null) { + throw new AssertionError("No nested cause in assertion", e); } - assertTrue(assertTriggered); } @Test @@ -89,50 +66,24 @@ public void testAssertExceptionContainsWorking() throws Throwable { @Test public void testAssertExceptionMatchesNullEx() throws Throwable { - boolean assertTriggered = false; - try { - assertExceptionMatches(null, null); - } catch (AssertionError e) { - assertTriggered = true; - if (!e.toString().contains(E_NULL_THROWABLE)) { - throw e; - } - } - assertTrue(assertTriggered); + intercept(AssertionError.class, E_NULL_THROWABLE, () -> assertExceptionMatches(null, null)); } @Test public void testAssertExceptionMatchesNullString() throws Throwable { - boolean assertTriggered = false; - try { - assertExceptionMatches(null, new BrokenException()); - fail("assertion should have failed"); - } catch (AssertionError e) { - assertTriggered = true; - if (!e.toString().contains(E_NULL_THROWABLE_STRING)) { - throw e; - } - } - assertTrue(assertTriggered); + intercept(AssertionError.class, E_NULL_THROWABLE_STRING, () -> assertExceptionMatches(null, new BrokenException())); } @Test public void testAssertExceptionMatchesWrongText() throws Throwable { - boolean assertTriggered = false; - try { - assertExceptionMatches(Pattern.compile(".*Expected.*"), new Exception("(actual)")); - } catch (AssertionError e) { - assertTriggered = true; - String s = e.toString(); - if (!s.contains(E_UNEXPECTED_EXCEPTION) - || !s.contains("(actual)") ) { - throw e; - } - if (e.getCause() == null) { - throw new AssertionError("No nested cause in assertion", e); - } + AssertionError e = intercept(AssertionError.class, E_UNEXPECTED_EXCEPTION, + () -> assertExceptionMatches(Pattern.compile(".*Expected.*"), new Exception("(actual)"))); + if (!e.toString().contains("(actual)")) { + throw new AssertionError("no actual string in exception", e); + } + if (e.getCause() == null) { + throw new AssertionError("No nested cause in assertion", e); } - assertTrue(assertTriggered); } @Test