2222import java .util .concurrent .CountDownLatch ;
2323import java .util .concurrent .ExecutorService ;
2424import java .util .concurrent .Executors ;
25+ import java .util .concurrent .Future ;
2526import java .util .concurrent .TimeUnit ;
2627import java .util .function .Predicate ;
2728
28- import org .jspecify .annotations .Nullable ;
2929import org .junit .jupiter .api .AfterEach ;
3030import org .junit .jupiter .api .BeforeEach ;
3131import org .junit .jupiter .api .Test ;
@@ -196,41 +196,21 @@ void getErrUsesCache() {
196196
197197 @ Test
198198 void getOutCacheShouldNotReturnStaleDataWhenDataIsLoggedWhileReading () throws Exception {
199- this .output .push ();
199+ TestLatchedOutputCapture output = new TestLatchedOutputCapture ();
200+ output .push ();
200201 System .out .print ("A" );
201- this .output .waitAfterBuildLatch = new CountDownLatch (1 );
202-
203- ExecutorService executorService = null ;
202+ ExecutorService executor = Executors .newFixedThreadPool (2 );
204203 try {
205- executorService = Executors .newFixedThreadPool (2 );
206- var readingThreadFuture = executorService .submit (() -> {
207- // this will release the releaseAfterBuildLatch and block on the waitAfterBuildLatch
208- assertThat (this .output .getOut ()).isEqualTo ("A" );
209- });
210- var writingThreadFuture = executorService .submit (() -> {
211- // wait until we finished building the first result (but did not yet update the cache)
212- try {
213- this .output .releaseAfterBuildLatch .await ();
214- }
215- catch (InterruptedException e ) {
216- throw new RuntimeException (e );
217- }
218- // print something else and then release the latch, for the other thread to continue
219- System .out .print ("B" );
220- this .output .waitAfterBuildLatch .countDown ();
221- });
222- readingThreadFuture .get ();
223- writingThreadFuture .get ();
204+ Future <?> reader = executor .submit (output ::releaseAfterBuildAndAssertResultIsA );
205+ Future <?> writer = executor .submit (output ::awaitReleaseAfterBuildThenWriteBAndRelease );
206+ reader .get ();
207+ writer .get ();
224208 }
225209 finally {
226- if (executorService != null ) {
227- executorService .shutdown ();
228- executorService .awaitTermination (10 , TimeUnit .SECONDS );
229- }
210+ executor .shutdown ();
211+ executor .awaitTermination (10 , TimeUnit .SECONDS );
230212 }
231-
232- // If not synchronized correctly this will fail, because the second print did not clear the cache and the cache will return stale data.
233- assertThat (this .output .getOut ()).isEqualTo ("AB" );
213+ assertThat (output .getOut ()).isEqualTo ("AB" );
234214 }
235215
236216 private void pushAndPrint () {
@@ -257,28 +237,48 @@ static class TestOutputCapture extends OutputCapture {
257237
258238 int buildCount ;
259239
260- @ Nullable
261- CountDownLatch waitAfterBuildLatch = null ;
240+ @ Override
241+ String build (Predicate <Type > filter ) {
242+ this .buildCount ++;
243+ return super .build (filter );
244+ }
245+
246+ }
247+
248+ static class TestLatchedOutputCapture extends OutputCapture {
262249
263- CountDownLatch releaseAfterBuildLatch = new CountDownLatch (1 );
250+ private final CountDownLatch waitAfterBuild = new CountDownLatch (1 );
251+
252+ private final CountDownLatch releaseAfterBuild = new CountDownLatch (1 );
264253
265254 @ Override
266255 String build (Predicate <Type > filter ) {
267- this .buildCount ++;
268256 var result = super .build (filter );
269- this .releaseAfterBuildLatch .countDown ();
270- if (this .waitAfterBuildLatch != null ) {
271- try {
272- this .waitAfterBuildLatch .await ();
273- }
274- catch (InterruptedException e ) {
275- Thread .currentThread ().interrupt ();
276- throw new RuntimeException (e );
277- }
278- }
257+ this .releaseAfterBuild .countDown ();
258+ await (this .waitAfterBuild );
279259 return result ;
280260 }
281261
262+ void releaseAfterBuildAndAssertResultIsA () {
263+ assertThat (getOut ()).isEqualTo ("A" );
264+ }
265+
266+ void awaitReleaseAfterBuildThenWriteBAndRelease () {
267+ await (this .releaseAfterBuild );
268+ System .out .print ("B" );
269+ this .waitAfterBuild .countDown ();
270+ }
271+
272+ private void await (CountDownLatch latch ) {
273+ try {
274+ latch .await ();
275+ }
276+ catch (InterruptedException ex ) {
277+ Thread .currentThread ().interrupt ();
278+ throw new RuntimeException (ex );
279+ }
280+ }
281+
282282 }
283283
284284}
0 commit comments