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 3e58e65b4cc51e30a2877251d5e3f4ecca0af7fc..50bf75f02633c7e0a6b6835f1124c7188d714018 100644 (file)
@@ -7,7 +7,9 @@
  */
 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;
@@ -22,7 +24,8 @@ 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.
@@ -39,6 +42,8 @@ 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() {
@@ -62,13 +67,61 @@ public abstract class TestIMdsalApiManager implements IMdsalApiManager {
     }
 
     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?)", !nonNullFlows.isEmpty());
         }
         // TODO Support Iterable <-> List directly within XtendBeanGenerator
         List<FlowEntity> expectedFlowsAsNewArrayList = Lists.newArrayList(expectedFlows);
-        AssertDataObjects.assertEqualBeans(expectedFlowsAsNewArrayList, nonNullFlows);
+        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
@@ -76,6 +129,13 @@ public abstract class TestIMdsalApiManager implements IMdsalApiManager {
         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) {
@@ -83,4 +143,14 @@ public abstract class TestIMdsalApiManager implements IMdsalApiManager {
         return Futures.immediateCheckedFuture(null);
     }
 
+    @Override
+    public synchronized void batchedAddFlow(BigInteger dpId, FlowEntity flowEntity) {
+        getOrNewFlows().add(flowEntity);
+    }
+
+    @Override
+    public synchronized void batchedRemoveFlow(BigInteger dpId, FlowEntity flowEntity) {
+        getOrNewFlows().remove(flowEntity);
+    }
+
 }