[ONOS-4387] Support for multiple selectors in mp2sp intents

Changes:
- Adds extension to mp2sp intents;
- Adds extension to linkcollection intents;
- Adds extension to mp2sp compiler;
- Adds extension to linkcollection compiler;
- Adds unit tests for both mp2sp and linkcollection intents;

Change-Id: I673c2b660d2364c510b1b3050ed3626ad2f37bda
diff --git a/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java b/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java
index f72354d..5a5345b 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/LinkCollectionIntent.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.net.intent;
 
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Link;
@@ -39,6 +43,11 @@
 
     private final Set<ConnectPoint> ingressPoints;
     private final Set<ConnectPoint> egressPoints;
+    /**
+     * To manage multiple selectors use case.
+     */
+    private final Map<ConnectPoint, TrafficSelector> ingressSelectors;
+
     private final boolean egressTreatmentFlag;
 
     /**
@@ -56,6 +65,7 @@
      * @param constraints optional list of constraints
      * @param priority    priority to use for the flows generated by this intent
      * @param egressTreatment true if treatment should be applied by the egress device
+     * @param ingressSelectors map to store the association ingress to selector
      * @throws NullPointerException {@code path} is null
      */
     private LinkCollectionIntent(ApplicationId appId,
@@ -66,12 +76,15 @@
                                  Set<ConnectPoint> ingressPoints,
                                  Set<ConnectPoint> egressPoints,
                                  List<Constraint> constraints,
-                                 int priority, boolean egressTreatment) {
+                                 int priority,
+                                 boolean egressTreatment,
+                                 Map<ConnectPoint, TrafficSelector> ingressSelectors) {
         super(appId, key, resources(links), selector, treatment, constraints, priority);
         this.links = links;
         this.ingressPoints = ingressPoints;
         this.egressPoints = egressPoints;
         this.egressTreatmentFlag = egressTreatment;
+        this.ingressSelectors = ingressSelectors;
     }
 
     /**
@@ -83,6 +96,7 @@
         this.ingressPoints = null;
         this.egressPoints = null;
         this.egressTreatmentFlag = false;
+        this.ingressSelectors = null;
     }
 
     /**
@@ -104,6 +118,7 @@
         Set<Link> links;
         Set<ConnectPoint> ingressPoints;
         Set<ConnectPoint> egressPoints;
+        Map<ConnectPoint, TrafficSelector> ingressSelectors = Collections.emptyMap();
         boolean egressTreatmentFlag;
 
         private Builder() {
@@ -165,6 +180,17 @@
         }
 
         /**
+         * Sets the map ingress selectors to connection points of the intent.
+         *
+         * @param ingressSelectors maps connection point to traffic selector
+         * @return this builder
+         */
+        public Builder ingressSelectors(Map<ConnectPoint, TrafficSelector> ingressSelectors) {
+            this.ingressSelectors = ImmutableMap.copyOf(ingressSelectors);
+            return this;
+        }
+
+        /**
          * Sets the links of the link collection intent
          * that will be built.
          *
@@ -206,7 +232,8 @@
                     egressPoints,
                     constraints,
                     priority,
-                    egressTreatmentFlag
+                    egressTreatmentFlag,
+                    ingressSelectors
             );
         }
     }
@@ -241,6 +268,14 @@
     }
 
     /**
+     * Returns the multiple selectors jointly with their connection points.
+     * @return multiple selectors
+     */
+    public Map<ConnectPoint, TrafficSelector> ingressSelectors() {
+        return ingressSelectors;
+    }
+
+    /**
      * Returns whether treatment should be applied on egress.
      *
      * @return the egress treatment flag
@@ -262,7 +297,8 @@
                 .add("links", links())
                 .add("ingress", ingressPoints())
                 .add("egress", egressPoints())
-                .add("treatementOnEgress", applyTreatmentOnEgress())
+                .add("selectors", ingressSelectors())
+                .add("treatmentOnEgress", applyTreatmentOnEgress())
                 .toString();
     }
-}
\ No newline at end of file
+}
diff --git a/core/api/src/main/java/org/onosproject/net/intent/MultiPointToSinglePointIntent.java b/core/api/src/main/java/org/onosproject/net/intent/MultiPointToSinglePointIntent.java
index 0c01543..d1fe612 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/MultiPointToSinglePointIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/MultiPointToSinglePointIntent.java
@@ -17,6 +17,7 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import org.onosproject.core.ApplicationId;
@@ -26,6 +27,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -39,6 +41,10 @@
 
     private final Set<ConnectPoint> ingressPoints;
     private final ConnectPoint egressPoint;
+    /**
+     * To manage multiple selectors use case.
+     */
+    private final Map<ConnectPoint, TrafficSelector> ingressSelectors;
 
     /**
      * Creates a new multi-to-single point connectivity intent for the specified
@@ -52,19 +58,22 @@
      * @param egressPoint   port to which traffic will egress
      * @param constraints   constraints to apply to the intent
      * @param priority      priority to use for flows generated by this intent
+     * @param ingressSelectors map to store the association ingress to selector
      * @throws NullPointerException     if {@code ingressPoints} or
      *                                  {@code egressPoint} is null.
      * @throws IllegalArgumentException if the size of {@code ingressPoints} is
      *                                  not more than 1
      */
     private MultiPointToSinglePointIntent(ApplicationId appId,
-                                         Key key,
-                                         TrafficSelector selector,
-                                         TrafficTreatment treatment,
-                                         Set<ConnectPoint> ingressPoints,
-                                         ConnectPoint egressPoint,
-                                         List<Constraint> constraints,
-                                         int priority) {
+                                          Key key,
+                                          TrafficSelector selector,
+                                          TrafficTreatment treatment,
+                                          Set<ConnectPoint> ingressPoints,
+                                          ConnectPoint egressPoint,
+                                          List<Constraint> constraints,
+                                          int priority,
+                                          Map<ConnectPoint, TrafficSelector> ingressSelectors
+                                          ) {
         super(appId, key, Collections.emptyList(), selector, treatment, constraints,
                 priority);
 
@@ -76,6 +85,7 @@
 
         this.ingressPoints = Sets.newHashSet(ingressPoints);
         this.egressPoint = egressPoint;
+        this.ingressSelectors = ingressSelectors;
     }
 
     /**
@@ -85,6 +95,7 @@
         super();
         this.ingressPoints = null;
         this.egressPoint = null;
+        this.ingressSelectors = null;
     }
 
     /**
@@ -116,6 +127,7 @@
     public static final class Builder extends ConnectivityIntent.Builder {
         Set<ConnectPoint> ingressPoints;
         ConnectPoint egressPoint;
+        Map<ConnectPoint, TrafficSelector> ingressSelectors = Collections.emptyMap();
 
         private Builder() {
             // Hide constructor
@@ -189,13 +201,30 @@
         }
 
         /**
+         * Sets the selectors of the multi point to single point intent
+         * that will be built.
+         *
+         * @param ingressSelectors the multiple selectos
+         * @return this builder
+         */
+        public Builder selectors(Map<ConnectPoint, TrafficSelector> ingressSelectors) {
+            this.ingressSelectors = ImmutableMap.copyOf(ingressSelectors);
+            return this;
+        }
+
+        /**
          * Builds a multi point to single point intent from the
          * accumulated parameters.
          *
-         * @return point to point intent
+         * @return multi point to single point intent
          */
         public MultiPointToSinglePointIntent build() {
 
+            if (selector != null && !selector.criteria().isEmpty() &&
+                    ingressSelectors != null && !ingressSelectors.isEmpty()) {
+                throw new IllegalArgumentException("Selector and Multiple Selectors are both set");
+            }
+
             return new MultiPointToSinglePointIntent(
                     appId,
                     key,
@@ -204,7 +233,8 @@
                     ingressPoints,
                     egressPoint,
                     constraints,
-                    priority
+                    priority,
+                    ingressSelectors
             );
         }
     }
