From 551840a46f535f3e0d0818ba3f34878148eade96 Mon Sep 17 00:00:00 2001 From: Viktor Seifert Date: Tue, 7 Mar 2023 11:36:44 +0100 Subject: [PATCH] RED-6310: Moved code for multithreaded tests to a helper class (cherry picked from commit 40d6961742436fd4f7c5e07616850d8b1eb31a43) --- .../NotificationPreferencesServiceTest.java | 42 +++-------- .../integration/tests/NotificationTest.java | 75 ++++--------------- .../utils/MultithreadedTestRunner.java | 71 ++++++++++++++++++ 3 files changed, 96 insertions(+), 92 deletions(-) create mode 100644 persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/MultithreadedTestRunner.java diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationPreferencesServiceTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationPreferencesServiceTest.java index 9348d16b0..75428b5c3 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationPreferencesServiceTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationPreferencesServiceTest.java @@ -2,14 +2,12 @@ package com.iqser.red.service.peristence.v1.server.integration.tests; import static org.assertj.core.api.Assertions.assertThat; -import java.util.ArrayList; -import java.util.Collections; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest; +import com.iqser.red.service.peristence.v1.server.integration.utils.MultithreadedTestRunner; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPreferencesPersistenceService; import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; @@ -25,6 +23,9 @@ public class NotificationPreferencesServiceTest extends AbstractPersistenceServe @Autowired NotificationPreferencesPersistenceService notificationPreferencesPersistenceService; + final MultithreadedTestRunner multithreadedTestRunner = new MultithreadedTestRunner(2, 1000); + + @BeforeEach public void setup() { @@ -37,38 +38,15 @@ public class NotificationPreferencesServiceTest extends AbstractPersistenceServe public void testNotificationPreferencesConcurrent() { final String userId = "1"; + Runnable test = () -> notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(userId); + Runnable afterTest = () -> notificationPreferencesPersistenceService.deleteNotificationPreferences(userId); + var exceptions = multithreadedTestRunner.runMutlithreadedCollectingExceptions(test, afterTest); - for (int i = 0; i < 1000; i++) { - var exceptions = Collections.synchronizedList(new ArrayList()); - - Thread t1 = new Thread(() -> { - try { - notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(userId); - } catch (Exception e) { - exceptions.add(e); - } - }); - Thread t2 = new Thread(() -> { - try { - notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(userId); - } catch (Exception e) { - exceptions.add(e); - } - }); - t1.start(); - t2.start(); - t1.join(); - t2.join(); - - notificationPreferencesPersistenceService.deleteNotificationPreferences(userId); - - for (Exception ex : exceptions) { - log.error("Exception during notification creation", ex); - } - - assertThat(exceptions).isEmpty(); + for (Exception ex : exceptions) { + log.error("Exception during notification creation", ex); } + assertThat(exceptions).isEmpty(); } } diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationTest.java index 264d06274..5ac460e5e 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/NotificationTest.java @@ -3,8 +3,6 @@ package com.iqser.red.service.peristence.v1.server.integration.tests; import static org.assertj.core.api.Assertions.assertThat; import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.Collections; import java.util.Map; import org.assertj.core.util.Lists; @@ -14,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import com.iqser.red.service.peristence.v1.server.integration.client.NotificationClient; import com.iqser.red.service.peristence.v1.server.integration.client.NotificationPreferencesClient; import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest; +import com.iqser.red.service.peristence.v1.server.integration.utils.MultithreadedTestRunner; import com.iqser.red.service.persistence.service.v1.api.model.audit.AddNotificationRequest; import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive; import com.iqser.red.service.persistence.service.v1.api.model.notification.Notification; @@ -32,6 +31,8 @@ public class NotificationTest extends AbstractPersistenceServerServiceTest { @Autowired private NotificationPreferencesClient notificationPreferencesClient; + private final MultithreadedTestRunner multithreadedTestRunner = new MultithreadedTestRunner(2, 1000); + @Test public void testNotificationPreferences() { @@ -119,38 +120,15 @@ public class NotificationTest extends AbstractPersistenceServerServiceTest { public void testNotificationPreferencesConcurrent() { final String userId = "1"; + Runnable test = () -> notificationPreferencesClient.getNotificationPreferences(userId); + Runnable afterTest = () -> notificationPreferencesClient.deleteNotificationPreferences(userId); + var exceptions = multithreadedTestRunner.runMutlithreadedCollectingExceptions(test, afterTest); - for (int i = 0; i < 1000; i++) { - var exceptions = Collections.synchronizedList(new ArrayList()); - - Thread t1 = new Thread(() -> { - try { - notificationPreferencesClient.getNotificationPreferences(userId); - } catch (Exception e) { - exceptions.add(e); - } - }); - Thread t2 = new Thread(() -> { - try { - notificationPreferencesClient.getNotificationPreferences(userId); - } catch (Exception e) { - exceptions.add(e); - } - }); - t1.start(); - t2.start(); - t1.join(); - t2.join(); - - notificationPreferencesClient.deleteNotificationPreferences(userId); - - for (Exception ex : exceptions) { - log.error("Exception during notification creation", ex); - } - - assertThat(exceptions).isEmpty(); + for (Exception ex : exceptions) { + log.error("Exception during notification creation", ex); } + assertThat(exceptions).isEmpty(); } @@ -159,38 +137,15 @@ public class NotificationTest extends AbstractPersistenceServerServiceTest { public void testNotificationsConcurrent() { final String userId = "1"; + Runnable test = () -> notificationClient.getNotifications(userId, false); + Runnable afterTest = () -> notificationPreferencesClient.deleteNotificationPreferences(userId); + var exceptions = multithreadedTestRunner.runMutlithreadedCollectingExceptions(test, afterTest); - for (int i = 0; i < 1000; i++) { - var exceptions = Collections.synchronizedList(new ArrayList()); - - Thread t1 = new Thread(() -> { - try { - notificationClient.getNotifications(userId, false); - } catch (Exception e) { - exceptions.add(e); - } - }); - Thread t2 = new Thread(() -> { - try { - notificationClient.getNotifications(userId, false); - } catch (Exception e) { - exceptions.add(e); - } - }); - t1.start(); - t2.start(); - t1.join(); - t2.join(); - - notificationPreferencesClient.deleteNotificationPreferences(userId); - - for (Exception ex : exceptions) { - log.error("Exception during notification creation", ex); - } - - assertThat(exceptions).isEmpty(); + for (Exception ex : exceptions) { + log.error("Exception during notification creation", ex); } + assertThat(exceptions).isEmpty(); } } diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/MultithreadedTestRunner.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/MultithreadedTestRunner.java new file mode 100644 index 000000000..2dd4cc6c8 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/MultithreadedTestRunner.java @@ -0,0 +1,71 @@ +package com.iqser.red.service.peristence.v1.server.integration.utils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@RequiredArgsConstructor +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class MultithreadedTestRunner { + + int numberOfThreads; + int numberOfExecutions; + + + public List runMutlithreadedCollectingExceptions(boolean stopOnFirstRunWithExceptions, Runnable test, Runnable afterTest) { + + List allExceptions = new ArrayList<>(); + + for (int execution = 1; execution <= numberOfExecutions; execution++) { + var threads = new ArrayList(numberOfThreads); + var exceptions = Collections.synchronizedList(new ArrayList()); + + for (int threadNumber = 1; threadNumber <= numberOfThreads; threadNumber++) { + Thread t = new Thread(() -> { + try { + test.run(); + } catch (Exception e) { + exceptions.add(e); + } + }); + + threads.add(t); + } + + for (Thread t : threads) { + t.start(); + } + + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + afterTest.run(); + + if (stopOnFirstRunWithExceptions) { + if (!exceptions.isEmpty()) { + return exceptions; + } + } else { + allExceptions.addAll(exceptions); + } + } + + return allExceptions; + } + + + public List runMutlithreadedCollectingExceptions(Runnable test, Runnable afterTest) { + + return runMutlithreadedCollectingExceptions(true, test, afterTest); + } + +}