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 ;
4041 * @author Phillip Webb
4142 * @author Andy Wilkinson
4243 * @author Sam Brannen
44+ * @author Daniel Schmidt
4345 * @see OutputCaptureExtension
4446 * @see OutputCaptureRule
4547 */
@@ -49,11 +51,14 @@ class OutputCapture implements CapturedOutput {
4951
5052 private AnsiOutputState ansiOutputState ;
5153
52- private final AtomicReference <String > out = new AtomicReference <>(null );
54+ private final AtomicLong outVersion = new AtomicLong ();
55+ private final AtomicReference <VersionedCacheResult > out = new AtomicReference <>(null );
5356
54- private final AtomicReference <String > err = new AtomicReference <>(null );
57+ private final AtomicLong errVersion = new AtomicLong ();
58+ private final AtomicReference <VersionedCacheResult > err = new AtomicReference <>(null );
5559
56- private final AtomicReference <String > all = new AtomicReference <>(null );
60+ private final AtomicLong allVersion = new AtomicLong ();
61+ private final AtomicReference <VersionedCacheResult > all = new AtomicReference <>(null );
5762
5863 /**
5964 * Push a new system capture session onto the stack.
@@ -106,7 +111,7 @@ public String toString() {
106111 */
107112 @ Override
108113 public String getAll () {
109- return get (this .all , (type ) -> true );
114+ return get (this .all , this . allVersion , (type ) -> true );
110115 }
111116
112117 /**
@@ -115,7 +120,7 @@ public String getAll() {
115120 */
116121 @ Override
117122 public String getOut () {
118- return get (this .out , Type .OUT ::equals );
123+ return get (this .out , this . outVersion , Type .OUT ::equals );
119124 }
120125
121126 /**
@@ -124,7 +129,7 @@ public String getOut() {
124129 */
125130 @ Override
126131 public String getErr () {
127- return get (this .err , Type .ERR ::equals );
132+ return get (this .err , this . errVersion , Type .ERR ::equals );
128133 }
129134
130135 /**
@@ -136,19 +141,24 @@ void reset() {
136141 }
137142
138143 void clearExisting () {
144+ this .outVersion .incrementAndGet ();
139145 this .out .set (null );
146+ this .errVersion .incrementAndGet ();
140147 this .err .set (null );
148+ this .allVersion .incrementAndGet ();
141149 this .all .set (null );
142150 }
143151
144- private String get (AtomicReference <String > existing , Predicate <Type > filter ) {
152+ private String get (AtomicReference <VersionedCacheResult > resultCache , AtomicLong version , Predicate <Type > filter ) {
145153 Assert .state (!this .systemCaptures .isEmpty (),
146154 "No system captures found. Please check your output capture registration." );
147- String result = existing .get ();
148- if ( result == null ) {
149- result = build ( filter );
150- existing . compareAndSet ( null , result ) ;
155+ long currentVersion = version .get ();
156+ VersionedCacheResult cached = resultCache . get ();
157+ if ( cached != null && cached . version == currentVersion ) {
158+ return cached . result ;
151159 }
160+ String result = build (filter );
161+ resultCache .compareAndSet (null , new VersionedCacheResult (result , currentVersion ));
152162 return result ;
153163 }
154164
@@ -160,6 +170,10 @@ String build(Predicate<Type> filter) {
160170 return builder .toString ();
161171 }
162172
173+ private record VersionedCacheResult (String result , long version ) {
174+
175+ }
176+
163177 /**
164178 * A capture session that captures {@link System#out System.out} and {@link System#out
165179 * System.err}.
0 commit comments