Skip to content

Commit 4b19b61

Browse files
garyrussellartembilan
authored andcommitted
Convert repeated tests to JUnit 5 @RepeatableTest
- add lifecycle to `@LogLevels` to avoid adjusting the log on each iteration - with `@RepeatableTest` there is a template context between the class and method contexts * * Remove Lifecycle from `@LogLevels` * Only apply levels once for a class-level annotation * Log a new delimiter between tests when using a class-level annotation * Only log one delimiter per test method (e.g. when `@RepeatedTest`)
1 parent 28e9103 commit 4b19b61

File tree

6 files changed

+152
-158
lines changed

6 files changed

+152
-158
lines changed

spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LogLevels.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@
5353

5454
/**
5555
* The Log4j level name to switch the categories to during the test.
56-
* @return the level (default DEBUG).
56+
* @return the level (default Log4j {@code Levels.toLevel()} - currently, DEBUG).
5757
*/
58-
String level() default "DEBUG";
58+
String level() default "";
5959

6060
}

spring-rabbit-junit/src/main/java/org/springframework/amqp/rabbit/junit/LogLevelsCondition.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@
1717
package org.springframework.amqp.rabbit.junit;
1818

1919
import java.lang.reflect.AnnotatedElement;
20+
import java.lang.reflect.Method;
2021
import java.util.Arrays;
22+
import java.util.Map;
2123
import java.util.Optional;
24+
import java.util.concurrent.ConcurrentHashMap;
2225

26+
import org.apache.commons.logging.LogFactory;
2327
import org.apache.logging.log4j.Level;
2428
import org.junit.jupiter.api.extension.AfterAllCallback;
2529
import org.junit.jupiter.api.extension.AfterEachCallback;
30+
import org.junit.jupiter.api.extension.BeforeAllCallback;
2631
import org.junit.jupiter.api.extension.BeforeEachCallback;
2732
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
2833
import org.junit.jupiter.api.extension.ExecutionCondition;
@@ -33,6 +38,7 @@
3338
import org.springframework.amqp.rabbit.junit.JUnitUtils.LevelsContainer;
3439
import org.springframework.core.annotation.MergedAnnotation;
3540
import org.springframework.core.annotation.MergedAnnotations;
41+
import org.springframework.core.log.LogAccessor;
3642

