You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/07/23 13:13:21 UTC

(juneau) 01/03: TODO-279: triage @Disabled tests; drop LogRecord source-detection feature

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

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git

commit 7901a7a66d85321b2125bb5d16b951fd929981fb
Author: James Bognar <ja...@apache.org>
AuthorDate: Thu Jul 23 09:12:42 2026 -0400

    TODO-279: triage @Disabled tests; drop LogRecord source-detection feature
    
    - Remove the unreliable JUL-style source detection from LogRecord: findSource()
      stack-walk, the memoized source field, getSourceClassName()/getSourceMethodName()
      overrides, and the {source}/{class}/{method} placeholder cases in formatted().
      10.0.0 behavior change: formatted() no longer supports those placeholders, and
      positional %N$s specifiers shift down one index. Deletes the 2 chronically
      @Disabled tests that exercised it (feature confirmed self-contained, no other
      consumers).
    - Convert BsonArrayDebug_Test and RoundTripLargeObjects_Test from @Disabled to
      @Tag("manual") so they're runnable on demand; add a property-driven
      ${excludedGroups} (default "manual") Surefire mechanism so -DexcludedGroups=
      can re-enable them.
    
    Co-authored-by: Cursor <cu...@cursor.com>
---
 .../apache/juneau/commons/logging/LogRecord.java   | 78 +++----------------
 .../commons/logging/LogRecordCapture_Test.java     |  2 +-
 .../juneau/commons/logging/LogRecord_Test.java     | 90 +---------------------
 .../juneau/marshall/bson/BsonArrayDebug_Test.java  |  6 +-
 juneau-integration-tests/pom.xml                   |  6 +-
 .../a/rttests/RoundTripLargeObjects_Test.java      |  7 +-
 pom.xml                                            | 12 +++
 7 files changed, 37 insertions(+), 164 deletions(-)

diff --git a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
index 63559fe860..8e7c1af3eb 100644
--- a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
+++ b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
@@ -16,7 +16,6 @@
  */
 package org.apache.juneau.commons.logging;
 
