Support encapsulation in PathIntent

- related to ONOS-3467
- unit test work
- depends on ONOS-3507 and also on the advertisement of VLAN resource
  for different devices.

Change-Id: Ia852c751135b5ca4a16901c6f3a85ceea11514a3
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
index f07bf42..dda82c6 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
@@ -15,14 +15,11 @@
  */
 package org.onosproject.net.intent.impl.compiler;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
+import com.google.common.collect.ImmutableList;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -30,30 +27,38 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultLink;
 import org.onosproject.net.DefaultPath;
+import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.Link;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.MockIdGenerator;
 import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.provider.ProviderId;
 
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.easymock.EasyMock.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.number.OrderingComparison.greaterThan;
 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
 import static org.onosproject.net.Link.Type.DIRECT;
-import static org.onosproject.net.NetTestTools.APP_ID;
-import static org.onosproject.net.NetTestTools.PID;
-import static org.onosproject.net.NetTestTools.connectPoint;
+import static org.onosproject.net.NetTestTools.*;
 
 /**
  * Unit tests for PathIntentCompiler.
@@ -85,6 +90,7 @@
     );
     private final int hops = links.size() - 1;
     private PathIntent intent;
+    private PathIntent constraintIntent;
 
     /**
      * Configures objects used in all the test cases.
@@ -96,6 +102,7 @@
         expect(coreService.registerApplication("org.onosproject.net.intent"))
                 .andReturn(appId);
         sut.coreService = coreService;
+        sut.resourceService = new MockResourceService();
 
         Intent.bindIdGenerator(idGenerator);
 
@@ -106,6 +113,14 @@
                 .priority(PRIORITY)
                 .path(new DefaultPath(pid, links, hops))
                 .build();
+        constraintIntent = PathIntent.builder()
+                .appId(APP_ID)
+                .selector(selector)
+                .treatment(treatment)
+                .priority(PRIORITY)
+                .constraints(ImmutableList.of(new EncapsulationConstraint(EncapsulationType.VLAN)))
+                .path(new DefaultPath(pid, links, hops))
+                .build();
         intentExtensionService = createMock(IntentExtensionService.class);
         intentExtensionService.registerCompiler(PathIntent.class, sut);
         intentExtensionService.unregisterCompiler(PathIntent.class);
@@ -169,4 +184,91 @@
 
         sut.deactivate();
     }
+
+    /**
+     * Tests the compilation behavior of the path intent compiler in case of
+     * encasulation costraint {@link EncapsulationConstraint}.
+     */
+    @Test
+    public void testEncapCompile() {
+        sut.activate();
+
+        List<Intent> compiled = sut.compile(constraintIntent, Collections.emptyList(), Collections.emptySet());
+        assertThat(compiled, hasSize(1));
+
+        Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules();
+        assertThat(rules, hasSize(3));
+
+        FlowRule rule1 = rules.stream()
+                .filter(x -> x.deviceId().equals(d1p0.deviceId()))
+                .findFirst()
+                .get();
+        assertThat(rule1.deviceId(), is(d1p0.deviceId()));
+        assertThat(rule1.priority(), is(intent.priority()));
+        verifyEncapSelector(rule1.selector(), d1p0, VlanId.NONE);
+        VlanId vlanToEncap = verifyEncapTreatment(rule1.treatment(), d1p1, true, false);
+
+        FlowRule rule2 = rules.stream()
+                .filter(x -> x.deviceId().equals(d2p0.deviceId()))
+                .findFirst()
+                .get();
+        assertThat(rule2.deviceId(), is(d2p0.deviceId()));
+        assertThat(rule2.priority(), is(intent.priority()));
+        verifyEncapSelector(rule2.selector(), d2p0, vlanToEncap);
+        verifyEncapTreatment(rule2.treatment(), d2p1, false, false);
+
+        FlowRule rule3 = rules.stream()
+                .filter(x -> x.deviceId().equals(d3p0.deviceId()))
+                .findFirst()
+                .get();
+        assertThat(rule3.deviceId(), is(d3p1.deviceId()));
+        assertThat(rule3.priority(), is(intent.priority()));
+        verifyEncapSelector(rule3.selector(), d3p1, vlanToEncap);
+        verifyEncapTreatment(rule3.treatment(), d3p0, false, true);
+
+        sut.deactivate();
+    }
+
+
+    private VlanId verifyEncapTreatment(TrafficTreatment trafficTreatment,
+                                        ConnectPoint egress, boolean isIngress, boolean isEgress) {
+        Set<Instructions.OutputInstruction> ruleOutput = trafficTreatment.allInstructions().stream()
+                .filter(treat -> treat instanceof Instructions.OutputInstruction)
+                .map(treat -> (Instructions.OutputInstruction) treat)
+                .collect(Collectors.toSet());
+        assertThat(ruleOutput, hasSize(1));
+        assertThat((ruleOutput.iterator().next()).port(), is(egress.port()));
+        VlanId vlanToEncap = VlanId.NONE;
+        if (isIngress && !isEgress) {
+            Set<L2ModificationInstruction.ModVlanIdInstruction> vlanRules = trafficTreatment.allInstructions().stream()
+                    .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction)
+                    .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x)
+                    .collect(Collectors.toSet());
+            assertThat(vlanRules, hasSize(1));
+            L2ModificationInstruction.ModVlanIdInstruction vlanRule = vlanRules.iterator().next();
+            assertThat(vlanRule.vlanId().toShort(), greaterThan((short) 0));
+            vlanToEncap = vlanRule.vlanId();
+        } else if (!isIngress && !isEgress) {
+            assertThat(trafficTreatment.allInstructions().stream()
+                               .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction)
+                               .collect(Collectors.toSet()), hasSize(0));
+        } else {
+            assertThat(trafficTreatment.allInstructions().stream()
+                               .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction)
+                               .collect(Collectors.toSet()), hasSize(0));
+            assertThat(trafficTreatment.allInstructions().stream()
+                               .filter(treat -> treat instanceof L2ModificationInstruction.PopVlanInstruction)
+                               .collect(Collectors.toSet()), hasSize(1));
+
+        }
+
+        return vlanToEncap;
+
+    }
+
+    private void verifyEncapSelector(TrafficSelector trafficSelector, ConnectPoint ingress, VlanId vlanToMatch) {
+
+        is(DefaultTrafficSelector.builder(selector).matchInPort(ingress.port())
+                   .matchVlanId(vlanToMatch).build());
+    }
 }