3743
/**
3844
* JUnit condition that adjusts and reverts log levels before/after each test.
@@ -42,7 +48,9 @@
4248
*
4349
*/
4450
public class LogLevelsCondition
45-
implements ExecutionCondition, BeforeEachCallback, AfterEachCallback, AfterAllCallback {
51+
implements ExecutionCondition, BeforeEachCallback, AfterEachCallback, BeforeAllCallback, AfterAllCallback {
52+
53+
private static final LogAccessor logger = new LogAccessor(LogFactory.getLog(LogLevelsCondition.class));
4654

4755
private static final String STORE_ANNOTATION_KEY = "logLevelsAnnotation";
4856

@@ -51,6 +59,8 @@ public class LogLevelsCondition
5159
private static final ConditionEvaluationResult ENABLED =
5260
ConditionEvaluationResult.enabled("@LogLevels always enabled");
5361

62+
private final Map<String, Boolean> loggedMethods = new ConcurrentHashMap<>();
63+
5464
@Override
5565
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
5666
Optional<AnnotatedElement> element = context.getElement();
@@ -66,14 +76,9 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con
6676
}
6777

6878
@Override
69-
public void beforeEach(ExtensionContext context) {
79+
public void beforeAll(ExtensionContext context) {
7080
Store store = context.getStore(Namespace.create(getClass(), context));
7181
LogLevels logLevels = store.get(STORE_ANNOTATION_KEY, LogLevels.class);
72-
if (logLevels == null) {
73-
ExtensionContext parent = context.getParent().get();
74-
store = parent.getStore(Namespace.create(getClass(), parent));
75-
logLevels = store.get(STORE_ANNOTATION_KEY, LogLevels.class);
76-
}
7782
if (logLevels != null) {
7883
store.put(STORE_CONTAINER_KEY, JUnitUtils.adjustLogLevels(context.getDisplayName(),
7984
Arrays.asList((logLevels.classes())),
@@ -82,30 +87,50 @@ public void beforeEach(ExtensionContext context) {
8287
}
8388
}
8489

90+
@Override
91+
public void beforeEach(ExtensionContext context) {
92+
Store store = context.getStore(Namespace.create(getClass(), context));
93+
LogLevels logLevels = store.get(STORE_ANNOTATION_KEY, LogLevels.class);
94+
if (logLevels != null) { // Method level annotation
95+
if (store.get(STORE_CONTAINER_KEY) == null) {
96+
store.put(STORE_CONTAINER_KEY, JUnitUtils.adjustLogLevels(context.getDisplayName(),
97+
Arrays.asList((logLevels.classes())),
98+
Arrays.asList(logLevels.categories()),
99+
Level.toLevel(logLevels.level())));
100+
}
101+
}
102+
else {
103+
Optional<Method> testMethod = context.getTestMethod();
104+
if (testMethod.isPresent()
105+
&& this.loggedMethods.putIfAbsent(testMethod.get().getName(), Boolean.TRUE) == null) {
106+
logger.info(() -> "+++++++++++++++++++++++++++++ Begin " + testMethod.get().getName());
107+
}
108+
}
109+
}
110+
85111
@Override
86112
public void afterEach(ExtensionContext context) {
87113
Store store = context.getStore(Namespace.create(getClass(), context));
88114
LevelsContainer container = store.get(STORE_CONTAINER_KEY, LevelsContainer.class);
89-
boolean parentStore = false;
90-
if (container == null) {
91-
ExtensionContext parent = context.getParent().get();
92-
store = parent.getStore(Namespace.create(getClass(), parent));
93-
container = store.get(STORE_CONTAINER_KEY, LevelsContainer.class);
94-
parentStore = true;
95-
}
96115
if (container != null) {
97-
JUnitUtils.revertLevels(context.getDisplayName(), container);
98-
store.remove(STORE_CONTAINER_KEY);
99-
if (!parentStore) {
100-
store.remove(STORE_ANNOTATION_KEY);
116+
LogLevels logLevels = store.get(STORE_ANNOTATION_KEY, LogLevels.class);
117+
if (logLevels != null) {
118+
JUnitUtils.revertLevels(context.getDisplayName(), container);
119+
store.remove(STORE_CONTAINER_KEY);
101120
}
102121
}
103122
}
104123

105124
@Override
106125
public void afterAll(ExtensionContext context) {
107126
Store store = context.getStore(Namespace.create(getClass(), context));
108-
store.remove(STORE_ANNOTATION_KEY);
127+
LogLevels logLevels = store.remove(STORE_ANNOTATION_KEY, LogLevels.class);
128+
if (logLevels != null) {
129+
LevelsContainer container = store.get(STORE_CONTAINER_KEY, LevelsContainer.class);
130+
JUnitUtils.revertLevels(context.getDisplayName(), container);
131+
store.remove(STORE_CONTAINER_KEY);
132+
}
133+
this.loggedMethods.clear();
109134
}
110135

111136
}

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitTemplatePerformanceIntegrationTests.java

Lines changed: 63 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,22 @@
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
2020

21-
import org.apache.logging.log4j.Level;
22-
import org.junit.After;
23-
import org.junit.Before;
24-
import org.junit.Rule;
25-
import org.junit.Test;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.concurrent.CountDownLatch;
24+
import java.util.concurrent.ExecutorService;
25+
import java.util.concurrent.Executors;
26+
import java.util.concurrent.TimeUnit;
27+
import java.util.stream.Stream;
28+
29+
import org.junit.jupiter.api.AfterAll;
30+
import org.junit.jupiter.api.BeforeAll;
31+
import org.junit.jupiter.api.RepeatedTest;
2632

2733
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
28-
import org.springframework.amqp.rabbit.junit.BrokerRunning;
2934
import org.springframework.amqp.rabbit.junit.BrokerTestUtils;
30-
import org.springframework.amqp.rabbit.junit.LogLevelAdjuster;
31-
import org.springframework.amqp.rabbit.junit.LongRunningIntegrationTest;
32-
import org.springframework.amqp.rabbit.test.RepeatProcessor;
33-
import org.springframework.test.annotation.Repeat;
35+
import org.springframework.amqp.rabbit.junit.LogLevels;
36+
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
3437
import org.springframework.transaction.TransactionDefinition;
3538
import org.springframework.transaction.TransactionException;
3639
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
@@ -44,87 +47,77 @@
4447
* @since 1.0
4548
*
4649
*/
50+
@RabbitAvailable(queues = RabbitTemplatePerformanceIntegrationTests.ROUTE)
51+
@LogLevels(level = "ERROR", classes = RabbitTemplate.class)
4752
public class RabbitTemplatePerformanceIntegrationTests {
4853

49-
private static final String ROUTE = "test.queue";
54+
public static final String ROUTE = "test.queue.RabbitTemplatePerformanceIntegrationTests";
5055

51-
private final RabbitTemplate template = new RabbitTemplate();
56+
private static final RabbitTemplate template = new RabbitTemplate();
5257

53-
@Rule
54-
public LongRunningIntegrationTest longTests = new LongRunningIntegrationTest();
58+
private static final ExecutorService exec = Executors.newFixedThreadPool(4);
5559

56-
@Rule
57-
public RepeatProcessor repeat = new RepeatProcessor(4);
60+
private static CachingConnectionFactory connectionFactory;
5861

59-
@Rule
60-
// After the repeat processor, so it only runs once
61-
public LogLevelAdjuster logLevels = new LogLevelAdjuster(Level.ERROR, RabbitTemplate.class);
62-
63-
@Rule
64-
// After the repeat processor, so it only runs once
65-
public BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues(ROUTE);
66-
67-
private CachingConnectionFactory connectionFactory;
68-
69-
@Before
70-
public void declareQueue() {
71-
if (repeat.isInitialized()) {
72-
// Important to prevent concurrent re-initialization
73-
return;
74-
}
62+
@BeforeAll
63+
public static void declareQueue() {
7564
connectionFactory = new CachingConnectionFactory();
7665
connectionFactory.setHost("localhost");
77-
connectionFactory.setChannelCacheSize(repeat.getConcurrency());
7866
connectionFactory.setPort(BrokerTestUtils.getPort());
7967
template.setConnectionFactory(connectionFactory);
8068
}
8169

82-
@After
83-
public void cleanUp() {
84-
if (!repeat.isFinalizing()) {
85-
return;
86-
}
87-
this.template.stop();
88-
this.connectionFactory.destroy();
89-
this.brokerIsRunning.removeTestQueues();
70+
@AfterAll
71+
public static void cleanUp() {
72+
template.stop();
73+
connectionFactory.destroy();
74+
exec.shutdownNow();
9075
}
9176

92-
@Test
93-
@Repeat(200)
94-
public void testSendAndReceive() throws Exception {
95-
template.convertAndSend(ROUTE, "message");
96-
String result = (String) template.receiveAndConvert(ROUTE);
97-
int count = 5;
98-
while (result == null && count-- > 0) {
99-
/*
100-
* Retry for the purpose of non-transacted case because channel operations are async in that case
101-
*/
102-
Thread.sleep(10L);
103-
result = (String) template.receiveAndConvert(ROUTE);
104-
}
105-
assertThat(result).isEqualTo("message");
77+
@RepeatedTest(50)
78+
public void testSendAndReceive() throws InterruptedException {
79+
CountDownLatch latch = new CountDownLatch(4);
80+
List<String> results = new ArrayList<>();
81+
Stream.of(1, 2, 3, 4).forEach(i -> exec.execute(() -> {
82+
template.convertAndSend(ROUTE, "message");
83+
results.add((String) template.receiveAndConvert(ROUTE, 10_000L));
84+
latch.countDown();
85+
}));
86+
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
87+
assertThat(results).contains("message", "message", "message", "message");
10688
}
10789

108-
@Test
109-
@Repeat(200)
110-
public void testSendAndReceiveTransacted() throws Exception {
90+
@RepeatedTest(50)
91+
public void testSendAndReceiveTransacted() throws InterruptedException {
92+
CountDownLatch latch = new CountDownLatch(4);
93+
List<String> results = new ArrayList<>();
11194
template.setChannelTransacted(true);
112-
template.convertAndSend(ROUTE, "message");
113-
String result = (String) template.receiveAndConvert(ROUTE);
114-
assertThat(result).isEqualTo("message");
95+
Stream.of(1, 2, 3, 4).forEach(i -> exec.execute(() -> {
96+
template.convertAndSend(ROUTE, "message");
97+
results.add((String) template.receiveAndConvert(ROUTE, 10_000L));
98+
latch.countDown();
99+
}));
100+
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
101+
assertThat(results).contains("message", "message", "message", "message");
115102
}
116103

117-
@Test
118-
@Repeat(200)
119-
public void testSendAndReceiveExternalTransacted() throws Exception {
104+
@RepeatedTest(50)
105+
public void testSendAndReceiveExternalTransacted() throws InterruptedException {
106+
template.setChannelTransacted(true);
107+
CountDownLatch latch = new CountDownLatch(4);
108+
List<String> results = new ArrayList<>();
120109
template.setChannelTransacted(true);
121-
new TransactionTemplate(new TestTransactionManager()).execute(status -> {
110+
Stream.of(1, 2, 3, 4).forEach(i -> exec.execute(() -> {
111+
new TransactionTemplate(new TestTransactionManager()).execute(status -> {
112+
template.convertAndSend(ROUTE, "message");
113+
return null;
114+
});
122115
template.convertAndSend(ROUTE, "message");
123-
return null;
124-
});
125-
template.convertAndSend(ROUTE, "message");
126-
String result = (String) template.receiveAndConvert(ROUTE);
127-
assertThat(result).isEqualTo("message");
116+
results.add((String) template.receiveAndConvert(ROUTE, 10_000L));
117+
latch.countDown();
118+
}));
119+
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
120+
assertThat(results).contains("message", "message", "message", "message");
128121
}
129122

130123
@SuppressWarnings("serial")

0 commit comments

Comments
 (0)