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

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

commit edd032e1d3e5c3b20701841d56d15668879c5c7c
Author: James Bognar <[email protected]>
AuthorDate: Tue Aug 18 13:40:32 2026 -0400

    Close subscription leaks in RunStateBroadcaster/SseLogServlet tests
    
    RunStateBroadcaster.subscribe() already returns an AutoCloseable; these 
tests
    discarded it. Switched to try-with-resources. No production code changed; 
adds
    one Sonar resource-suppression rationale on a Mockito stub that owns 
nothing.
---
 .../juneau/releng/log/RunStateBroadcasterTest.java | 26 +++++-----
 .../juneau/releng/log/SseLogServletTest.java       | 58 ++++++++++++----------
 2 files changed, 43 insertions(+), 41 deletions(-)

diff --git 
a/src/test/java/org/apache/juneau/releng/log/RunStateBroadcasterTest.java 
b/src/test/java/org/apache/juneau/releng/log/RunStateBroadcasterTest.java
index 40c7300dbb..1f67abc587 100644
--- a/src/test/java/org/apache/juneau/releng/log/RunStateBroadcasterTest.java
+++ b/src/test/java/org/apache/juneau/releng/log/RunStateBroadcasterTest.java
@@ -25,17 +25,16 @@ import org.junit.jupiter.api.Test;
 class RunStateBroadcasterTest {
 
        @Test
-       void publishDeliversToEverySubscriber() {
+       void publishDeliversToEverySubscriber() throws Exception {
                var bc = new RunStateBroadcaster();
                var a = new ArrayList<String>();
                var b = new ArrayList<String>();
-               bc.subscribe(a::add);
-               bc.subscribe(b::add);
+               try (var s1 = bc.subscribe(a::add); var s2 = 
bc.subscribe(b::add)) {
+                       bc.publish("{\"status\":\"RUNNING\"}");
 
-               bc.publish("{\"status\":\"RUNNING\"}");
-
-               assertEquals(List.of("{\"status\":\"RUNNING\"}"), a);
-               assertEquals(List.of("{\"status\":\"RUNNING\"}"), b);
+                       assertEquals(List.of("{\"status\":\"RUNNING\"}"), a);
+                       assertEquals(List.of("{\"status\":\"RUNNING\"}"), b);
+               }
        }
 
        @Test
@@ -52,17 +51,16 @@ class RunStateBroadcasterTest {
        }
 
        @Test
-       void aThrowingSubscriberDoesNotBreakOthers() {
+       void aThrowingSubscriberDoesNotBreakOthers() throws Exception {
                var bc = new RunStateBroadcaster();
                var seen = new ArrayList<String>();
-               bc.subscribe(s -> {
+               try (var s1 = bc.subscribe(s -> {
                        throw new RuntimeException("dead client");
-               });
-               bc.subscribe(seen::add);
-
-               bc.publish("snapshot");
+               }); var s2 = bc.subscribe(seen::add)) {
+                       bc.publish("snapshot");
 
-               assertEquals(List.of("snapshot"), seen);
+                       assertEquals(List.of("snapshot"), seen);
+               }
        }
 
        @Test
diff --git a/src/test/java/org/apache/juneau/releng/log/SseLogServletTest.java 
b/src/test/java/org/apache/juneau/releng/log/SseLogServletTest.java
index bc1b79af39..2e182daf95 100644
--- a/src/test/java/org/apache/juneau/releng/log/SseLogServletTest.java
+++ b/src/test/java/org/apache/juneau/releng/log/SseLogServletTest.java
@@ -58,6 +58,9 @@ class SseLogServletTest {
                return req;
        }
 
+       @SuppressWarnings({
+               "resource" // Mockito stub: resp.getWriter() returns the 
caller-owned PrintWriter; no resource is opened here.
+       })
        private HttpServletResponse responseWriting(PrintWriter out) throws 
IOException {
                var resp = mock(HttpServletResponse.class);
                when(resp.getWriter()).thenReturn(out);
@@ -133,33 +136,34 @@ class SseLogServletTest {
                var initialSeen = new CountDownLatch(1);
                var updateSeen = new CountDownLatch(1);
                var writer = new FailableWriter(initialSeen, updateSeen);
-               var out = new PrintWriter(writer);
-               var bc = new RunStateBroadcaster();
-               var servlet = new SseLogServlet((v, s) -> Optional.empty(), (v, 
s) -> Optional.empty(),
-                               v -> Optional.of("INITIAL_SNAPSHOT"), v -> 
Optional.of(bc));
-               var req = requestFor("/9.2.1/state");
-               var resp = responseWriting(out);
-
-               var worker = new Thread(() -> servlet.doGet(req, resp));
-               worker.setDaemon(true);
-               worker.start();
-
-               assertTrue(initialSeen.await(2, TimeUnit.SECONDS), "the current 
snapshot must be sent on connect");
-               // tail() subscribes to bc slightly after the initial send 
completes; re-publish until the worker
-               // thread's subscription has actually landed rather than racing 
a single publish against it.
-               var deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2);
-               while (!updateSeen.await(20, TimeUnit.MILLISECONDS)) {
-                       bc.publish("UPDATED_SNAPSHOT");
-                       if (System.nanoTime() > deadline)
-                               fail("a published snapshot must be tailed 
live");
+               try (var out = new PrintWriter(writer)) {
+                       var bc = new RunStateBroadcaster();
+                       var servlet = new SseLogServlet((v, s) -> 
Optional.empty(), (v, s) -> Optional.empty(),
+                                       v -> Optional.of("INITIAL_SNAPSHOT"), v 
-> Optional.of(bc));
+                       var req = requestFor("/9.2.1/state");
+                       var resp = responseWriting(out);
+
+                       var worker = new Thread(() -> servlet.doGet(req, resp));
+                       worker.setDaemon(true);
+                       worker.start();
+
+                       assertTrue(initialSeen.await(2, TimeUnit.SECONDS), "the 
current snapshot must be sent on connect");
+                       // tail() subscribes to bc slightly after the initial 
send completes; re-publish until the worker
+                       // thread's subscription has actually landed rather 
than racing a single publish against it.
+                       var deadline = System.nanoTime() + 
TimeUnit.SECONDS.toNanos(2);
+                       while (!updateSeen.await(20, TimeUnit.MILLISECONDS)) {
+                               bc.publish("UPDATED_SNAPSHOT");
+                               if (System.nanoTime() > deadline)
+                                       fail("a published snapshot must be 
tailed live");
+                       }
+
+                       writer.failNext = true;
+                       bc.publish("nudge-the-blocked-poll-loop"); // wake 
tail()'s poll so its next write hits the failure
+                       worker.join(2000);
+                       assertFalse(worker.isAlive(), "the tail loop must exit 
once writes to the client start failing");
+
+                       assertEquals(SseLogServlet.sse("INITIAL_SNAPSHOT") + 
SseLogServlet.sse("UPDATED_SNAPSHOT"),
+                                       writer.captured.toString());
                }
-
-               writer.failNext = true;
-               bc.publish("nudge-the-blocked-poll-loop"); // wake tail()'s 
poll so its next write hits the failure
-               worker.join(2000);
-               assertFalse(worker.isAlive(), "the tail loop must exit once 
writes to the client start failing");
-
-               assertEquals(SseLogServlet.sse("INITIAL_SNAPSHOT") + 
SseLogServlet.sse("UPDATED_SNAPSHOT"),
-                               writer.captured.toString());
        }
 }

Reply via email to