@@ -229,6 +259,14 @@
         return egressPoint;
     }
 
+    /**
+     * Returns the multiple selectors jointly with their connection points.
+     * @return multiple selectors
+     */
+    public Map<ConnectPoint, TrafficSelector> ingressSelectors() {
+        return ingressSelectors;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
@@ -241,6 +279,7 @@
                 .add("treatment", treatment())
                 .add("ingress", ingressPoints())
                 .add("egress", egressPoint())
+                .add("selectors", ingressSelectors())
                 .add("constraints", constraints())
                 .toString();
     }
diff --git a/core/api/src/test/java/org/onosproject/net/intent/ConnectivityIntentTest.java b/core/api/src/test/java/org/onosproject/net/intent/ConnectivityIntentTest.java
index 0f5b3d0..6e9d68c 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/ConnectivityIntentTest.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/ConnectivityIntentTest.java
@@ -15,8 +15,12 @@
  */
 package org.onosproject.net.intent;
 
+import java.util.Collections;
+import java.util.Map;
 import java.util.Set;
 
+import com.google.common.collect.Maps;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.TestApplicationId;
 import org.onosproject.net.ConnectPoint;
@@ -38,6 +42,7 @@
     public static final IntentId IID = new IntentId(123);
     public static final TrafficSelector MATCH = DefaultTrafficSelector.emptySelector();
     public static final TrafficTreatment NOP = DefaultTrafficTreatment.emptyTreatment();
