Skip to content

Commit 1e35f68

Browse files
Add test for AuditableBeanWrapperFactory.
Assert AuditableBeanWrapperFactory behaves correctly under load. See: #3442
1 parent 2a5a92e commit 1e35f68

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

src/test/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactoryUnitTests.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232
import java.util.HashMap;
3333
import java.util.Map;
3434
import java.util.Optional;
35+
import java.util.concurrent.CompletableFuture;
36+
import java.util.concurrent.CountDownLatch;
37+
import java.util.concurrent.ExecutorService;
38+
import java.util.concurrent.Executors;
39+
import java.util.concurrent.TimeUnit;
40+
import java.util.stream.IntStream;
3541

3642
import org.assertj.core.api.AbstractLongAssert;
3743
import org.junit.jupiter.api.BeforeEach;
@@ -275,6 +281,61 @@ void shouldPassthruTemporalValue() {
275281
assertThat(source.created).isEqualTo(now);
276282
}
277283

284+
@Test // GH-3441
285+
void getBeanWrapperForIsThreadSafe() throws Exception {
286+
287+
int threadCount = 50;
288+
int iterationsPerThread = 100;
289+
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
290+
CountDownLatch startLatch = new CountDownLatch(1);
291+
CountDownLatch completionLatch = new CountDownLatch(threadCount);
292+
293+
try {
294+
295+
var futures = IntStream.range(0, threadCount).mapToObj(threadIndex -> CompletableFuture.runAsync(() -> {
296+
try {
297+
startLatch.await();
298+
299+
for (int i = 0; i < iterationsPerThread; i++) {
300+
var sample = new Sample();
301+
var wrapper = factory.getBeanWrapperFor(sample);
302+
303+
assertThat(wrapper).isPresent();
304+
assertThat(wrapper.get().getBean()).isSameAs(sample);
305+
306+
var sampleWithInstant = new SampleWithInstant();
307+
var wrapperWithInstant = factory.getBeanWrapperFor(sampleWithInstant);
308+
309+
assertThat(wrapperWithInstant).isPresent();
310+
assertThat(wrapperWithInstant.get().getBean()).isSameAs(sampleWithInstant);
311+
312+
var withEmbedded = new WithEmbedded();
313+
var wrapperWithEmbedded = factory.getBeanWrapperFor(withEmbedded);
314+
315+
assertThat(wrapperWithEmbedded).isPresent();
316+
assertThat(wrapperWithEmbedded.get().getBean()).isSameAs(withEmbedded);
317+
}
318+
} catch (InterruptedException e) {
319+
Thread.currentThread().interrupt();
320+
throw new RuntimeException(e);
321+
} finally {
322+
completionLatch.countDown();
323+
}
324+
}, executor)).toList();
325+
326+
startLatch.countDown();
327+
328+
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(30, TimeUnit.SECONDS);
329+
assertThat(completionLatch.await(1, TimeUnit.SECONDS)).isTrue();
330+
331+
Map<?, ?> metadataCache = (Map<?, ?>) ReflectionTestUtils.getField(factory, "metadataCache");
332+
assertThat(metadataCache).hasSize(4);
333+
} finally {
334+
executor.shutdown();
335+
assertThat(executor.awaitTermination(5, TimeUnit.SECONDS)).isTrue();
336+
}
337+
}
338+
278339
private void assertLastModificationDate(Object source, TemporalAccessor expected) {
279340

280341
var sample = new Sample();

0 commit comments

Comments
 (0)