2323import java .util .ArrayList ;
2424import java .util .Deque ;
2525import java .util .List ;
26+ import java .util .concurrent .atomic .AtomicLong ;
2627import java .util .concurrent .atomic .AtomicReference ;
2728import java .util .function .Consumer ;
2829import java .util .function .Predicate ;
4243 * @author Phillip Webb
4344 * @author Andy Wilkinson
4445 * @author Sam Brannen
46+ * @author Daniel Schmidt
4547 * @see OutputCaptureExtension
4648 * @see OutputCaptureRule
4749 */
@@ -51,11 +53,14 @@ class OutputCapture implements CapturedOutput {
5153
5254 private @ Nullable AnsiOutputState ansiOutputState ;
5355
54- private final AtomicReference <String > out = new AtomicReference <>(null );
56+ private final AtomicLong outVersion = new AtomicLong ();
57+ private final AtomicReference <VersionedCacheResult > out = new AtomicReference <>(null );
5558
56- private final AtomicReference <String > err = new AtomicReference <>(null );
59+ private final AtomicLong errVersion = new AtomicLong ();
60+ private final AtomicReference <VersionedCacheResult > err = new AtomicReference <>(null );
5761
58- private final AtomicReference <String > all = new AtomicReference <>(null );
62+ private final AtomicLong allVersion = new AtomicLong ();
63+ private final AtomicReference <VersionedCacheResult > all = new AtomicReference <>(null );
5964
6065 /**
6166 * Push a new system capture session onto the stack.
@@ -108,7 +113,7 @@ public String toString() {
108113 */
109114 @ Override
110115 public String getAll () {
111- return get (this .all , (type ) -> true );
116+ return get (this .all , this . allVersion , (type ) -> true );
112117 }
113118
114119 /**
@@ -117,7 +122,7 @@ public String getAll() {
117122 */
118123 @ Override
119124 public String getOut () {
120- return get (this .out , Type .OUT ::equals );
125+ return get (this .out , this . outVersion , Type .OUT ::equals );
121126 }
122127
123128 /**
@@ -126,7 +131,7 @@ public String getOut() {
126131 */
127132 @ Override
128133 public String getErr () {
129- return get (this .err , Type .ERR ::equals );
134+ return get (this .err , this . errVersion , Type .ERR ::equals );
130135 }
131136
132137 /**
@@ -138,19 +143,24 @@ void reset() {
138143 }
139144
140145 void clearExisting () {
146+ this .outVersion .incrementAndGet ();
141147 this .out .set (null );
148+ this .errVersion .incrementAndGet ();
142149 this .err .set (null );
150+ this .allVersion .incrementAndGet ();
143151 this .all .set (null );
144152 }
145153
146- private String get (AtomicReference <String > existing , Predicate <Type > filter ) {
154+ private String get (AtomicReference <VersionedCacheResult > resultCache , AtomicLong version , Predicate <Type > filter ) {
147155 Assert .state (!this .systemCaptures .isEmpty (),
148156 "No system captures found. Please check your output capture registration." );
149- String result = existing .get ();
150- if ( result == null ) {
151- result = build ( filter );
152- existing . compareAndSet ( null , result ) ;
157+ long currentVersion = version .get ();
158+ VersionedCacheResult cached = resultCache . get ();
159+ if ( cached != null && cached . version == currentVersion ) {
160+ return cached . result ;
153161 }
162+ String result = build (filter );
163+ resultCache .compareAndSet (null , new VersionedCacheResult (result , currentVersion ));
154164 return result ;
155165 }
156166
@@ -162,6 +172,10 @@ String build(Predicate<Type> filter) {
162172 return builder .toString ();
163173 }
164174
175+ private record VersionedCacheResult (String result , long version ) {
176+
177+ }
178+
165179 /**
166180 * A capture session that captures {@link System#out System.out} and {@link System#out
167181 * System.err}.
0 commit comments