+    public static final Map<ConnectPoint, TrafficSelector> MATCHES = Collections.emptyMap();
 
     public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
     public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
@@ -45,4 +50,18 @@
 
     public static final Set<ConnectPoint> PS1 = itemSet(new ConnectPoint[]{P1, P3});
     public static final Set<ConnectPoint> PS2 = itemSet(new ConnectPoint[]{P2, P3});
+
+    public static final TrafficSelector VLANMATCH1 = DefaultTrafficSelector.builder()
+            .matchVlanId(VlanId.vlanId("2"))
+            .build();
+    public static final TrafficSelector VLANMATCH2 = DefaultTrafficSelector.builder()
+            .matchVlanId(VlanId.vlanId("3"))
+            .build();
+
+    public static final Map<ConnectPoint, TrafficSelector> VLANMATCHES = Maps.newHashMap();
+    static {
+        VLANMATCHES.put(P1, VLANMATCH1);
+        VLANMATCHES.put(P2, VLANMATCH2);
+    }
+
 }
diff --git a/core/api/src/test/java/org/onosproject/net/intent/MultiPointToSinglePointIntentTest.java b/core/api/src/test/java/org/onosproject/net/intent/MultiPointToSinglePointIntentTest.java
index 9061418..115af4a 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/MultiPointToSinglePointIntentTest.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/MultiPointToSinglePointIntentTest.java
@@ -13,9 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.net.intent;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import static org.junit.Assert.assertEquals;
 import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
@@ -42,6 +45,39 @@
         assertEquals("incorrect egress", P2, intent.egressPoint());
     }
 
+    @Rule
+    public ExpectedException wrongMultiple = ExpectedException.none();
+
+    @Test
+    public void multipleSelectors() {
+
+        MultiPointToSinglePointIntent intent = createFirstMultiple();
+        assertEquals("incorrect id", APPID, intent.appId());
+        assertEquals("incorrect match", MATCH, intent.selector());
+        assertEquals("incorrect ingress", PS1, intent.ingressPoints());
+        assertEquals("incorrect egress", P2, intent.egressPoint());
+        assertEquals("incorrect selectors", MATCHES, intent.ingressSelectors());
+
+        intent = createSecondMultiple();
+        assertEquals("incorrect id", APPID, intent.appId());
+        assertEquals("incorrect match", VLANMATCH1, intent.selector());
+        assertEquals("incorrect ingress", PS1, intent.ingressPoints());
+        assertEquals("incorrect egress", P2, intent.egressPoint());
+        assertEquals("incorrect selectors", MATCHES, intent.ingressSelectors());
+
+        intent = createThirdMultiple();
+        assertEquals("incorrect id", APPID, intent.appId());
+        assertEquals("incorrect match", MATCH, intent.selector());
+        assertEquals("incorrect ingress", PS1, intent.ingressPoints());
+        assertEquals("incorrect egress", P2, intent.egressPoint());
+        assertEquals("incorrect selectors", VLANMATCHES, intent.ingressSelectors());
+
+        wrongMultiple.expect(IllegalArgumentException.class);
+        wrongMultiple.expectMessage("Selector and Multiple Selectors are both set");
+        intent = createWrongMultiple();
+    }
+
+
     @Override
     protected MultiPointToSinglePointIntent createOne() {
         return MultiPointToSinglePointIntent.builder()
@@ -63,4 +99,49 @@
                 .egressPoint(P1)
                 .build();
     }
