From 368e6970480a0969c0b27f9f4e38d0df8d1051b4 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 3 Jun 2019 13:57:21 +0300 Subject: [PATCH 1/4] Polish Javadoc --- .../boot/test/system/CapturedOutput.java | 2 +- .../test/system/OutputCaptureExtension.java | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/CapturedOutput.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/CapturedOutput.java index 166cb70e7b1a..3ec0ee766e51 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/CapturedOutput.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/CapturedOutput.java @@ -23,7 +23,7 @@ *
  * assertThat(output).contains("started"); // Checks all output
  * assertThat(output.getErr()).contains("failed"); // Only checks System.err
- * assertThat(output.getOut()).contains("ok"); // Only checks System.put
+ * assertThat(output.getOut()).contains("ok"); // Only checks System.out
  * 
* * @author Madhura Bhave diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java index a5672ee5e29a..060827730924 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java @@ -27,14 +27,14 @@ import org.junit.jupiter.api.extension.ParameterResolver; /** - * JUnit 5 {@code @Extension} to capture {@link System#out System.out} and - * {@link System#err System.err}. Can be used on a test class via - * {@link ExtendWith @ExtendWith}. This extension provides {@link ParameterResolver - * parameter resolution} for a {@link CapturedOutput} instance which can be used to assert - * that the correct output was written. + * JUnit Jupiter {@code @Extension} to capture {@link System#out System.out} and + * {@link System#err System.err}. Can be registered for an entire test class or for an + * individual test method via {@link ExtendWith @ExtendWith}. This extension provides + * {@linkplain ParameterResolver parameter resolution} for a {@link CapturedOutput} + * instance which can be used to assert that the correct output was written. *

* To use with {@link ExtendWith @ExtendWith}, inject the {@link CapturedOutput} as an - * argument to your test class constructor or test method: + * argument to your test class constructor, test method, or lifecycle methods: * *

  * @ExtendWith(OutputCaptureExtension.class)
@@ -42,7 +42,15 @@
  *
  *     @Test
  *     void test(CapturedOutput output) {
+ *         System.out.println("ok");
  *         assertThat(output).contains("ok");
+ *         System.err.println("error");
+ *     }
+ *
+ *     @AfterEach
+ *     void after(CapturedOutput output) {
+ *         assertThat(output.getOut()).contains("ok");
+ *         assertThat(output.getErr()).contains("error");
  *     }
  *
  * }
@@ -51,6 +59,7 @@
  * @author Madhura Bhave
  * @author Phillip Webb
  * @author Andy Wilkinson
+ * @author Sam Brannen
  * @since 2.2.0
  * @see CapturedOutput
  */

From 9a2fe1ddb9050e9fe16583beedb48945f435c817 Mon Sep 17 00:00:00 2001
From: Sam Brannen 
Date: Mon, 3 Jun 2019 13:58:06 +0300
Subject: [PATCH 2/4] Fix error message in OutputCapture

---
 .../springframework/boot/test/system/OutputCapture.java   | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java
index 487975794068..eff1b25c9bda 100644
--- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java
@@ -39,6 +39,7 @@
  * @author Madhura Bhave
  * @author Phillip Webb
  * @author Andy Wilkinson
+ * @author Sam Brannen
  * @since 2.2.0
  * @see OutputCaptureExtension
  * @see OutputCaptureRule