-import static org.apache.juneau.commons.function.Suppliers.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.commons.utils.Shorts.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
@@ -75,20 +74,15 @@ public class LogRecord extends java.util.logging.LogRecord {
 
 	// Key constants for format placeholders
 	private static final String KEY_date = "date";
-	private static final String KEY_source = "source";
 	private static final String KEY_logger = "logger";
 	private static final String KEY_level = "level";
 	private static final String KEY_msg = "msg";
 	private static final String KEY_thrown = "thrown";
 	private static final String KEY_timestamp = "timestamp";
-	private static final String KEY_class = "class";
-	private static final String KEY_method = "method";
 	private static final String KEY_thread = "thread";
 	private static final String KEY_threadid = "threadid";
 	private static final String KEY_exception = "exception";
 
-	private transient Supplier<Optional<StackTraceElement>> source = memoize(()->findSource());
-
 	/**
 	 * Constructor.
 	 *
@@ -121,49 +115,6 @@ public class LogRecord extends java.util.logging.LogRecord {
 		return isEmptyArray(p) ? m : f(m, p);
 	}
 
-	/**
-	 * Returns the source class name, calculating it lazily from the stack trace if not already set.
-	 *
-	 * @return The source class name, or <jk>null</jk> if not available.
-	 */
-	@Override
-	public String getSourceClassName() {
-		if (source == null)
-			source = memoize(()->findSource());
-		return source.get().map(x -> x.getClassName()).orElse(null);
-	}
-
-	/**
-	 * Returns the source method name, calculating it lazily from the stack trace if not already set.
-	 *
-	 * @return The source method name, or <jk>null</jk> if not available.
-	 */
-	@Override
-	public String getSourceMethodName() {
-		if (source == null)
-			source = memoize(()->findSource());
-		return source.get().map(x -> x.getMethodName()).orElse(null);
-	}
-
-	@SuppressWarnings({
-		"java:S3776" // Cognitive complexity acceptable for stack trace filtering
-	})
-	private static Optional<StackTraceElement> findSource() {
-		for (var e : new Throwable().getStackTrace()) {
-			var c = e.getClassName();
-			var m = e.getMethodName();
-			// Skip internal classes, logging classes, lambda methods/classes, and synthetic methods
-			if (eq(c, cn(LogRecord.class)) || eq(c, cn(Logger.class)) || eq(c, cn(StringUtils.class))
-				|| c.startsWith("java.util.logging.")
-				|| (m != null && (m.contains("lambda$") || m.startsWith("access$")))
-				|| c.contains("$$Lambda$") || (c.contains("/") && c.contains("$Lambda"))) {
-				continue;
-			}
-			return o(e);
-		}
-		return oe();
-	}
-
 	/**
 	 * Formats this log record as a string using the specified format pattern.
 	 *
@@ -179,7 +130,6 @@ public class LogRecord extends java.util.logging.LogRecord {
 	 * {@link java.util.Formatter Formatter} with the following arguments:
 	 * <ol>
 	 * 	<li><c>date</c> - The date/time as a {@link Date} object
-	 * 	<li><c>source</c> - The source class name and method name (e.g., "com.example.MyClass myMethod")
 	 * 	<li><c>logger</c> - The logger name
 	 * 	<li><c>level</c> - The log level
 	 * 	<li><c>message</c> - The formatted log message
@@ -190,9 +140,6 @@ public class LogRecord extends java.util.logging.LogRecord {
 	 * <ul>
 	 * 	<li><js>"{date}"</js> - The date/time formatted using {@link SimpleFormatter SimpleFormatter}'s default date format
 	 * 	<li><js>"{timestamp}"</js> - The date/time formatted as ISO-8601 (yyyy-MM-dd'T'HH:mm:ss.SSSZ)
-	 * 	<li><js>"{class}"</js> - The source class name
-	 * 	<li><js>"{method}"</js> - The source method name
-	 * 	<li><js>"{source}"</js> - The source class name and method name (e.g., "com.example.MyClass myMethod")
 	 * 	<li><js>"{logger}"</js> - The logger name
 	 * 	<li><js>"{level}"</js> - The log level name
 	 * 	<li><js>"{msg}"</js> - The log message (formatted if parameters are present)
@@ -209,11 +156,10 @@ public class LogRecord extends java.util.logging.LogRecord {
 	 * </p>
 	 * <ul>
 	 * 	<li><js>"%1$s"</js> or <js>"%1$tc"</js> - The date/time (argument 1)
-	 * 	<li><js>"%2$s"</js> - The source (argument 2)
-	 * 	<li><js>"%3$s"</js> - The logger name (argument 3)
-	 * 	<li><js>"%4$s"</js> - The log level (argument 4)
-	 * 	<li><js>"%5$s"</js> - The log message (argument 5)
-	 * 	<li><js>"%6$s"</js> - The throwable and its backtrace (argument 6)
+	 * 	<li><js>"%2$s"</js> - The logger name (argument 2)
+	 * 	<li><js>"%3$s"</js> - The log level (argument 3)
+	 * 	<li><js>"%4$s"</js> - The log message (argument 4)
+	 * 	<li><js>"%5$s"</js> - The throwable and its backtrace (argument 5)
 	 * </ul>
 	 *
 	 * <p>
@@ -245,7 +191,7 @@ public class LogRecord extends java.util.logging.LogRecord {
 	 * 	<jc>// Result: "[2025-01-22T07:42:45.123-0500 INFO] User John logged in"</jc>
 	 *
 	 * 	<jc>// Using Formatter-style specifiers</jc>
-	 * 	String <jv>formatted2</jv> = <jv>record</jv>.formatted(<js>"%4$s: %5$s [%1$tc]%n"</js>);
+	 * 	String <jv>formatted2</jv> = <jv>record</jv>.formatted(<js>"%3$s: %4$s [%1$tc]%n"</js>);
 	 * 	<jc>// Result: "INFO: User John logged in [Tue Jan 22 07:42:45 PST 2025]"</jc>
 	 *
 	 * 	<jc>// Mixed named placeholders and Formatter specifiers</jc>
@@ -263,23 +209,19 @@ public class LogRecord extends java.util.logging.LogRecord {
 	})
 	public String formatted(String format) {
 		var date = new Date(getMillis());
-		Supplier<String> sourceName = () -> getSourceClassName() + ' ' + getSourceMethodName();
 
 		Function<String, Object> resolver = key -> switch (key) {
 			case KEY_date -> "%1$s";
-			case KEY_source -> sourceName.get();  // Override default behavior since logging class doesn't handle classes outside of java.util.logging.
-			case KEY_logger -> "%3$s";
-			case KEY_level -> "%4$s";
-			case KEY_msg -> "%5$s";
-			case KEY_thrown -> "%6$s";
+			case KEY_logger -> "%2$s";
+			case KEY_level -> "%3$s";
+			case KEY_msg -> "%4$s";
+			case KEY_thrown -> "%5$s";
 			case KEY_timestamp -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(date);
-			case KEY_class -> getSourceClassName();
-			case KEY_method -> getSourceMethodName();
 			case KEY_thread, KEY_threadid -> s(getThreadID());
 			case KEY_exception -> o(getThrown()).map(x -> x.getMessage()).orElse("");
 			default -> "";
 		};
 
-		return safeOptCatch(()->f(formatNamed(format, resolver), date, sourceName, getLoggerName(), getLevel(), getMessage(), getThrown()), x -> x.getLocalizedMessage()).orElse(null);
+		return safeOptCatch(()->f(formatNamed(format, resolver), date, getLoggerName(), getLevel(), getMessage(), getThrown()), x -> x.getLocalizedMessage()).orElse(null);
 	}
 }
diff --git a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
index 8666cff19a..2efef2beff 100644
--- a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
+++ b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
@@ -141,7 +141,7 @@ class LogRecordCapture_Test extends TestBase {
 		try (var capture = logger.captureEvents()) {
 			logger.info("Message");
 
-			var formatted = capture.getRecords("%4$s: %5$s");
+			var formatted = capture.getRecords("%3$s: %4$s");
 			assertSize(1, formatted);
 			assertTrue(formatted.get(0).contains("INFO"));
 			assertTrue(formatted.get(0).contains("Message"));
diff --git a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
index 976c958664..2d6dde98fc 100644
--- a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
+++ b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
@@ -108,58 +108,6 @@ class LogRecord_Test extends TestBase {
 		assertNull(rec.getParameters());
 	}
 
-	//====================================================================================================
-	// Source class and method name calculation
-	//====================================================================================================
-
-	@Test void c01_getSourceClassName_calculatedLazily() {
-		// Create LogRecord through Logger to get proper call stack
-		// findSource() filters out LogRecord, Logger, and lambda methods, so test classes are included
-		try (var capture = getLogger("c01").captureEvents()) {
-			var logger = getLogger("c01");
-			logger.info("Message");
-
-			var rec = capture.getRecords().get(0);
-			var className = rec.getSourceClassName();
-			// Should be calculated from stack trace
-			assertNotNull(className);
-			assertTrue(className.contains("LogRecord_Test")); // Should be this test class
-		}
-	}
-
-	@Test void c02_getSourceMethodName_calculatedLazily() {
-		// Create LogRecord through Logger to get proper call stack
-		// findSource() filters out LogRecord, Logger, and lambda methods, so test methods are included
-		try (var capture = getLogger("c02").captureEvents()) {
-			var logger = getLogger("c02");
-			logger.info("Message");
-
-			var rec = capture.getRecords().get(0);
-			var methodName = rec.getSourceMethodName();
-			// Should be calculated from stack trace
-			assertNotNull(methodName);
-			assertEquals("c02_getSourceMethodName_calculatedLazily", methodName);
-		}
-	}
-
-	@Test void c03_source_cachedAfterFirstAccess() {
-		// Create LogRecord through Logger to get proper call stack
-		try (var capture = getLogger("c03").captureEvents()) {
-			var logger = getLogger("c03");
-			logger.info("Message");
-
-			var rec = capture.getRecords().get(0);
-			var className1 = rec.getSourceClassName();
-			var className2 = rec.getSourceClassName();
-			var methodName1 = rec.getSourceMethodName();
-			var methodName2 = rec.getSourceMethodName();
-
-			// Should return same values (cached)
-			assertEquals(className1, className2);
-			assertEquals(methodName1, methodName2);
-		}
-	}
-
 	//====================================================================================================
 	// formatted() method - named placeholders
 	//====================================================================================================
@@ -195,23 +143,6 @@ class LogRecord_Test extends TestBase {
 		);
 	}
 
-	// Stack-walk source detection is unreliable in this JVM/test-runner combination; revisit if source-detection accuracy becomes important.
-	@Disabled("stack-walk source detection is unreliable in this JVM/test-runner combination")
-	@Test void d04_formatted_classAndMethod() {
-		// Create LogRecord through Logger to get proper call stack
-		// findSource() filters out LogRecord, Logger, and lambda methods, so test classes are included
-		try (var capture = getLogger("d04").captureEvents()) {
-			var logger = getLogger("d04");
-			logger.info("Message");
-
-			var rec = capture.getRecords().get(0);
-			var formatted = rec.formatted("{class}.{method}");
-			assertNotNull(formatted);
-			assertTrue(formatted.contains("LogRecord_Test"));
-			assertTrue(formatted.contains("d04_formatted_classAndMethod"));
-		}
-	}
-
 	@Test void d05_formatted_logger() {
 		var rec = new LogRecord("test.logger", Level.INFO, "Message", null, null);
 
@@ -266,25 +197,6 @@ class LogRecord_Test extends TestBase {
 		assertTrue(formatted.contains("RuntimeException"));
 	}
 
-	// Stack-walk source detection is unreliable in this JVM/test-runner combination; revisit if source-detection accuracy becomes important.
-	@Disabled("stack-walk source detection is unreliable in this JVM/test-runner combination")
-	@Test void d12_formatted_source() {
-		// Create LogRecord through Logger to get proper call stack
-		// findSource() now only filters out LogRecord and Logger classes, so test classes are included
-		try (var capture = getLogger("d12").captureEvents()) {
-			var logger = getLogger("d12");
-			logger.info("Message");
-
-			var rec = capture.getRecords().get(0);
-			var formatted = rec.formatted("{source}");
-			assertNotNull(formatted);
-			// The {source} placeholder resolves to %2$s which formats the source supplier
-			// The source supplier returns "className methodName"
-			assertTrue(formatted.contains("LogRecord_Test"));
-			assertTrue(formatted.contains("d12_formatted_source"));
-		}
-	}
-
 	//====================================================================================================
 	// formatted() method - Formatter-style specifiers
 	//====================================================================================================
@@ -292,7 +204,7 @@ class LogRecord_Test extends TestBase {
 	@Test void e01_formatted_formatterSpecifiers() {
 		var rec = new LogRecord("test.logger", Level.INFO, "Message", null, null);
 
-		var formatted = rec.formatted("%4$s: %5$s");
+		var formatted = rec.formatted("%3$s: %4$s");
 		assertTrue(formatted.contains("INFO"));
 		assertTrue(formatted.contains("Message"));
 	}
diff --git a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/bson/BsonArrayDebug_Test.java b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/bson/BsonArrayDebug_Test.java
index a96c5e645c..b88750a83f 100644
--- a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/bson/BsonArrayDebug_Test.java
+++ b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/bson/BsonArrayDebug_Test.java
@@ -21,8 +21,8 @@ import static org.junit.jupiter.api.Assertions.*;
 
 import org.junit.jupiter.api.*;
 
-/** Debug test to inspect BSON output for arrays. Disabled to avoid stdout noise during normal test runs. */
-@Disabled("Debug test - prints hex dumps for manual inspection; run manually when needed")
+/** Debug test to inspect BSON output for arrays. Tagged manual to avoid stdout noise during normal test runs; run explicitly with -Dgroups=manual. */
+@Tag("manual")
 class BsonArrayDebug_Test {
 
 	@Test
@@ -35,7 +35,7 @@ class BsonArrayDebug_Test {
 	@Test
 	void dumpJsonListBson() {
 		var s = BsonSerializer.create().keepNullProperties().addBeanTypes().addRootType().build();
-		var x = new org.apache.juneau.marshall.collections.JsonList("['abc',123]");
+		var x = new org.apache.juneau.marshall.collections.JsonList("[\"abc\",123]");
 		var bytes = s.write(x);
 		assertTrue(bytes.length > 0);
 	}
diff --git a/juneau-integration-tests/pom.xml b/juneau-integration-tests/pom.xml
index 63758c684e..4048af59f9 100644
--- a/juneau-integration-tests/pom.xml
+++ b/juneau-integration-tests/pom.xml
@@ -681,7 +681,9 @@
 					<execution>
 						<id>default-test</id>
 						<configuration>
-							<excludedGroups>container,benchmark</excludedGroups>
+							<!-- ${excludedGroups} (default "manual", see root pom) is merged in here so it stays
+							     overridable via -DexcludedGroups= the same way as every other module. -->
+							<excludedGroups>container,benchmark,${excludedGroups}</excludedGroups>
 							<reportsDirectory>${project.build.directory}/surefire-reports/core</reportsDirectory>
 							<!--
 								Process-level parallelism for the in-memory core bucket: each fork is a
@@ -701,7 +703,7 @@
 						</goals>
 						<configuration>
 							<groups>container</groups>
-							<excludedGroups>benchmark</excludedGroups>
+							<excludedGroups>benchmark,${excludedGroups}</excludedGroups>
 							<reportsDirectory>${project.build.directory}/surefire-reports/container</reportsDirectory>
 						</configuration>
 					</execution>
diff --git a/juneau-integration-tests/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java b/juneau-integration-tests/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
index 570e959881..98aeb1fa0f 100755
--- a/juneau-integration-tests/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
+++ b/juneau-integration-tests/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
@@ -44,8 +44,13 @@ import org.junit.jupiter.params.provider.*;
 /**
  * Tests designed to serialize and parse objects to make sure we end up
  * with the same objects for all serializers and parsers.
+ *
+ * <p>
+ * Round-trips a 20000-element object across every serializer/parser pair in the reactor, 10 times each;
+ * tagged manual because the runtime cost is too high for the normal build, not because of a correctness issue.
+ * </p>
  */
-@Disabled
+@Tag("manual")
 @SuppressWarnings({
 	"serial"  // serialVersionUID not required for test classes.
 })
diff --git a/pom.xml b/pom.xml
index 21de6b65e3..aa6d485eee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,9 @@
 		<jakarta.validation-api.version>3.0.2</jakarta.validation-api.version>
 		<logback.version>1.5.18</logback.version>
 		<log4j.version>2.24.3</log4j.version>
+		<!-- Default Surefire excludedGroups; overridable on the CLI (e.g. -DexcludedGroups= to run "manual"-tagged
+		     tests) because it's referenced via ${excludedGroups} rather than hardcoded in the plugin config. -->
+		<excludedGroups>manual</excludedGroups>
 	</properties>
 
 	<modules>
@@ -296,11 +299,20 @@
 					    WARNING/SEVERE still surface.
 					Modules that define their own surefire <systemPropertyVariables> (e.g. java.locale.providers)
 					merge with these; module-level keys win on conflict.  See src/test/resources/.
+
+					Also excludes the "manual" JUnit5 tag by default so on-demand/manual-inspection tests
+					(e.g. debug harnesses, expensive stress tests) are skipped during normal builds; run them
+					explicitly with -DexcludedGroups= -Dgroups=manual.  The value comes from the ${excludedGroups}
+					property (default "manual") rather than a literal, so it can be overridden on the CLI -
+					a literal value in this <configuration> would take precedence over any -D override.
+					Modules that already override <excludedGroups> per execution (e.g. juneau-integration-tests)
+					merge ${excludedGroups} into their own exclusion lists the same way.
 				-->
 				<plugin>
 					<groupId>org.apache.maven.plugins</groupId>
 					<artifactId>maven-surefire-plugin</artifactId>
 					<configuration>
+						<excludedGroups>${excludedGroups}</excludedGroups>
 						<systemPropertyVariables>
 							<logback.configurationFile>${maven.multiModuleProjectDirectory}/src/test/resources/logback-test.xml</logback.configurationFile>
 							<java.util.logging.config.file>${maven.multiModuleProjectDirectory}/src/test/resources/logging.properties</java.util.logging.config.file>