Handle nullable lists
[genius.git] / idmanager / idmanager-impl / src / test / java / org / opendaylight / genius / idmanager / test / IdManagerTest.java
index cba03106f253887bb9e39f71be9d2f12ba430757..23f7d02919be388e79d1881a5f1a5ef6fedeb742 100644 (file)
@@ -8,29 +8,31 @@
 package org.opendaylight.genius.idmanager.test;
 
 import static java.util.Comparator.comparing;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.genius.idmanager.IdUtils.nullToEmpty;
 
 import com.google.common.base.Optional;
-
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
-
 import javax.inject.Inject;
-
+import junit.framework.AssertionFailedError;
 import org.junit.Before;
 import org.junit.ComparisonFailure;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -45,7 +47,9 @@ import org.opendaylight.genius.datastoreutils.testutils.JobCoordinatorTestModule
 import org.opendaylight.genius.datastoreutils.testutils.TestableDataTreeChangeListenerModule;
 import org.opendaylight.genius.idmanager.IdUtils;
 import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule;
+import org.opendaylight.infrautils.testutils.LogCaptureRule;
 import org.opendaylight.infrautils.testutils.LogRule;
+import org.opendaylight.infrautils.utils.concurrent.Executors;
 import org.opendaylight.mdsal.binding.testutils.AssertDataObjects;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
