Browse Source

Merge branch '3.4.x' into 3.5.x

Closes gh-46721
pull/47140/head
Phillip Webb 8 months ago
parent
commit
a91ff37b92
  1. 34
      spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java
  2. 61
      spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/OutputCaptureTests.java

34
spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java

@ -40,6 +40,7 @@ import org.springframework.util.ClassUtils; @@ -40,6 +40,7 @@ import org.springframework.util.ClassUtils;
* @author Phillip Webb
* @author Andy Wilkinson
* @author Sam Brannen
* @author Daniel Schmidt
* @see OutputCaptureExtension
* @see OutputCaptureRule
*/
@ -49,11 +50,15 @@ class OutputCapture implements CapturedOutput { @@ -49,11 +50,15 @@ class OutputCapture implements CapturedOutput {
private AnsiOutputState ansiOutputState;
private final AtomicReference<String> out = new AtomicReference<>(null);
private final AtomicReference<Object> out = new AtomicReference<>();
private final AtomicReference<String> err = new AtomicReference<>(null);
private final AtomicReference<Object> err = new AtomicReference<>();
private final AtomicReference<String> all = new AtomicReference<>(null);
private final AtomicReference<Object> all = new AtomicReference<>();
OutputCapture() {
clearExisting();
}
/**
* Push a new system capture session onto the stack.
@ -136,20 +141,21 @@ class OutputCapture implements CapturedOutput { @@ -136,20 +141,21 @@ class OutputCapture implements CapturedOutput {
}
void clearExisting() {
this.out.set(null);
this.err.set(null);
this.all.set(null);
this.out.set(new NoOutput());
this.err.set(new NoOutput());
this.all.set(new NoOutput());
}
private String get(AtomicReference<String> existing, Predicate<Type> filter) {
private String get(AtomicReference<Object> existing, Predicate<Type> filter) {
Assert.state(!this.systemCaptures.isEmpty(),
"No system captures found. Please check your output capture registration.");
String result = existing.get();
if (result == null) {
result = build(filter);
existing.compareAndSet(null, result);
Object existingOutput = existing.get();
if (existingOutput instanceof String) {
return (String) existingOutput;
}
return result;
String builtOutput = build(filter);
existing.compareAndSet(existingOutput, builtOutput);
return builtOutput;
}
String build(Predicate<Type> filter) {
@ -339,4 +345,8 @@ class OutputCapture implements CapturedOutput { @@ -339,4 +345,8 @@ class OutputCapture implements CapturedOutput {
}
static class NoOutput {
}
}

61
spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/OutputCaptureTests.java

@ -19,6 +19,11 @@ package org.springframework.boot.test.system; @@ -19,6 +19,11 @@ package org.springframework.boot.test.system;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.NoSuchElementException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import org.junit.jupiter.api.AfterEach;
@ -32,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -32,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* Tests for {@link OutputCapture}.
*
* @author Phillip Webb
* @author Daniel Schmidt
*/
class OutputCaptureTests {
@ -188,6 +194,25 @@ class OutputCaptureTests { @@ -188,6 +194,25 @@ class OutputCaptureTests {
assertThat(this.output.buildCount).isEqualTo(2);
}
@Test
void getOutCacheShouldNotReturnStaleDataWhenDataIsLoggedWhileReading() throws Exception {
TestLatchedOutputCapture output = new TestLatchedOutputCapture();
output.push();
System.out.print("A");
ExecutorService executor = Executors.newFixedThreadPool(2);
try {
Future<?> reader = executor.submit(output::releaseAfterBuildAndAssertResultIsA);
Future<?> writer = executor.submit(output::awaitReleaseAfterBuildThenWriteBAndRelease);
reader.get();
writer.get();
}
finally {
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
}
assertThat(output.getOut()).isEqualTo("AB");
}
private void pushAndPrint() {
this.output.push();
System.out.print("A");
@ -220,4 +245,40 @@ class OutputCaptureTests { @@ -220,4 +245,40 @@ class OutputCaptureTests {
}
static class TestLatchedOutputCapture extends OutputCapture {
private final CountDownLatch waitAfterBuild = new CountDownLatch(1);
private final CountDownLatch releaseAfterBuild = new CountDownLatch(1);
@Override
String build(Predicate<Type> filter) {
var result = super.build(filter);
this.releaseAfterBuild.countDown();
await(this.waitAfterBuild);
return result;
}
void releaseAfterBuildAndAssertResultIsA() {
assertThat(getOut()).isEqualTo("A");
}
void awaitReleaseAfterBuildThenWriteBAndRelease() {
await(this.releaseAfterBuild);
System.out.print("B");
this.waitAfterBuild.countDown();
}
private void await(CountDownLatch latch) {
try {
latch.await();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RuntimeException(ex);
}
}
}
}

Loading…
Cancel
Save