Merge "Updated TestIMdsalApiManager.java to support installFlow() with CheckedFuture...
[genius.git] / mdsalutil / mdsalutil-api / src / test / java / org / opendaylight / genius / mdsalutil / interfaces / testutils / TestIMdsalApiManager.java
index fe91b98c9d4701eead45f415dd0addca3d79923a..50bf75f02633c7e0a6b6835f1124c7188d714018 100644 (file)
@@ -7,18 +7,25 @@
  */
 package org.opendaylight.genius.mdsalutil.interfaces.testutils;
 
+import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.opendaylight.mdsal.binding.testutils.AssertDataObjects.assertEqualBeans;
 import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.realOrException;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.math.BigInteger;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
-import org.opendaylight.mdsal.binding.testutils.AssertDataObjects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Fake IMdsalApiManager useful for tests.
@@ -35,36 +42,115 @@ import org.opendaylight.mdsal.binding.testutils.AssertDataObjects;
  */
 public abstract class TestIMdsalApiManager implements IMdsalApiManager {
 
+    private static final Logger LOG = LoggerFactory.getLogger(TestIMdsalApiManager.class);
+
     private List<FlowEntity> flows;
 
     public static TestIMdsalApiManager newInstance() {
         return Mockito.mock(TestIMdsalApiManager.class, realOrException());
     }
 
-    private synchronized List<FlowEntity> initializeFlows() {
-        return Collections.synchronizedList(new ArrayList<>());
+    /**
+     * Get list of installed flows.
+     * Prefer the {@link #assertFlows(Iterable)} instead of using this and checking yourself.
+     * @return immutable copy of list of flows
+     */
+    public synchronized List<FlowEntity> getFlows() {
+        return ImmutableList.copyOf(getOrNewFlows());
     }
 
-    public List<FlowEntity> getFlows() {
+    private synchronized List<FlowEntity> getOrNewFlows() {
         if (flows == null) {
-            flows = initializeFlows();
+            flows = new ArrayList<>();
         }
         return flows;
     }
 
-    public void assertFlows(Iterable<FlowEntity> expectedFlows) {
-        List<FlowEntity> flows = this.getFlows();
+    public synchronized void assertFlows(Iterable<FlowEntity> expectedFlows) {
+        checkNonEmptyFlows(expectedFlows);
+        List<FlowEntity> nonNullFlows = getOrNewFlows();
         if (!Iterables.isEmpty(expectedFlows)) {
-            assertTrue("No Flows created (bean wiring may be broken?)", !flows.isEmpty());
+            assertTrue("No Flows created (bean wiring may be broken?)", !nonNullFlows.isEmpty());
         }
         // TODO Support Iterable <-> List directly within XtendBeanGenerator
         List<FlowEntity> expectedFlowsAsNewArrayList = Lists.newArrayList(expectedFlows);
-        AssertDataObjects.assertEqualBeans(expectedFlowsAsNewArrayList, flows);
+        assertEqualBeans(expectedFlowsAsNewArrayList, nonNullFlows);
+    }
+
+
+    private synchronized void checkNonEmptyFlows(Iterable<FlowEntity> expectedFlows) {
+        if (!Iterables.isEmpty(expectedFlows)) {
+            assertTrue("No Flows created (bean wiring may be broken?)", !getOrNewFlows().isEmpty());
+        }
+    }
+
+    public synchronized void assertFlowsInAnyOrder(Iterable<FlowEntity> expectedFlows) {
+        checkNonEmptyFlows(expectedFlows);
+        // TODO Support Iterable <-> List directly within XtendBeanGenerator
+        List<FlowEntity> expectedFlowsAsNewArrayList = Lists.newArrayList(expectedFlows);
+
+        // FYI: This containsExactlyElementsIn() assumes that FlowEntity, and everything in it,
+        // has correctly working equals() implementations.  assertEqualBeans() does not assume
+        // that, and would work even without equals, because it only uses property reflection.
+        // Normally this will lead to the same result, but if one day it doesn't (because of
+        // a bug in an equals() implementation somewhere), then it's worth to keep this diff
+        // in mind.
+
+        // FTR: This use of G Truth and then catch AssertionError and using assertEqualBeans iff NOK
+        // (thus discarding the message from G Truth) is a bit of a hack, but it works well...
+        // If you're tempted to improve this, please remember that correctly re-implementing
+        // containsExactlyElementsIn (or Hamcrest's similar containsInAnyOrder) isn't a 1 line
+        // trivia... e.g. a.containsAll(b) && b.containsAll(a) isn't sufficient, because it
+        // won't work for duplicates (which we frequently have here); and ordering before is
+        // not viable because FlowEntity is not Comparable, and Comparator based on hashCode
+        // is not a good idea (different instances can have same hashCode), and e.g. on
+        // System#identityHashCode even less so.
+        try {
+            assertThat(flows).containsExactlyElementsIn(expectedFlowsAsNewArrayList);
+        } catch (AssertionError e) {
+            // We LOG the AssertionError just for clarity why containsExactlyElementsIn() failed
+            LOG.warn("assert containsExactlyElementsIn() failed", e);
+            // We LOG the expected and actual flow in case of a failed assertion
+            // because, even though that is typically just a HUGE String that's
+            // hard to read (the diff printed subsequently by assertEqualBeans
+            // is, much, more readable), there are cases when looking more closely
+            // at the full toString() output of the flows is still useful, so:
+            LOG.warn("assert failed [order ignored!]; expected flows: {}", expectedFlowsAsNewArrayList);
+            LOG.warn("assert failed [order ignored!]; actual flows  : {}", flows);
+            // The point of this is basically just that our assertEqualBeans output,
+            // in case of a comparison failure, is *A LOT* more clearly readable
+            // than what G Truth (or Hamcrest) can do based on toString.
+            assertEqualBeans(expectedFlowsAsNewArrayList, flows);
+        }
+    }
+
+    @Override
+    public synchronized void installFlow(FlowEntity flowEntity) {
+        getOrNewFlows().add(flowEntity);
+    }
+
+    @Override
+    public synchronized CheckedFuture<Void, TransactionCommitFailedException> installFlow(BigInteger dpId,
+            FlowEntity flowEntity) {
+        installFlow(flowEntity);
+        return Futures.immediateCheckedFuture(null);
+    }
+
+    @Override
+    public synchronized CheckedFuture<Void, TransactionCommitFailedException> removeFlow(BigInteger dpnId,
+            FlowEntity flowEntity) {
+        getOrNewFlows().remove(flowEntity);
+        return Futures.immediateCheckedFuture(null);
+    }
+
+    @Override
+    public synchronized void batchedAddFlow(BigInteger dpId, FlowEntity flowEntity) {
+        getOrNewFlows().add(flowEntity);
     }
 
     @Override
-    public void installFlow(FlowEntity flowEntity) {
-        getFlows().add(flowEntity);
+    public synchronized void batchedRemoveFlow(BigInteger dpId, FlowEntity flowEntity) {
+        getOrNewFlows().remove(flowEntity);
     }
 
 }