Updates related to SDN-IP submitting intent operations:

  * Updated IntentSynchronizer.submitPeerIntents() to use intent batch
    operation

  * Refactored the SDN-IP unit tests:
    The verification for intent service operations is moved to a new class
    TestIntentServiceHeler

Change-Id: Id6766ebb4d6bf492e8f9787c2c2247ac38d42964
diff --git a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/TestIntentServiceHelper.java b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/TestIntentServiceHelper.java
new file mode 100644
index 0000000..4cc9053
--- /dev/null
+++ b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/TestIntentServiceHelper.java
@@ -0,0 +1,213 @@
+package org.onlab.onos.sdnip;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.easymock.IArgumentMatcher;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentOperation;
+import org.onlab.onos.net.intent.IntentOperations;
+import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.onos.sdnip.IntentSynchronizer.IntentKey;
+
+import static org.easymock.EasyMock.reportMatcher;
+
+/**
+ * Helper class for testing operations submitted to the IntentService.
+ */
+public final class TestIntentServiceHelper {
+    /**
+     * Default constructor to prevent instantiation.
+     */
+    private TestIntentServiceHelper() {
+    }
+
+    /**
+     * Matcher method to set the expected intent operations to match against
+     * (ignoring the intent ID for each intent).
+     *
+     * @param intentOperations the expected Intent Operations
+     * @return the submitted Intent Operations
+     */
+    static IntentOperations eqExceptId(IntentOperations intentOperations) {
+        reportMatcher(new IdAgnosticIntentOperationsMatcher(intentOperations));
+        return intentOperations;
+    }
+
+
+    /**
+     * Matcher method to set an expected point-to-point intent to match
+     * against (ignoring the intent ID).
+     *
+     * @param intent the expected point-to-point intent
+     * @return the submitted point-to-point intent
+     */
+    private static PointToPointIntent eqExceptId(
+            PointToPointIntent intent) {
+        reportMatcher(new IdAgnosticPointToPointIntentMatcher(intent));
+        return intent;
+    }
+
+    /*
+     * EasyMock matcher that matches {@link IntenOperations} but
+     * ignores the {@link IntentId} when matching.
+     * <p/>
+     * The normal intent equals method tests that the intent IDs are equal,
+     * however in these tests we can't know what the intent IDs will be in
+     * advance, so we can't set up expected intents with the correct IDs. Thus,
+     * the solution is to use an EasyMock matcher that verifies that all the
+     * value properties of the provided intent match the expected values, but
+     * ignores the intent ID when testing equality.
+     */
+    private static final class IdAgnosticIntentOperationsMatcher implements
+                IArgumentMatcher {
+
+        private final IntentOperations intentOperations;
+        private String providedString;
+
+        /**
+         * Constructor taking the expected intent operations to match against.
+         *
+         * @param intentOperations the expected intent operations
+         */
+        public IdAgnosticIntentOperationsMatcher(
+                        IntentOperations intentOperations) {
+            this.intentOperations = intentOperations;
+        }
+
+        @Override
+        public void appendTo(StringBuffer strBuffer) {
+            strBuffer.append("IntentOperationsMatcher unable to match: "
+                    + providedString);
+        }
+
+        @Override
+        public boolean matches(Object object) {
+            if (!(object instanceof IntentOperations)) {
+                return false;
+            }
+
+            IntentOperations providedIntentOperations =
+                (IntentOperations) object;
+            providedString = providedIntentOperations.toString();
+
+            List<IntentKey> thisSubmitIntents = new LinkedList<>();
+            List<IntentId> thisWithdrawIntentIds = new LinkedList<>();
+            List<IntentKey> thisReplaceIntents = new LinkedList<>();
+            List<IntentKey> thisUpdateIntents = new LinkedList<>();
+            List<IntentKey> providedSubmitIntents = new LinkedList<>();
+            List<IntentId> providedWithdrawIntentIds = new LinkedList<>();
+            List<IntentKey> providedReplaceIntents = new LinkedList<>();
+            List<IntentKey> providedUpdateIntents = new LinkedList<>();
+
+            extractIntents(intentOperations, thisSubmitIntents,
+                           thisWithdrawIntentIds, thisReplaceIntents,
+                           thisUpdateIntents);
+            extractIntents(providedIntentOperations, providedSubmitIntents,
+                           providedWithdrawIntentIds, providedReplaceIntents,
+                           providedUpdateIntents);
+
+            return CollectionUtils.isEqualCollection(thisSubmitIntents,
+                                                     providedSubmitIntents) &&
+                CollectionUtils.isEqualCollection(thisWithdrawIntentIds,
+                                                  providedWithdrawIntentIds) &&
+                CollectionUtils.isEqualCollection(thisUpdateIntents,
+                                                  providedUpdateIntents) &&
+                CollectionUtils.isEqualCollection(thisReplaceIntents,
+                                                  providedReplaceIntents);
+        }
+
+        /**
+         * Extracts the intents per operation type. Each intent is encapsulated
+         * in IntentKey so it can be compared by excluding the Intent ID.
+         *
+         * @param intentOperations the container with the intent operations
+         * to extract the intents from
+         * @param submitIntents the SUBMIT intents
+         * @param withdrawIntentIds the WITHDRAW intents IDs
+         * @param replaceIntents the REPLACE intents
+         * @param updateIntents the UPDATE intens
+         */
+        private void extractIntents(IntentOperations intentOperations,
+                                    List<IntentKey> submitIntents,
+                                    List<IntentId> withdrawIntentIds,
+                                    List<IntentKey> replaceIntents,
+                                    List<IntentKey> updateIntents) {
+            for (IntentOperation oper : intentOperations.operations()) {
+                IntentId intentId;
+                IntentKey intentKey;
+                switch (oper.type()) {
+                case SUBMIT:
+                    intentKey = new IntentKey(oper.intent());
+                    submitIntents.add(intentKey);
+                    break;
+                case WITHDRAW:
+                    intentId = oper.intentId();
+                    withdrawIntentIds.add(intentId);
+                    break;
+                case REPLACE:
+                    intentKey = new IntentKey(oper.intent());
+                    replaceIntents.add(intentKey);
+                    break;
+                case UPDATE:
+                    intentKey = new IntentKey(oper.intent());
+                    updateIntents.add(intentKey);
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+    }
+
+    /*
+     * EasyMock matcher that matches {@link PointToPointIntent}s but
+     * ignores the {@link IntentId} when matching.
+     * <p/>
+     * The normal intent equals method tests that the intent IDs are equal,
+     * however in these tests we can't know what the intent IDs will be in
+     * advance, so we can't set up expected intents with the correct IDs. Thus,
+     * the solution is to use an EasyMock matcher that verifies that all the
+     * value properties of the provided intent match the expected values, but
+     * ignores the intent ID when testing equality.
+     */
+    private static final class IdAgnosticPointToPointIntentMatcher implements
+                IArgumentMatcher {
+
+        private final PointToPointIntent intent;
+        private String providedIntentString;
+
+        /**
+         * Constructor taking the expected intent to match against.
+         *
+         * @param intent the expected intent
+         */
+        public IdAgnosticPointToPointIntentMatcher(PointToPointIntent intent) {
+            this.intent = intent;
+        }
+
+        @Override
+        public void appendTo(StringBuffer strBuffer) {
+            strBuffer.append("PointToPointIntentMatcher unable to match: "
+                    + providedIntentString);
+        }
+
+        @Override
+        public boolean matches(Object object) {
+            if (!(object instanceof PointToPointIntent)) {
+                return false;
+            }
+
+            PointToPointIntent providedIntent = (PointToPointIntent) object;
+            providedIntentString = providedIntent.toString();
+
+            PointToPointIntent matchIntent =
+                    new PointToPointIntent(providedIntent.appId(),
+                            intent.selector(), intent.treatment(),
+                            intent.ingressPoint(), intent.egressPoint());
+
+            return matchIntent.equals(providedIntent);
+        }
+    }
+}