@@ -73,9 +77,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 
 public class IdManagerTest {
 
+    private static final Logger LOG = LoggerFactory.getLogger(IdManagerTest.class);
+
     private static final String TEST_KEY1 = "test-key1";
     private static final String TEST_KEY2 = "test-key2";
     private static final String ID_POOL_NAME = "test-pool";
@@ -83,7 +92,12 @@ public class IdManagerTest {
     private static final long ID_LOW = 0L;
     private static final long ID_HIGH = 100L;
 
+    // public static @ClassRule RunUntilFailureClassRule classRepeater = new RunUntilFailureClassRule();
+    // public @Rule RunUntilFailureRule repeater = new RunUntilFailureRule(classRepeater);
+
     public @Rule LogRule logRule = new LogRule();
+    public @Rule LogCaptureRule logCaptureRule = new LogCaptureRule();
+
     public @Rule MethodRule guice = new GuiceRule(IdManagerTestModule.class,
             TestableDataTreeChangeListenerModule.class, JobCoordinatorTestModule.class);
 
@@ -151,9 +165,10 @@ public class IdManagerTest {
         asyncEventsWaiter.awaitEventsConsumption();
 
         String localPoolName = idUtils.getLocalPoolName(ID_POOL_NAME);
-        IdPool parentIdPool = new IdPoolBuilder().setPoolName(ID_POOL_NAME).setKey(new IdPoolKey(ID_POOL_NAME))
+        IdPool parentIdPool = new IdPoolBuilder().setPoolName(ID_POOL_NAME).withKey(new IdPoolKey(ID_POOL_NAME))
+                .setAvailableIdsHolder(createAvailableIdHolder(ID_LOW, ID_HIGH, ID_HIGH + 1))
                 .setReleasedIdsHolder(createReleaseIdHolder(Arrays.asList(1L, 2L, 3L))).build();
-        IdPool childPool = new IdPoolBuilder().setPoolName(localPoolName).setKey(new IdPoolKey(localPoolName))
+        IdPool childPool = new IdPoolBuilder().setPoolName(localPoolName).withKey(new IdPoolKey(localPoolName))
                 .setAvailableIdsHolder(createAvailableIdHolder(0L, 9L, 10L)).build();
         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
         tx.merge(LogicalDatastoreType.CONFIGURATION, getIdPoolIdentifier(ID_POOL_NAME), parentIdPool);
@@ -182,12 +197,13 @@ public class IdManagerTest {
         Optional<IdPool> actualIdPoolChild = singleTxdataBroker.syncReadOptional(LogicalDatastoreType.CONFIGURATION,
                 InstanceIdentifier.builder(IdPools.class)
                         .child(IdPool.class, new IdPoolKey(idUtils.getLocalPoolName(ID_POOL_NAME))).build());
-        assertEquals(false, actualIdPoolParent.isPresent());
-        assertEquals(false, actualIdPoolChild.isPresent());
+        assertFalse(actualIdPoolParent.isPresent());
+        assertFalse(actualIdPoolChild.isPresent());
     }
 
     @Test
-    public void testMultithreadedIdAllocationFromAvailableIds() throws Exception {
+    @SuppressWarnings("checkstyle:IllegalThrows") // OK as exceptionInExecutor can't be Exception & AssertionFailedError
+    public void testMultithreadedIdAllocationFromAvailableIds() throws Throwable {
         CreateIdPoolInput createIdPoolInput = new CreateIdPoolInputBuilder().setHigh(ID_HIGH).setLow(ID_LOW)
                 .setPoolName(ID_POOL_NAME).build();
         idManagerService.createIdPool(createIdPoolInput);
@@ -205,7 +221,8 @@ public class IdManagerTest {
     }
 
     @Test
-    public void testMultithreadedIdAllocationFromReleaseIds() throws Exception {
+    @SuppressWarnings("checkstyle:IllegalThrows") // OK as exceptionInExecutor can't be Exception & AssertionFailedError
+    public void testMultithreadedIdAllocationFromReleaseIds() throws Throwable {
         CreateIdPoolInput createIdPoolInput = new CreateIdPoolInputBuilder().setHigh(ID_HIGH).setLow(ID_LOW)
                 .setPoolName(ID_POOL_NAME).build();
         AllocateIdInput allocateIdInput = new AllocateIdInputBuilder().setIdKey(TEST_KEY1).setPoolName(ID_POOL_NAME)
@@ -216,9 +233,10 @@ public class IdManagerTest {
         coordinatorEventsWaiter.awaitEventsConsumption();
 
         String localPoolName = idUtils.getLocalPoolName(ID_POOL_NAME);
-        IdPool parentIdPool = new IdPoolBuilder().setPoolName(ID_POOL_NAME).setKey(new IdPoolKey(ID_POOL_NAME))
-                .setReleasedIdsHolder(createReleaseIdHolder(Arrays.asList(1L, 2L, 3L))).build();
-        IdPool childPool = new IdPoolBuilder().setPoolName(localPoolName).setKey(new IdPoolKey(localPoolName))
+        IdPool parentIdPool = new IdPoolBuilder().setPoolName(ID_POOL_NAME).withKey(new IdPoolKey(ID_POOL_NAME))
+                .setReleasedIdsHolder(createReleaseIdHolder(Collections.emptyList())).build();
+        IdPool childPool = new IdPoolBuilder().setPoolName(localPoolName).withKey(new IdPoolKey(localPoolName))
+                .setReleasedIdsHolder(createReleaseIdHolder(Arrays.asList(1L, 2L, 3L)))
                 .setAvailableIdsHolder(createAvailableIdHolder(0L, 9L, 10L)).build();
         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
         tx.merge(LogicalDatastoreType.CONFIGURATION, getIdPoolIdentifier(ID_POOL_NAME), parentIdPool);
@@ -242,8 +260,8 @@ public class IdManagerTest {
     }
 
     @Test
-    @Ignore // TODO make less brittle and completely reliable, see https://jira.opendaylight.org/browse/GENIUS-105
-    public void testMultithreadedIdAllocationForSameKeyFromAvailableIds() throws Exception {
+    @SuppressWarnings("checkstyle:IllegalThrows") // OK as exceptionInExecutor can't be Exception & AssertionFailedError
+    public void testMultithreadedIdAllocationForSameKeyFromAvailableIds() throws Throwable {
         CreateIdPoolInput createIdPoolInput = new CreateIdPoolInputBuilder().setHigh(ID_HIGH).setLow(ID_LOW)
                 .setPoolName(ID_POOL_NAME).build();
         idManagerService.createIdPool(createIdPoolInput);
@@ -254,8 +272,8 @@ public class IdManagerTest {
     }
 
     @Test
-    @Ignore
-    public void testMultithreadedIdAllocationForSameKeyFromReleasedIds() throws Exception {
+    @SuppressWarnings("checkstyle:IllegalThrows") // OK as exceptionInExecutor can't be Exception & AssertionFailedError
+    public void testMultithreadedIdAllocationForSameKeyFromReleasedIds() throws Throwable {
         CreateIdPoolInput createIdPoolInput = new CreateIdPoolInputBuilder().setHigh(ID_HIGH).setLow(ID_LOW)
                 .setPoolName(ID_POOL_NAME).build();
         AllocateIdInput allocateIdInput = new AllocateIdInputBuilder().setIdKey(TEST_KEY2).setPoolName(ID_POOL_NAME)
@@ -266,9 +284,11 @@ public class IdManagerTest {
         asyncEventsWaiter.awaitEventsConsumption();
 
         String localPoolName = idUtils.getLocalPoolName(ID_POOL_NAME);
-        IdPool parentIdPool = new IdPoolBuilder().setPoolName(ID_POOL_NAME).setKey(new IdPoolKey(ID_POOL_NAME))
-                .setReleasedIdsHolder(createReleaseIdHolder(Arrays.asList(1L, 2L, 3L))).build();
-        IdPool childPool = new IdPoolBuilder().setPoolName(localPoolName).setKey(new IdPoolKey(localPoolName))
+        IdPool parentIdPool = new IdPoolBuilder().setPoolName(ID_POOL_NAME).withKey(new IdPoolKey(ID_POOL_NAME))
+                .setAvailableIdsHolder(createAvailableIdHolder(ID_LOW, ID_HIGH, ID_HIGH + 1))
+                .setReleasedIdsHolder(createReleaseIdHolder(Collections.emptyList())).build();
+        IdPool childPool = new IdPoolBuilder().setPoolName(localPoolName).withKey(new IdPoolKey(localPoolName))
+                .setReleasedIdsHolder(createReleaseIdHolder(Arrays.asList(1L, 2L, 3L)))
                 .setAvailableIdsHolder(createAvailableIdHolder(0L, 9L, 10L)).build();
         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
         tx.merge(LogicalDatastoreType.CONFIGURATION, getIdPoolIdentifier(ID_POOL_NAME), parentIdPool);
@@ -281,11 +301,13 @@ public class IdManagerTest {
                 ExpectedAllocateIdFromReleasedId.idPoolChild());
     }
 
-    private void requestIdsConcurrently(boolean isSameKey) throws InterruptedException {
+    @SuppressWarnings("checkstyle:IllegalThrows") // OK as exceptionInExecutor can't be Exception & AssertionFailedError
+    private void requestIdsConcurrently(boolean isSameKey) throws Throwable {
         int numberOfTasks = 3;
         CountDownLatch latch = new CountDownLatch(numberOfTasks);
-        Set<Long> idSet = new HashSet<>();
-        ExecutorService executor = Executors.newCachedThreadPool();
+        Set<Long> idSet = Sets.newConcurrentHashSet();
+        ExecutorService executor = Executors.newCachedThreadPool("requestIdsConcurrently()", LOG);
+        AtomicReference<Throwable> exceptionInExecutorAtomic = new AtomicReference<>();
         for (int i = 0; i < numberOfTasks; i++) {
             final String idKey;
             if (isSameKey) {
@@ -294,6 +316,8 @@ public class IdManagerTest {
                 idKey = TEST_KEY1 + i;
             }
             executor.execute(() -> {
+                // Any exception thrown inside this background thread will not cause the test to fail
+                // so you cannot use assert* here but must set the exceptionInExecutor which is checked after
                 Future<RpcResult<AllocateIdOutput>> result;
                 result = idManagerService.allocateId(
                         new AllocateIdInputBuilder().setPoolName(ID_POOL_NAME).setIdKey(idKey).build());
@@ -301,31 +325,40 @@ public class IdManagerTest {
                     if (result.get().isSuccessful()) {
                         Long idValue = result.get().getResult().getIdValue();
                         idSet.add(idValue);
-                        assertTrue(idValue <= ID_LOW + BLOCK_SIZE);
+                        if (idValue > ID_LOW + BLOCK_SIZE) {
+                            exceptionInExecutorAtomic.set(new AssertionFailedError("idValue <= ID_LOW + BLOCK_SIZE"));
+                        }
                     } else {
                         RpcError error = result.get().getErrors().iterator().next();
-                        assertTrue(error.getCause().getMessage().contains("Ids exhausted for pool : " + ID_POOL_NAME));
+                        if (!error.getCause().getMessage().contains("Ids exhausted for pool : " + ID_POOL_NAME)) {
+                            exceptionInExecutorAtomic.set(error.getCause());
+                        }
                     }
-                } catch (ExecutionException | InterruptedException e) {
-                    assertTrue(e.getCause().getMessage(), false);
+                } catch (InterruptedException | ExecutionException e) {
+                    exceptionInExecutorAtomic.set(e);
                 } finally {
                     latch.countDown();
                 }
             });
         }
-        latch.await();
+        if (!latch.await(13, SECONDS)) {
+            fail("latch.await(13, SECONDS) timed out :(");
+        }
+        Throwable exceptionInExecutor = exceptionInExecutorAtomic.get();
+        if (exceptionInExecutor != null) {
+            throw exceptionInExecutor;
+        }
         if (isSameKey) {
-            assertTrue(idSet.size() == 1);
+            assertEquals(1, idSet.size());
         } else {
-            assertTrue(idSet.size() == numberOfTasks);
+            assertEquals(numberOfTasks, idSet.size());
         }
     }
 
     private InstanceIdentifier<IdPool> getIdPoolIdentifier(String poolName) {
         InstanceIdentifier.InstanceIdentifierBuilder<IdPool> idBuilder =
                 InstanceIdentifier.builder(IdPools.class).child(IdPool.class, new IdPoolKey(poolName));
-        InstanceIdentifier<IdPool> id = idBuilder.build();
-        return id;
+        return idBuilder.build();
     }
 
     private ReleasedIdsHolder createReleaseIdHolder(List<Long> delayedIds) {
@@ -334,7 +367,7 @@ public class IdManagerTest {
             delayedIdEntries.add(new DelayedIdEntriesBuilder().setId(id).setReadyTimeSec(0L).build());
         }
         return new ReleasedIdsHolderBuilder().setDelayedIdEntries(delayedIdEntries)
-                .setAvailableIdCount(Long.valueOf(delayedIds.size())).build();
+                .setAvailableIdCount((long) delayedIds.size()).build();
     }
 
     private AvailableIdsHolder createAvailableIdHolder(long start, long end, long cursor) {
@@ -374,7 +407,7 @@ public class IdManagerTest {
     private IdPool getUpdatedActualParentPool() throws ReadFailedException {
         IdPool idPoolParentFromDS = singleTxdataBroker.syncRead(LogicalDatastoreType.CONFIGURATION,
                 InstanceIdentifier.builder(IdPools.class).child(IdPool.class, new IdPoolKey(ID_POOL_NAME)).build());
-        List<ChildPools> childPool = idPoolParentFromDS.getChildPools();
+        List<ChildPools> childPool = nullToEmpty(idPoolParentFromDS.getChildPools());
         List<ChildPools> updatedChildPool = childPool.stream()
                 .map(child -> new ChildPoolsBuilder(child).setLastAccessTime(0L).build()).collect(Collectors.toList());
         List<IdEntries> idEntries = idPoolParentFromDS.getIdEntries();
@@ -384,7 +417,6 @@ public class IdManagerTest {
                     .collect(Collectors.toList());
             idPoolBuilder.setIdEntries(sortedIdEntries);
         }
-        IdPool actualIdPoolParent = idPoolBuilder.setChildPools(updatedChildPool).build();
-        return actualIdPoolParent;
+        return idPoolBuilder.setChildPools(updatedChildPool).build();
     }
 }