From 48b573bc507d4d2990efcf41d163d61d111dc140 Mon Sep 17 00:00:00 2001 From: Pavel Anisimov Date: Sat, 27 Aug 2022 17:25:39 +0300 Subject: [PATCH 1/4] Add caching to OutputCapture.SystemCapture --- .../boot/test/system/OutputCapture.java | 34 ++++++++++++++++--- .../boot/test/system/package-info.java | 2 +- 2 files changed, 30 insertions(+), 6 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 9f98bfacbdca..3f39b93d8941 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 @@ -22,7 +22,9 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; @@ -39,6 +41,7 @@ * @author Phillip Webb * @author Andy Wilkinson * @author Sam Brannen + * @author Pavel Anisimov * @see OutputCaptureExtension * @see OutputCaptureRule */ @@ -97,7 +100,7 @@ public String toString() { */ @Override public String getAll() { - return get((type) -> true); + return get(Type.filterAll); } /** @@ -106,7 +109,7 @@ public String getAll() { */ @Override public String getOut() { - return get(Type.OUT::equals); + return get(Type.filterOut); } /** @@ -115,7 +118,7 @@ public String getOut() { */ @Override public String getErr() { - return get(Type.ERR::equals); + return get(Type.filterErr); } /** @@ -136,7 +139,7 @@ private String get(Predicate filter) { } /** - * A capture session that captures {@link System#out System.out} and {@link System#out + * A capture session that captures {@link System#out System.out} and {@link System#err * System.err}. */ private static class SystemCapture { @@ -149,6 +152,8 @@ private static class SystemCapture { private final List capturedStrings = new ArrayList<>(); + private final Map, String> filter2CachedCapturedStrings = new HashMap<>(); + SystemCapture() { this.out = new PrintStreamCapture(System.out, this::captureOut); this.err = new PrintStreamCapture(System.err, this::captureErr); @@ -163,28 +168,41 @@ void release() { private void captureOut(String string) { synchronized (this.monitor) { + filter2CachedCapturedStrings.remove(Type.filterOut); + filter2CachedCapturedStrings.remove(Type.filterAll); this.capturedStrings.add(new CapturedString(Type.OUT, string)); } } private void captureErr(String string) { synchronized (this.monitor) { + filter2CachedCapturedStrings.remove(Type.filterErr); + filter2CachedCapturedStrings.remove(Type.filterAll); this.capturedStrings.add(new CapturedString(Type.ERR, string)); } } void append(StringBuilder builder, Predicate filter) { synchronized (this.monitor) { + String cachedCapturedStrings = filter2CachedCapturedStrings.get(filter); + if (cachedCapturedStrings != null) { + builder.append(cachedCapturedStrings); + return; + } + StringBuilder cacheBuilder = new StringBuilder(); for (CapturedString stringCapture : this.capturedStrings) { if (filter.test(stringCapture.getType())) { builder.append(stringCapture); + cacheBuilder.append(stringCapture); } } + filter2CachedCapturedStrings.put(filter, cacheBuilder.toString()); } } void reset() { synchronized (this.monitor) { + filter2CachedCapturedStrings.clear(); this.capturedStrings.clear(); } } @@ -278,7 +296,13 @@ public String toString() { */ private enum Type { - OUT, ERR + OUT, ERR; + + private static final Predicate filterOut = OUT::equals; + + private static final Predicate filterErr = ERR::equals; + + private static final Predicate filterAll = type -> true; } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/package-info.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/package-info.java index 95345c57ea0c..c5d79774bd2c 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/package-info.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 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. From aac8ed19f67b1de7a719308b9671c62b8b52d44a Mon Sep 17 00:00:00 2001 From: Pavel Anisimov Date: Sun, 28 Aug 2022 07:23:02 +0300 Subject: [PATCH 2/4] Move caching from OutputCapture.SystemCapture to OutputCapture --- .../boot/test/system/OutputCapture.java | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 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 3f39b93d8941..8c8a7e425cea 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 @@ -22,9 +22,9 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Predicate; @@ -51,6 +51,8 @@ class OutputCapture implements CapturedOutput { private AnsiOutputState ansiOutputState; + private final Map, String> filter2CachedCapturedStrings = new ConcurrentHashMap<>(); + /** * Push a new system capture session onto the stack. */ @@ -58,7 +60,7 @@ final void push() { if (this.systemCaptures.isEmpty()) { this.ansiOutputState = AnsiOutputState.saveAndDisable(); } - this.systemCaptures.addLast(new SystemCapture()); + this.systemCaptures.addLast(new SystemCapture(this)); } /** @@ -131,11 +133,17 @@ void reset() { private String get(Predicate filter) { Assert.state(!this.systemCaptures.isEmpty(), "No system captures found. Please check your output capture registration."); + String cachedCapturedStrings = filter2CachedCapturedStrings.get(filter); + if (cachedCapturedStrings != null) { + return cachedCapturedStrings; + } StringBuilder builder = new StringBuilder(); for (SystemCapture systemCapture : this.systemCaptures) { systemCapture.append(builder, filter); } - return builder.toString(); + String capturedStrings = builder.toString(); + filter2CachedCapturedStrings.put(filter, capturedStrings); + return capturedStrings; } /** @@ -152,11 +160,12 @@ private static class SystemCapture { private final List capturedStrings = new ArrayList<>(); - private final Map, String> filter2CachedCapturedStrings = new HashMap<>(); + private final OutputCapture owner; - SystemCapture() { + SystemCapture(OutputCapture owner) { this.out = new PrintStreamCapture(System.out, this::captureOut); this.err = new PrintStreamCapture(System.err, this::captureErr); + this.owner = owner; System.setOut(this.out); System.setErr(this.err); } @@ -168,41 +177,33 @@ void release() { private void captureOut(String string) { synchronized (this.monitor) { - filter2CachedCapturedStrings.remove(Type.filterOut); - filter2CachedCapturedStrings.remove(Type.filterAll); + owner.filter2CachedCapturedStrings.remove(Type.filterOut); + owner.filter2CachedCapturedStrings.remove(Type.filterAll); this.capturedStrings.add(new CapturedString(Type.OUT, string)); } } private void captureErr(String string) { synchronized (this.monitor) { - filter2CachedCapturedStrings.remove(Type.filterErr); - filter2CachedCapturedStrings.remove(Type.filterAll); + owner.filter2CachedCapturedStrings.remove(Type.filterErr); + owner.filter2CachedCapturedStrings.remove(Type.filterAll); this.capturedStrings.add(new CapturedString(Type.ERR, string)); } } void append(StringBuilder builder, Predicate filter) { synchronized (this.monitor) { - String cachedCapturedStrings = filter2CachedCapturedStrings.get(filter); - if (cachedCapturedStrings != null) { - builder.append(cachedCapturedStrings); - return; - } - StringBuilder cacheBuilder = new StringBuilder(); for (CapturedString stringCapture : this.capturedStrings) { if (filter.test(stringCapture.getType())) { builder.append(stringCapture); - cacheBuilder.append(stringCapture); } } - filter2CachedCapturedStrings.put(filter, cacheBuilder.toString()); } } void reset() { synchronized (this.monitor) { - filter2CachedCapturedStrings.clear(); + owner.filter2CachedCapturedStrings.clear(); this.capturedStrings.clear(); } } From c03c12a09cdc4014ae64521ccc2d538d8d805926 Mon Sep 17 00:00:00 2001 From: Pavel Anisimov Date: Sun, 28 Aug 2022 08:21:01 +0300 Subject: [PATCH 3/4] Fix style --- .../boot/test/system/OutputCapture.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 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 8c8a7e425cea..785b05ddc2b5 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 @@ -133,7 +133,7 @@ void reset() { private String get(Predicate filter) { Assert.state(!this.systemCaptures.isEmpty(), "No system captures found. Please check your output capture registration."); - String cachedCapturedStrings = filter2CachedCapturedStrings.get(filter); + String cachedCapturedStrings = this.filter2CachedCapturedStrings.get(filter); if (cachedCapturedStrings != null) { return cachedCapturedStrings; } @@ -142,7 +142,7 @@ private String get(Predicate filter) { systemCapture.append(builder, filter); } String capturedStrings = builder.toString(); - filter2CachedCapturedStrings.put(filter, capturedStrings); + this.filter2CachedCapturedStrings.put(filter, capturedStrings); return capturedStrings; } @@ -177,16 +177,16 @@ void release() { private void captureOut(String string) { synchronized (this.monitor) { - owner.filter2CachedCapturedStrings.remove(Type.filterOut); - owner.filter2CachedCapturedStrings.remove(Type.filterAll); + this.owner.filter2CachedCapturedStrings.remove(Type.filterOut); + this.owner.filter2CachedCapturedStrings.remove(Type.filterAll); this.capturedStrings.add(new CapturedString(Type.OUT, string)); } } private void captureErr(String string) { synchronized (this.monitor) { - owner.filter2CachedCapturedStrings.remove(Type.filterErr); - owner.filter2CachedCapturedStrings.remove(Type.filterAll); + this.owner.filter2CachedCapturedStrings.remove(Type.filterErr); + this.owner.filter2CachedCapturedStrings.remove(Type.filterAll); this.capturedStrings.add(new CapturedString(Type.ERR, string)); } } @@ -203,7 +203,7 @@ void append(StringBuilder builder, Predicate filter) { void reset() { synchronized (this.monitor) { - owner.filter2CachedCapturedStrings.clear(); + this.owner.filter2CachedCapturedStrings.clear(); this.capturedStrings.clear(); } } @@ -303,7 +303,7 @@ private enum Type { private static final Predicate filterErr = ERR::equals; - private static final Predicate filterAll = type -> true; + private static final Predicate filterAll = (type) -> true; } From 237bc5dfb9aad4234042b4bf50487950d3a265e1 Mon Sep 17 00:00:00 2001 From: Pavel Anisimov Date: Sun, 28 Aug 2022 13:42:49 +0300 Subject: [PATCH 4/4] Fix bug --- .../java/org/springframework/boot/test/system/OutputCapture.java | 1 + 1 file changed, 1 insertion(+) 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 785b05ddc2b5..c3fb18049274 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 @@ -171,6 +171,7 @@ private static class SystemCapture { } void release() { + this.owner.filter2CachedCapturedStrings.clear(); System.setOut(this.out.getParent()); System.setErr(this.err.getParent()); }