+
+    protected MultiPointToSinglePointIntent createFirstMultiple() {
+        return MultiPointToSinglePointIntent.builder()
+                .appId(APPID)
+                .selector(MATCH)
+                .treatment(NOP)
+                .ingressPoints(PS1)
+                .egressPoint(P2)
+                .selectors(MATCHES)
+                .build();
+    }
+
+    protected MultiPointToSinglePointIntent createSecondMultiple() {
+        return MultiPointToSinglePointIntent.builder()
+                .appId(APPID)
+                .selector(VLANMATCH1)
+                .treatment(NOP)
+                .ingressPoints(PS1)
+                .egressPoint(P2)
+                .selectors(MATCHES)
+                .build();
+    }
+
+    protected MultiPointToSinglePointIntent createThirdMultiple() {
+        return MultiPointToSinglePointIntent.builder()
+                .appId(APPID)
+                .selector(MATCH)
+                .treatment(NOP)
+                .ingressPoints(PS1)
+                .egressPoint(P2)
+                .selectors(VLANMATCHES)
+                .build();
+    }
+
+    protected MultiPointToSinglePointIntent createWrongMultiple() {
+        return MultiPointToSinglePointIntent.builder()
+                .appId(APPID)
+                .selector(VLANMATCH1)
+                .treatment(NOP)
+                .ingressPoints(PS1)
+                .egressPoint(P2)
+                .selectors(VLANMATCHES)
+                .build();
+    }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
index 9fbedb9..b37095a 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
@@ -48,6 +48,7 @@
 import org.onosproject.net.intent.LinkCollectionIntent;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -155,12 +156,15 @@
      *
      * @param intent the intent to compile
      * @param inPort the input port
+     * @param deviceId the current device
      * @param outPorts the output ports
      * @param ingressPorts the ingress ports
      * @param egressPorts the egress ports
      * @return the forwarding instruction object which encapsulates treatment and selector
      */