@@ -128,8 +129,11 @@ void reset() {
 
 	private String get(Predicate filter) {
 		Assert.state(!this.systemCaptures.isEmpty(),
-				"No system captures found. Check that you have used @RegisterExtension "
-						+ "or @ExtendWith and the fields are not private");
+				"No system captures found. When using JUnit 4, ensure that you have "
+						+ "registered the OutputCaptureRule via @ClassRule or @Rule "
+						+ "and that the field is public. "
+						+ "When using JUnit Jupiter, ensure that you have registered "
+						+ "the OutputCaptureExtension via @ExtendWith.");
 		StringBuilder builder = new StringBuilder();
 		for (SystemCapture systemCapture : this.systemCaptures) {
 			systemCapture.append(builder, filter);

From 490e31570c9f856e57c470b9018906f5567bc051 Mon Sep 17 00:00:00 2001
From: Sam Brannen 
Date: Mon, 3 Jun 2019 14:00:30 +0300
Subject: [PATCH 3/4] Use ExtensionContext.Store in OutputCaptureExtension

This commit refactors OutputCaptureExtension so that state is stored in
the ExtensionContext.Store instead of in the extension.

This is more idiomatic for JUnit Jupiter extensions and allows for
potential concurrent use of the extension.
---
 .../test/system/OutputCaptureExtension.java   | 24 ++++++++++++-------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java
index 060827730924..7007d0a7414b 100644
--- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCaptureExtension.java
@@ -22,6 +22,8 @@
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
 import org.junit.jupiter.api.extension.ParameterContext;
 import org.junit.jupiter.api.extension.ParameterResolutionException;
 import org.junit.jupiter.api.extension.ParameterResolver;
@@ -66,30 +68,28 @@
 public class OutputCaptureExtension implements BeforeAllCallback, AfterAllCallback,
 		BeforeEachCallback, AfterEachCallback, ParameterResolver {
 
-	private final OutputCapture outputCapture = new OutputCapture();
-
 	OutputCaptureExtension() {
 		// Package private to prevent users from directly creating an instance.
 	}
 
 	@Override
 	public void beforeAll(ExtensionContext context) throws Exception {
-		this.outputCapture.push();
+		getOutputCapture(context).push();
 	}
 
 	@Override
 	public void afterAll(ExtensionContext context) throws Exception {
-		this.outputCapture.pop();
+		getOutputCapture(context).pop();
 	}
 
 	@Override
 	public void beforeEach(ExtensionContext context) throws Exception {
-		this.outputCapture.push();
+		getOutputCapture(context).push();
 	}
 
 	@Override
 	public void afterEach(ExtensionContext context) throws Exception {
-		this.outputCapture.pop();
+		getOutputCapture(context).pop();
 	}
 
 	@Override
@@ -100,8 +100,16 @@ public boolean supportsParameter(ParameterContext parameterContext,
 
 	@Override
 	public Object resolveParameter(ParameterContext parameterContext,
-			ExtensionContext extensionContext) throws ParameterResolutionException {
-		return this.outputCapture;
+			ExtensionContext extensionContext) {
+		return getOutputCapture(extensionContext);
+	}
+
+	private OutputCapture getOutputCapture(ExtensionContext context) {
+		return getStore(context).getOrComputeIfAbsent(OutputCapture.class);
+	}
+
+	private Store getStore(ExtensionContext context) {
+		return context.getStore(Namespace.create(getClass()));
 	}
 
 }

From 8ce12377d337b0d1cfcf7d25beb5acf9fc5e44b0 Mon Sep 17 00:00:00 2001
From: Sam Brannen 
Date: Mon, 3 Jun 2019 14:01:51 +0300
Subject: [PATCH 4/4] Introduce failing concurrent tests for
 OutputCaptureExtension

This commit introduces failing tests for the OutputCaptureExtension,
which are currently @Disabled.
---
 ...ConcurrentOutputCaptureExtensionTests.java | 71 +++++++++++++++++++
 .../test/resources/junit-platform.properties  |  1 +
 2 files changed, 72 insertions(+)
 create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/ConcurrentOutputCaptureExtensionTests.java
 create mode 100644 spring-boot-project/spring-boot-test/src/test/resources/junit-platform.properties

diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/ConcurrentOutputCaptureExtensionTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/ConcurrentOutputCaptureExtensionTests.java
new file mode 100644
index 000000000000..81ce9120f85a
--- /dev/null
+++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/ConcurrentOutputCaptureExtensionTests.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.system;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.RepetitionInfo;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link OutputCaptureExtension} when test methods are executed concurrently.
+ *
+ * @author Sam Brannen
+ */
+@ExtendWith(OutputCaptureExtension.class)
+@Execution(ExecutionMode.CONCURRENT)
+@Disabled("OutputCaptureExtension currently does not support concurrent execution")
+class ConcurrentOutputCaptureExtensionTests {
+
+	@BeforeEach
+	void beforeEach() {
+		System.out.println("beforeEach");
+	}
+
+	@RepeatedTest(5)
+	void captureOutput(RepetitionInfo repetitionInfo, CapturedOutput output) {
+		String testOutput = "test output: " + repetitionInfo.getCurrentRepetition();
+		String testError = "test error: " + repetitionInfo.getCurrentRepetition();
+
+		System.out.println(testOutput);
+		System.err.println(testError);
+
+		assertThat(lines(output))//
+				.containsExactlyInAnyOrder("beforeEach", testOutput, testError);
+		assertThat(lines(output.getOut()))//
+				.containsExactly("beforeEach", testOutput);
+		assertThat(lines(output.getErr()))//
+				.containsExactly(testError);
+	}
+
+	private String[] lines(CharSequence text) {
+		return text.toString().split(System.lineSeparator());
+	}
+
+	@AfterEach
+	void after(CapturedOutput output) {
+		assertThat(output.getOut()).contains("beforeEach", "test output");
+		assertThat(output.getErr()).contains("test error");
+	}
+
+}
diff --git a/spring-boot-project/spring-boot-test/src/test/resources/junit-platform.properties b/spring-boot-project/spring-boot-test/src/test/resources/junit-platform.properties
new file mode 100644
index 000000000000..1d27b78fbbb9
--- /dev/null
+++ b/spring-boot-project/spring-boot-test/src/test/resources/junit-platform.properties
@@ -0,0 +1 @@
+junit.jupiter.execution.parallel.enabled=true