PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/test/java/jenkins/plugins/htmlaudio/domain/VolatileNotificationRepoAndFactoryStressTest.java

https://github.com/jenkinsci/html-audio-notifier-plugin
Java | 189 lines | 141 code | 45 blank | 3 comment | 13 complexity | 8445190bbacb96b32dc1beaeeca495ef MD5 | raw file
  1. package jenkins.plugins.htmlaudio.domain;
  2. import static java.util.Arrays.asList;
  3. import static jenkins.plugins.htmlaudio.domain.NotificationId.asNotificationId;
  4. import static org.junit.Assert.*;
  5. import java.util.ArrayList;
  6. import java.util.Collections;
  7. import java.util.HashSet;
  8. import java.util.Iterator;
  9. import java.util.List;
  10. import java.util.Random;
  11. import java.util.Set;
  12. import java.util.concurrent.CountDownLatch;
  13. import java.util.concurrent.TimeUnit;
  14. import jenkins.plugins.htmlaudio.domain.NotificationRepository.NotificationRemover;
  15. import jenkins.plugins.htmlaudio.domain.impl.VolatileNotificationRepositoryAndFactory;
  16. import org.junit.Test;
  17. import org.junit.runner.RunWith;
  18. import org.junit.runners.JUnit4;
  19. @RunWith(JUnit4.class)
  20. public class VolatileNotificationRepoAndFactoryStressTest {
  21. private static final int NUM_CLIENTS = 200;
  22. private static final int NUM_NOTIFICATIONS_PER_CLIENT = 200;
  23. private final VolatileNotificationRepositoryAndFactory impl
  24. = new VolatileNotificationRepositoryAndFactory();
  25. private final NotificationRepository repo = impl;
  26. private final NotificationFactory factory = impl;
  27. private final CountDownLatch readyForAction = new CountDownLatch(NUM_CLIENTS);
  28. private final CountDownLatch start = new CountDownLatch(1);
  29. private final CountDownLatch completed = new CountDownLatch(NUM_CLIENTS);
  30. private final Set<String> completedClients = Collections.synchronizedSet(new HashSet<String>());
  31. @Test
  32. public void repo_maintains_integrity_during_onslaught_of_crazy_monkeys() {
  33. final long started = System.currentTimeMillis();
  34. runClients();
  35. // make sure that the expected number of notifications was created
  36. final Notification n = factory.createAndPersist("url", "");
  37. assertEquals((NUM_CLIENTS * NUM_NOTIFICATIONS_PER_CLIENT) + 1,
  38. n.getId().getValue());
  39. // and that the repo is still sane after the beating =)
  40. assertEquals(asList(n), repo.findNewerThan(asNotificationId(n.getId().getValue() - 1)));
  41. assertEquals(asList(n), repo.findNewerThan(null));
  42. System.out.println("completed in " + (System.currentTimeMillis() - started) + "ms");
  43. }
  44. private void runClients() {
  45. for (int i = 0; i < NUM_CLIENTS; i++) {
  46. new Client().start();
  47. }
  48. await(readyForAction, "readyForAction");
  49. start.countDown();
  50. await(completed, "completed");
  51. assertEquals("some clients failed", NUM_CLIENTS, completedClients.size());
  52. }
  53. private void await(CountDownLatch l, String latchName) {
  54. try {
  55. assertTrue("timed out while waiting for '" + latchName + "'",
  56. l.await(30, TimeUnit.SECONDS));
  57. } catch (InterruptedException e) {
  58. throw new RuntimeException(e);
  59. }
  60. }
  61. private class Client extends Thread {
  62. private final Random rnd = new Random();
  63. private final List<NotificationId> myNotifications = new ArrayList<NotificationId>();
  64. private int remaining = NUM_NOTIFICATIONS_PER_CLIENT;
  65. public Client() {
  66. setDaemon(true);
  67. }
  68. @Override
  69. public void run() {
  70. readyForAction.countDown();
  71. try {
  72. log("ready...");
  73. await(start, "start");
  74. while (remaining > 0) {
  75. createSome();
  76. selectSome();
  77. if (remaining % 2 == 0) {
  78. removeSome();
  79. }
  80. }
  81. removeAll();
  82. completedClients.add(Thread.currentThread().getName());
  83. } finally {
  84. completed.countDown();
  85. }
  86. }
  87. private void createSome() {
  88. final int createCount = rnd.nextInt(remaining + 1);
  89. log("creating " + createCount + " notifications" + ", " + remaining + " remaining");
  90. for (int i = 0; i < createCount; i++) {
  91. final Notification n = factory.createAndPersist("url", Thread.currentThread().getName());
  92. myNotifications.add(n.getId());
  93. remaining--;
  94. }
  95. }
  96. private void removeSome() {
  97. final int removeCount = rnd.nextInt(myNotifications.size() + 1);
  98. log("removing " + removeCount + " notifications" + ", "
  99. + myNotifications.size() + " currently in repo");
  100. repo.remove(new NotificationRemover() {
  101. public void remove(Iterator<Notification> notifications) {
  102. int removed = 0;
  103. while (removed < removeCount
  104. && notifications.hasNext()) {
  105. final Notification n = notifications.next();
  106. if (myNotifications.contains(n.getId())) {
  107. notifications.remove();
  108. myNotifications.remove(n.getId());
  109. removed++;
  110. }
  111. }
  112. assertEquals(removeCount, removed);
  113. }
  114. });
  115. }
  116. private void selectSome() {
  117. if (myNotifications.isEmpty()) {
  118. return;
  119. }
  120. final int selectFrom = rnd.nextInt(myNotifications.size());
  121. final int selectedCount = myNotifications.size() - selectFrom;
  122. log("selecting " + selectedCount + "/" + myNotifications.size() + " of my own notifications");
  123. int foundCount = 0;
  124. for (Notification n :
  125. repo.findNewerThan(asNotificationId(myNotifications.get(selectFrom).getValue() - 1))) {
  126. final int index = myNotifications.indexOf(n.getId());
  127. if (index == -1) {
  128. continue;
  129. }
  130. assertTrue(index >= selectFrom);
  131. foundCount++;
  132. }
  133. assertEquals(selectedCount, foundCount);
  134. }
  135. private void removeAll() {
  136. while (!myNotifications.isEmpty()) {
  137. removeSome();
  138. }
  139. }
  140. private void log(String message) {
  141. // System.out.println("Client#" + Thread.currentThread().getName() + ": " + message);
  142. }
  143. }
  144. }