-    protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent, PortNumber inPort,
+    protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent,
+                                                                  PortNumber inPort,
+                                                                  DeviceId deviceId,
                                                                   Set<PortNumber> outPorts,
                                                                   Set<PortNumber> ingressPorts,
                                                                   Set<PortNumber> egressPorts) {
@@ -178,8 +182,27 @@
             intentTreatment = ingressTreatmentBuilder.build();
 
             if (ingressPorts.contains(inPort)) {
-                selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
-                treatment = intentTreatment;
+                if (intent.ingressSelectors() != null && !intent.ingressSelectors().isEmpty()) {
+                    /**
+                     * We iterate on the ingress points looking for the connect point
+                     * associated to inPort.
+                     */
+                    Optional<ConnectPoint> connectPoint = intent.ingressPoints()
+                            .stream()
+                            .filter(ingressPoint -> ingressPoint.port().equals(inPort)
+                                    && ingressPoint.deviceId().equals(deviceId))
+                            .findFirst();
+                    if (connectPoint.isPresent()) {
+                        selectorBuilder = DefaultTrafficSelector
+                                .builder(intent.ingressSelectors().get(connectPoint.get()));
+                    } else {
+                        throw new IntentCompilationException("Looking for connect point associated to the selector." +
+                                                                     "inPort not in IngressPoints");
+                    }
+                } else {
+                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                }
+                treatment = this.updateBuilder(ingressTreatmentBuilder, selectorBuilder.build()).build();
             } else {
                 selectorBuilder = this.createSelectorFromFwdInstructions(
                         new ForwardingInstructions(intentTreatment, intent.selector())
@@ -207,6 +230,18 @@
     }
 
     /**
+     * Update the original builder with the necessary operations
+     * to have a correct forwarding given an ingress selector.
+     *
+     * @param treatmentBuilder the builder to modify
+     * @return the new treatment created
+     */
+    private TrafficTreatment.Builder updateBuilder(TrafficTreatment.Builder treatmentBuilder,
+                                                   TrafficSelector intentSelector) {
+        return treatmentBuilder;
+    }
+
+    /**
      * Update the selector builder using a L0 instruction.
      *
      * @param builder the builder to update
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
index 7c2157c..83547e2 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
@@ -98,6 +98,7 @@
         inPorts.forEach(inport -> {
                 ForwardingInstructions instructions = this.createForwardingInstructions(intent,
                                                                                         inport,
+                                                                                        deviceId,
                                                                                         outPorts,
                                                                                         copyIngressPorts,
                                                                                         copyEgressPorts);
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
index 7ffff6b..0d6ad0a 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
@@ -115,6 +115,7 @@
         inPorts.forEach(inport -> {
             ForwardingInstructions instructions = this.createForwardingInstructions(intent,
                                                                                     inport,
+                                                                                    deviceId,
                                                                                     outPorts,
                                                                                     copyIngressPorts,
                                                                                     copyEgressPorts);
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
index 4aee117..00ed9b2 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
@@ -124,6 +124,7 @@
                 .links(Sets.newHashSet(links.values()))
                 .ingressPoints(intent.ingressPoints())
                 .egressPoints(ImmutableSet.of(intent.egressPoint()))
+                .ingressSelectors(intent.ingressSelectors())
                 .priority(intent.priority())
                 .constraints(intent.constraints())
                 .build();
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
index acac7df..8e89d14 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompilerTest.java
@@ -16,9 +16,11 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.ApplicationId;
@@ -32,6 +34,8 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentExtensionService;
@@ -41,7 +45,9 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -62,6 +68,7 @@
     private final ConnectPoint d2p0 = connectPoint("s2", 0);
     private final ConnectPoint d2p1 = connectPoint("s2", 1);
     private final ConnectPoint d3p1 = connectPoint("s3", 1);
+    private final ConnectPoint d3p2 = connectPoint("s3", 9);
     private final ConnectPoint d3p0 = connectPoint("s3", 10);
     private final ConnectPoint d1p0 = connectPoint("s1", 10);
 
@@ -70,15 +77,38 @@
             DefaultLink.builder().providerId(PID).src(d2p1).dst(d3p1).type(DIRECT).build(),
             DefaultLink.builder().providerId(PID).src(d1p1).dst(d3p1).type(DIRECT).build());
 
+    private final Set<Link> linksMultiple = ImmutableSet.of(
+            DefaultLink.builder().providerId(PID).src(d3p1).dst(d2p0).type(DIRECT).build());
+
     private final TrafficSelector selector = DefaultTrafficSelector.builder().build();
     private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
 
+    private final VlanId ingressVlan1 = VlanId.vlanId("10");
+    private final TrafficSelector selectorVlan1 = DefaultTrafficSelector
+            .builder()
+            .matchVlanId(ingressVlan1)
+            .build();
+
+    private final VlanId ingressVlan2 = VlanId.vlanId("20");
+    private final TrafficSelector selectorVlan2 = DefaultTrafficSelector
+            .builder()
+            .matchVlanId(ingressVlan2)
+            .build();
+
+    private final VlanId egressVlan = VlanId.vlanId("666");
+    private final TrafficTreatment vlanTreatment = DefaultTrafficTreatment
+            .builder()
+            .setVlanId(egressVlan)
+            .build();
+
     private CoreService coreService;
     private IntentExtensionService intentExtensionService;
     private IntentConfigurableRegistrator registrator;
     private IdGenerator idGenerator = new MockIdGenerator();
 
     private LinkCollectionIntent intent;
+    private LinkCollectionIntent intentMultiple;
+
 
     private LinkCollectionIntentCompiler sut;
 
@@ -100,6 +130,15 @@
                 .ingressPoints(ImmutableSet.of(d1p1))
                 .egressPoints(ImmutableSet.of(d3p1))
                 .build();
+        intentMultiple = LinkCollectionIntent.builder()
+                .appId(APP_ID)
+                .treatment(vlanTreatment)
+                .links(linksMultiple)
+                .ingressPoints(ImmutableSet.of(d3p0, d3p2))
+                .egressPoints(ImmutableSet.of(d2p1))
+                .ingressSelectors(this.createIngressSelectors())
+                .build();
+
         intentExtensionService = createMock(IntentExtensionService.class);
         intentExtensionService.registerCompiler(LinkCollectionIntent.class, sut);
         intentExtensionService.unregisterCompiler(LinkCollectionIntent.class);
@@ -168,4 +207,99 @@
 
         sut.deactivate();
     }
+
+    @Test
+    public void testCompileMultipleSelectors() {
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(intentMultiple, Collections.emptyList());
+        assertThat(compiled, hasSize(1));
+
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize((linksMultiple.size()) + intentMultiple.ingressPoints().size()));
+
+        Set<FlowRule> d3Rules = rules
+                .parallelStream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(d3Rules, hasSize(intentMultiple.ingressPoints().size()));
+
+        FlowRule rule1 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId())
+                        &&
+                        rule.selector().getCriterion(Criterion.Type.IN_PORT).equals(Criteria.matchInPort(d3p0.port())))
+                .findFirst()
+                .get();
+        assertThat(rule1.selector(), is(
+                DefaultTrafficSelector
+                        .builder(intentMultiple.selector())
+                        .matchInPort(d3p0.port())
+                        .matchVlanId(ingressVlan1)
+                        .build()
+        ));
+        assertThat(rule1.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder(intentMultiple.treatment())
+                        .setOutput(d3p1.port())
+                        .build()
+        ));
+        assertThat(rule1.priority(), is(intentMultiple.priority()));
+
+        FlowRule rule2 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d3p0.deviceId())
+                        &&
+                        rule.selector().getCriterion(Criterion.Type.IN_PORT).equals(Criteria.matchInPort(d3p2.port())))
+                .findFirst()
+                .get();
+        assertThat(rule2.selector(), is(
+                DefaultTrafficSelector
+                        .builder(intentMultiple.selector())
+                        .matchInPort(d3p2.port())
+                        .matchVlanId(ingressVlan2)
+                        .build()
+        ));
+        assertThat(rule2.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder(intentMultiple.treatment())
+                        .setOutput(d3p1.port())
+                        .build()
+        ));
+        assertThat(rule1.priority(), is(intentMultiple.priority()));
+
+        Set<FlowRule> d2Rules = rules
+                .parallelStream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .collect(Collectors.toSet());
+        assertThat(d2Rules, hasSize(intentMultiple.egressPoints().size()));
+
+        // We do not need in_port filter
+        FlowRule rule3 = rules.stream()
+                .filter(rule -> rule.deviceId().equals(d2p0.deviceId()))
+                .findFirst()
+                .get();
+        assertThat(rule3.selector(), is(
+                DefaultTrafficSelector
+                        .builder(intentMultiple.selector())
+                        .matchInPort(d2p0.port())
+                        .matchVlanId(egressVlan)
+                        .build()
+        ));
+        assertThat(rule3.treatment(), is(
+                DefaultTrafficTreatment
+                        .builder()
+                        .setOutput(d2p1.port())
+                        .build()
+        ));
+        assertThat(rule3.priority(), is(intentMultiple.priority()));
+
+        sut.deactivate();
+    }
+
+    public Map<ConnectPoint, TrafficSelector> createIngressSelectors() {
+        Map<ConnectPoint, TrafficSelector> mapToReturn = Maps.newHashMap();
+        mapToReturn.put(d3p0, selectorVlan1);
+        mapToReturn.put(d3p2, selectorVlan2);
+        return mapToReturn;
+    }
 }